0

我开始了一些我认为直截了当的事情:顺序(逐行)读取,计算一些值并更新同一行,然后再继续整个表的下一行。

上下文:一个平面表,2600 万条记录,复合 PK(4 个数值)。 物理表大小 1.3 GB。处理记录的顺序无关紧要。这只会在可预见的未来进行一次。计算太复杂,无法在 SQL 中完成(至少对我来说 :-)

推荐的有效方法是什么?

我尝试了什么:使用datareaderin ADO.NET(它不再有旧的 VB6 结果集,这本来会简单得多)。将它与每个reader.Read()循环中的更新语句 (statement.ExecuteNonQuery) 结合起来很棘手,因为 ADO.NET 不喜欢在同一个连接上这样做。所以我不得不打开2个连接。(更新查询在 WHERE 子句中使用复合 PK,这可能会很快,但仍然让我觉得效率低下,因为光标已经在我要更新的记录上。)

这种方法有点工作,但不适用于基于SELECT * FROM MyTable查询的阅读器。我不得不LIMIT一次读取几千行的块以避免超时错误。根据早期的实验,我估计2600 万条记录的处理过程需要9 个小时。我将它设置为通宵运行,当我回来时,它在整个过程中再次超时三分之一。重新启动后,我发现SELECT一旦偏移量变大,LIMIT 子句就会减慢查询速度。我对剩余 65% 的新估计超过了另外 20 小时,随着 LIMIT 偏移量的增加,可能会更长。

一定会有更好的办法!?

(我还尝试了优雅但当然超时的 EF :-)

4

2 回答 2

0

经过与 Eric 的上述讨论和进一步的实验,这是我对传奇的结论:

  • 关系数据库确实不适合顺序处理,并且任何此类过程在关系 DBMS 上执行时都会受到性能影响。
  • 在数据库历史的某个阶段,像 VB6 这样的平台提供了像“Recordset”这样的工具,它支持“基于光标”的表遍历、读取和更新记录。这些用于在支持的提供程序上工作,如 ODBC 和 OLE(以及附加到这些的 DBMS)。记录集看起来非常适合手头的工作,但在 ADO.NET 中不再可用(截至 2013 年)
  • 中小型桌子可以原谅设计错误。
  • 操作系统缓存整个数据表,因此在处理中小型表时掩盖了数据库效率低下
  • 一旦表大小(和/或行数)增加,系统就会开始颠簸并且表现异常。它可能以前表现不佳,但由于上述几点,您不会注意到它。
  • 我使用 SELECT...LIMIT (检索 1000 行的块)的方法在 2600 万行表中突然停止了大约 75%;即现在需要几分钟才能完成 1000 行的每个 SELECT。
  • 我曾尝试基于http://www.codeproject.com/Articles/8435/Simulating-Recordsets-with-ADO-NET模拟基于游标的记录集,结果发现 MySQL 不支持游标的更新,只支持存储过程中的游标,这违背了目的,因为我的计算必须在 DBMS 之外执行。(它可能适用于 SQL Server)
  • (因为我的表由一个 4 部分的复合键组成)我最终按照 Eric 的建议创建了一个人工 AutoIncrement 键/索引,因此我可以使用计算范围(例如 0-999、1000-1999 等)遍历记录与使用 LIMIT 不同,它在表格遍历的开始和结束时同样快。在慢速 2 核 Atom 上网本上创建 AutoIncrement 字段和索引/键(在一个命令中)花费了 MySQL 不到 1 小时(对于 150 字节/记录的 26+ 百万条记录)。
  • 在上述配置中,26+ 百万条记录的完整遍历大约需要 9 个小时,这与我在流程开始时 LIMIT 计时时的原始估计相同。

希望这可以帮助任何处于类似情况的人。非常感谢评论。

于 2013-01-15T11:38:38.930 回答
0

以小批量(1000 条左右的记录)更新数据库通常是一种好方法,因为它可以避免锁定行(或页面)太长时间,并避免超时。方法的那部分很棒。

对于较大的起始值,您可以提高 LIMIT 的性能。有多种方法。迄今为止我发现的最好的方法是根本不使用 LIMIT,而是选择主键范围

https://stackoverflow.com/a/1911210/141172

于 2013-01-11T01:33:23.613 回答