MySQL& dotnet:有效地更新每一行

时间:2013-01-11 01:21:04

标签: .net mysql sequential read-write

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

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

建议的有效方法是什么?

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

这种方法有效,但不适用于基于SELECT * FROM MyTable查询的阅读器。我不得不使用LIMIT一次读取几千行的块来避免超时错误。从早期的实验中我估计了2600万条记录的强度> 9小时。我把它设置为一夜之间运行,当我回来的时候,在整个过程中三分之一的时间再次超时。重新启动后,我发现一旦偏移量变大,LIMIT子句会减慢SELECT查询的速度。我对其余65%的新估计超过了另外20个小时,可能更长,因为LIMIT抵消增加。

必须有更好的方法!?

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

2 个答案:

答案 0 :(得分:0)

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

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

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

答案 1 :(得分:0)

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

  • 关系数据库确实不适合顺序处理,任何此类进程在关系DBMS上执行时都会受到性能影响。
  • 在数据库历史的某个阶段,像VB6这样的平台提供了像“Recordset”这样的工具,它们可以实现“基于游标”遍历表,读取和更新记录。这些曾经在支持的提供程序上工作,如ODBC和OLE(以及附加到这些的DBMS)。记录集对于手头的工作看起来非常诱人,但在ADO.NET中已不再可用(截至2013年)
  • 中小型桌子会原谅设计错误。
  • 操作系统缓存整个数据表,因此在处理中小型表时会掩盖数据库的不足之处
  • 一旦表格大小(和/或行数)增加,系统就会开始抖动并且似乎表现异常。它之前可能表现不佳,但由于上述各点,你不会注意到它。
  • 我使用SELECT ... LIMIT(检索1000行的块)的方法在大约75%时突然停止,尽管2600万行表;即每个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不同,它在表格遍历的开始和结束时同样快。创建AutoIncrement字段加索引/键(在一个命令中)在一个缓慢的2核Atom上网本上花费了不到1小时的MySQL(对于150多字节/记录的2600万条记录)。
  • 在上述配置中,2600多万条记录的完整遍历大约需要9个小时,相当于LIMIT在流程开始时计时的原始估算值。

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