插入数十万行时,MySQL 与 MS Access 相比非常慢
Posted
技术标签:
【中文标题】插入数十万行时,MySQL 与 MS Access 相比非常慢【英文标题】:MySQL very slow compared to MS Access when inserting hundred of thousands of rows 【发布时间】:2010-05-09 10:09:46 【问题描述】:我目前正在将数十万行数据添加到一个表中,首先是 MS Access 表,然后是 mysql 表。
我第一次尝试使用 MS Access,不到 40 秒。 然后我尝试使用与 MySQL 相同的源和相同的表结构,耗时 6 分 40 秒。速度慢了 1000%!!!
那么,数据库服务器具有更好的性能是神话吗?
【问题讨论】:
您需要添加更多关于您的设置和连接的信息。此外,这可能更适合服务器故障。这肯定是一个边缘案例......暂时不投票迁移。 您能描述一下您用来将数据插入每个数据库的方法吗?如果可能的话,使用一些代码/SQL语句。 从来没有人说过数据库服务器比基于文件的数据库性能更高。在相同的硬件上,我希望基于文件的系统更快。 @Neil 没人?好吧,我一直都听到:) @Mark & @Pekka 我正在使用 MS Access 通过 ODBC 连接到 MySQL,尽管它肯定比本机慢,我猜它应该像 Oracle 那样慢 2 倍。现在我可以在未来尝试使用 C# 或 Java,看看它是否会表现得更好,但我怀疑它会改变 10 倍。 【参考方案1】:执行数千个独立的 INSERT 会非常缓慢。由于 MySQL 是一个多用户、事务性数据库,因此在每个查询期间进行的操作要比 Access 多得多。 SQL 服务器上的每个 INSERT 操作都经过以下步骤:
-
解码和解析查询。
打开表进行写入,必要时建立锁。
插入新行。
如有必要,请更新索引。
将表保存到磁盘。
理想情况下,您希望尽可能少地执行步骤 1、2、4 和 5。 MySQL 有一些功能可以帮助您。
准备您的查询
通过准备要重复使用的查询,您只需执行第 1 步一次。方法如下:
PREPARE myinsert FROM 'INSERT INTO mytable VALUES (?, ?, ?)';
SET @id = 100;
SET @name = 'Joe';
SET @age = 34;
EXECUTE myinsert USING @id, @name, @age;
SET @id = 101;
SET @name = 'Fran';
SET @age = 23;
EXECUTE myinsert USING @id, @name, @age;
# Repeat until done
DEALLOCATE PREPARE myinsert;
在 mysql.com 网站上阅读有关 PREPARE 的更多信息。
使用事务
将几个(或几百个)INSERT 组合到一个事务中。服务器每次事务只需执行第 2、4 和 5 步一次。
PREPARE myinsert FROM 'INSERT INTO mytable VALUES (?, ?, ?)';
START TRANSACTION;
SET @id = 100;
SET @name = 'Joe';
SET @age = 34;
EXECUTE myinsert USING @id, @name, @age;
SET @id = 101;
SET @name = 'Fran';
SET @age = 23;
EXECUTE myinsert USING @id, @name, @age;
# Repeat a hundred times
COMMIT;
START TRANSACTION;
SET ...
SET ...
EXECUTE ...;
# Repeat a hundred times
COMMIT;
# Repeat transactions until done
DEALLOCATE PREPARE myinsert;
阅读更多关于transactions的信息。
从文件中加载表格
无需进行数千次插入,而是批量上传您的数据。如果您的数据位于分隔文件(例如 CSV)中,请使用 LOAD DATA 语句。
LOAD DATA LOCAL INFILE '/full/path/to/file/mydata.csv' INTO TABLE `mytable` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n';
这是LOAD DATA 上 MySQL 页面的链接。
【讨论】:
感谢您提供如此详尽的教程,我会尝试的。事实上,我别无选择,只能使用 MySQL。顺便说一句:MS Access 还可以支持多用户(理论上最多 255 个,实际上最多 20 个左右就可以了)甚至事务 - 事实上我也把事务放在了地方。我宁愿认为缓慢是由于 1°) odbc 和 2°) 因为 MS Access 是一个本地文件系统,不需要使用 tcp/ip 协议层来访问服务器。当我有时间的时候,我也应该使用 Oracle 进行测试,但 Oracle 的免费版本几乎与 Access 一样有限,因此不再有趣。【参考方案2】:通常,数据库最重要的性能方面不是插入数据的速度,而是查询数据的速度。我相信 MySQL 拥有比 MS Access 更强大的优化器,并且可以更好地利用索引。这方面的一个例子是loose index scan,它可以为某些类型的查询提供 10 倍或更多的速度。
此外,您用于插入数据的方法可能会影响插入所需的时间。例如,与许多单独的插入语句相比,使用批量插入通常会更快。此外,在插入时禁用索引并在之后再次启用它们可以提高性能。
【讨论】:
你说得对,我确定什么时候上传完我会比较查询速度。如果我不受 2 Go MS Access 大小的限制,我就不会使用 MySQL。事实上,即使查询速度要慢得多,我什至有兴趣使用多个 MS Access DB,每个数据库有 2 个 Go 并合并结果。感谢您的索引建议,将尝试。 当然,Msaccess 并不适合所有事情,但我对性能的巨大差距感到惊讶,至少对于非常简单的表上的插入而言。 @asksuperu:当然,这听起来是个好主意。但是,在您说数据库 X 在查询方面比数据库 Y 糟糕之前,请发布完整的测试信息,以便其他人可以重现您的结果,更重要的是审查和批评您的方法。没有同行评审的有缺陷的测试首先是导致这些神话的原因。 好吧,我的目的并不是真正进行比较,而是要完成一个相当广泛的用例:) 所以我的目的是找到最好的解决方案,而不是真的比较,但作为一个副作用,它是一个比较。【参考方案3】:MySQL 是否提供任何 SQL 跟踪工具以便您查看 Access 发送的内容?根据我通过 ODBC 将 Access 与 SQL Server 一起使用的经验,我可以告诉您 Jet 在批量插入方面做出了一些看似奇怪的决定。它所做的是为每条记录发送一个插入,而不是为所有记录发送一个批量插入。这会大大降低它的速度,但这确实意味着它不能用长时间的更新(以及相应的表锁等)来占用 SQL Server。
从插入的角度来看这是愚蠢的,但从成为良好的客户端/服务器公民的角度来看是聪明的——它允许 SQL Server 决定如何序列化请求的命令并将它们与来自其他用户的命令交错。这意味着锁比批量插入上的锁要短。
使用 SQL Server,您可以使用 ADO 来完成这项工作,并强制它以批处理的形式处理插入。我不知道 MySQL 有没有办法做到这一点。
需要考虑的一点:
如果源表和目标表都在 MySQL 中,则直通查询应使其完全由 MySQL 处理。
【讨论】:
可能 MySQL 有跟踪工具,但我真的不是它们的专家。您的评论很有趣,我也打算尝试 SQL Server,但由于您似乎评论它也会很慢,所以它不会比 MySQL 更有价值。就像 barry 建议的那样,sql passthrough 或批量插入是个好主意。 您能否详细说明“您可以使用 ADO 来解决问题并强制它将插入作为批处理进行处理”?我目前正在使用 DAO,不习惯 ADO。 来源是多个 MS Access 文件(每周 1 个,每个 250 Mb)我无法控制这种格式,因为它来自另一个部门。 ADO 提供了 DAO 所缺乏的批处理模式,因此将 ADO 用于特定目的并不可耻。我必须查找如何执行此操作,但它肯定是您的连接对象或执行方法的一些参数。 将多个Access文件作为源追加到MySQL中,或许MySQL可以从ODBC数据源加载读取数据。如果是这样,那么您可以在 SQL 中使用 IN 语句让 MySQL 检索其操作侧的所有数据(而不是让 Jet 将其分解为单独的语句。以上是关于插入数十万行时,MySQL 与 MS Access 相比非常慢的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 ODBC 驱动程序仅从 MS Access 向 MySql 自动插入新记录?
在 ms Access 中从每个组中选择至少 3 行时出错 - 仅选择了至少 2 行
在 MS Access (VBA) 中使用 ADODB 将非 ASCII 插入 MySQL 数据库时出现“不正确的字符串值”,但重试有效