【问题标题】:Slow MySQL inserts缓慢的 MySQL 插入
【发布时间】:2010-11-24 14:20:15
【问题描述】:

我正在使用和开发使用 MySQL 作为后端引擎的软件(它可以使用其他软件,例如 PostgreSQL、Oracle 或 SQLite,但这是我们正在使用的主要应用程序)。该软件的设计方式是我们要访问的二进制数据作为 BLOBs 保存在各个列中(每个表都有一个 BLOB 列,其他列有整数/浮点数来表征 BLOB,以及一个带有 BLOB 的 MD5 散列的字符串列)。这些表通常有 2、3 或 4 个索引,其中一个始终是 MD5 列,即UNIQUE。有些表已经有数百万个条目,它们的大小已经进入了数千兆字节。我们在同一台服务器中保留了单独的每年 MySQL 数据库(到目前为止)。对于一般应用程序(Dell PowerEdge 2U 型服务器)来说,硬件是相当合理的(我认为)。

MySQL SELECT 查询相对较快。那里几乎没有抱怨,因为这些(大部分时间)处于批处理模式。但是,INSERT 查询需要很长时间,这会随着表大小(行数)的增加而增加。诚然,这是因为 MD5 列的类型为 UNIQUE,因此每个 INSERT 都必须确定每个新行是否具有对应的已插入的 MD5 字符串。如果有其他索引(不是唯一的),性能会变差,这并不太奇怪(我认为)。但我仍然无法放心,这种软件架构选择(我怀疑将 BLOB 保留在表行中而不是磁盘中会产生重大的负面影响)不是最佳选择。插入并不重要,但有这种感觉是很烦人的。

有没有人遇到过类似情况?使用 MySQL,甚至其他(最好是基于 Linux 的)RDBMes?您愿意提供任何见解,也许是一些性能数据?

顺便说一句,工作语言是 C++(它封装了对 MySQL API 的 C 调用)。

【问题讨论】:

    标签: mysql database performance insert indexing


    【解决方案1】:

    可能是时候进行水平分区并将 blob 字段移动到单独的表中了。在“垂直分区的快速旁注”中的 article 中,作者从表中删除了一个较大的 varchar 字段,它提高了查询速度大约一个数量级。

    原因是,如果要覆盖的空间更少,磁盘上数据的物理遍历会显着加快,因此将更大的字段移动到其他地方会提高性能。

    此外(您可能已经这样做了)将索引列的大小减小到其绝对最小值(对于 md5 的 ascii 编码中的 char(32))是有益的,因为键的大小与速度成正比它的用途。

    如果您使用 InnoDB 表一次执行多个插入,则可以通过将它们包装到事务中并在一个查询中执行多个插入来显着提高插入速度:

    START TRANSACTION
    INSERT INTO x (id, md5, field1, field2) values (1, '123dab...', 'data1','data2'),(2,'ab2...','data3','data4'),.....;
    COMMIT
    

    【讨论】:

    • BEGIN TRANSACTION 选项似乎是目前最直接的解决方案。但是我有几个问题,因为我不熟悉它(同时我会保留 RTFM)。 1) 由于 MySQL 的 API 是用 C 语言编写的,因此我们可能会搞砸并尝试插入指向 null 的 VALUES。这几乎肯定会导致客户端应用程序因 SEGFAULT 而死。在这种情况下,交易会“脏”吗?需要清理吗?如何? 2) 我们正在使用 MyISAM。现在切换到 InnoDB 可能会很痛苦和/或危险。您知道 MyISAM 上的 TRANSACTION 性能吗?提前感谢您的反馈。
    • 好的。您知道 INSERT INTO 语法是否对查询字符串的长度施加了限制吗? I.o.w.,MySQL 是否接受长度为(例如)数亿个字符的 INSERT INTO 查询字符串?我闻起来像麻烦。
    • 限制由配置文件中的 max_allowed_pa​​cket 决定。我保留我的16M。我通常在一个插入语句中添加大约 10000 条记录
    【解决方案2】:

    Speed of INSERT Statements。你有频繁的 MD5 冲突吗?我相信这些不应该发生太多次,所以也许你可以使用INSERT ... ON DUPLICATE 之类的东西来处理冲突。如果您有特定的插入时间段,您可以在插入时间disable keys 并稍后恢复它们。另一种选择是使用replication,使用主机进行插入,使用从机进行选择。

    【讨论】:

    • 感谢您的建议,我会研究一下。
    • 来自上面的链接:“如果您同时从同一个客户端插入多行,请使用带有多个 VALUES 列表的 INSERT 语句一次插入多行。这要快得多(很多在某些情况下比使用单独的单行 INSERT 语句快几倍。”这使我插入 6K 记录的过程从 2.5 分钟缩短到 5 秒
    【解决方案3】:

    您在使用 MyISAM 吗?
    AFAIK MyISAM 的读取性能非常好,但写入性能较差。

    InnoDB 的速度应该是平衡的。

    【讨论】:

    • 是的,我忘记了那个细节。我们使用的服务器使用 MyISAM。
    【解决方案4】:

    您的数据是否适合 RAM?如果没有,请获得更多 RAM,直到变得不经济为止(对于大多数人来说,16G 通常是重点)。

    那么,您的索引是否适合 MyISAM 密钥缓冲区?

    如果您运行的是 32 位操作系统,请不要这样做。使用 64 位操作系统后,将密钥缓冲区设置为内存的大约 1/3。操作系统的缓存使用 RAM 来缓存数据文件(这对插入作用不大,但对选择有益)。

    在 MyISAM 中拥有数 GB 的表可能会很痛苦,因为在不正常关机的情况下,需要非常冗长的修复操作,但是

    不要在没有对应用程序进行重要验证的情况下切换 MySQL 引擎,它会在很多方面改变行为(不仅仅是性能)。它会影响磁盘空间的使用。

    【讨论】:

      【解决方案5】:

      我今天也问了一个有点相关的question

      提供的答案之一是考虑INSERT DELAYED,以便它进入插入队列,并在数据库不那么忙时进行处理。

      【讨论】:

      • 基于此线程中的回复,我查看了 MySQL 的 INSERT DELAYED 文档。他们声称在选择 INSERT DELAYED 时应该小心,因为与“正常”INSERT 相比,它会降低单客户端插入的整体性能。
      • 好的 - 我自己没有看到,虽然我也希望能够加速执行插入的脚本,而不是提高插入本身的性能
      • 值得注意的是,INSERT DELAYED 适用于 MyASAM,但不适用于 InnoDB
      猜你喜欢
      • 2012-03-12
      • 2014-12-05
      • 1970-01-01
      • 1970-01-01
      • 2020-12-05
      • 2011-06-17
      • 2013-08-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多