【问题标题】:Why does an insert query occasionally take so long to complete?为什么插入查询有时需要这么长时间才能完成?
【发布时间】:2011-04-12 23:00:09
【问题描述】:

这是一个非常简单的问题。将数据插入表中通常工作正常,除了插入查询需要几秒钟的几次。 (我试图批量插入数据。)所以我为插入过程设置了一个模拟,以找出为什么插入查询有时需要超过 2 秒才能运行。 Joshua 建议索引文件可能正在调整;我删除了 id(主键字段),但延迟仍然发生。

我有一个 MyISAM 表:daniel_test_insert(此表开始时完全为空):

create table if not exists daniel_test_insert ( 
    id int unsigned auto_increment not null, 
    value_str varchar(255) not null default '', 
    value_int int unsigned default 0 not null, 
    primary key (id) 
)

我将数据插入其中,有时插入查询需要超过 2 秒才能运行。 此表上没有读取操作 - 仅由单线程程序串行写入。

我运行完全相同的查询 100,000 次以找出查询有时需要很长时间的原因。到目前为止,这似乎是一个随机事件。

这个查询例如用了 4.194 秒(插入的时间很长):

Query: INSERT INTO daniel_test_insert SET value_int=12345, value_str='afjdaldjsf aljsdfl ajsdfljadfjalsdj fajd as f' - ran for 4.194 seconds
status               | duration | cpu_user  | cpu_system | context_voluntary | context_involuntary | page_faults_minor
starting             | 0.000042 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
checking permissions | 0.000024 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
Opening tables       | 0.000024 | 0.001000  | 0.000000   | 0                 | 0                   | 0                
System lock          | 0.000022 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
Table lock           | 0.000020 | 0.000000  | 0.000000   | 0                 | 0                   | 0                
init                 | 0.000029 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
update               | 4.067331 | 12.151152 | 5.298194   | 204894            | 18806               | 477995           
end                  | 0.000094 | 0.000000  | 0.000000   | 8                 | 0                   | 0                
query end            | 0.000033 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
freeing items        | 0.000030 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
closing tables       | 0.125736 | 0.278958  | 0.072989   | 4294              | 604                 | 2301             
logging slow query   | 0.000099 | 0.000000  | 0.000000   | 1                 | 0                   | 0                
logging slow query   | 0.000102 | 0.000000  | 0.000000   | 7                 | 0                   | 0                
cleaning up          | 0.000035 | 0.000000  | 0.000000   | 7                 | 0                   | 0

(这是 SHOW PROFILE 命令的缩略版,我把全为零的列都扔掉了。)

现在更新有大量的上下文切换和轻微的页面错误。 Opened_Tables 在这个数据库上每 10 秒增加 1 个(没有用完 table_cache 空间)

统计数据:

  • MySQL 5.0.89

  • 硬件:32 Gigs 内存 / 8 核 @ 2.66GHz;突袭 10 个 SCSI 硬盘 (SCSI II???)

  • 我已询问硬盘驱动器和 RAID 控制器:没有报告错误。 CPU 大约有 50% 空闲。

  • iostat -x 5(报告硬盘利用率低于 10%) 顶部报告负载平均约 10 1 分钟(我们的数据库机器正常)

  • 已使用 156k 交换空间(32 gigs 内存)

我不知道是什么导致了这种性能滞后。这不会发生在我们的低负载从属设备上,只会发生在我们的高负载主机上。这也发生在内存和 innodb 表中。有没有人有什么建议? (这是一个生产系统,所以没有什么异国情调!)

【问题讨论】:

  • 其他表/数据库工作正常吗?可能有坏驱动器(system.log 中的任何 I/O 错误)?从 mysql 命令行查询需要多长时间? mysql mydb
  • 不,我们在其他表上也有这个问题。我创建了一个示例表,然后将其倾倒在上面以查看发生了什么。如果我使用 mysql mydb
  • “同一行;100,000 次”是什么意思?你的意思是 single 插入需要 2 秒,还是相同的插入 100,000 次需要 2 秒?
  • 我插入同一行,100,000 次。有时单次插入需要 2 秒或更长时间。当查询时间超过 2 秒时,插入 100,000 行只是在 20,000 次中找到 1 次。我会更新问题

标签: sql mysql performance query-cache


【解决方案1】:

我在我的系统上发现了同样的现象。通常需要一毫秒的查询会突然需要 1-2 秒。我的所有案例都是简单的单表 INSERT/UPDATE/REPLACE 语句 --- 不在任何 SELECT 上。没有明显的负载、锁定或螺纹堆积。

我曾怀疑这是由于清除脏页、刷新磁盘更改或某些隐藏的互斥锁造成的,但我还没有缩小范围。

也排除了

  • 服务器负载 -- 与高负载无关
  • 引擎 -- 发生在 InnoDB/MyISAM/Memory 上
  • MySQL 查询缓存 -- 无论是打开还是关闭都会发生
  • 日志轮换 -- 事件中没有相关性

我在这一点上唯一的其他观察来自我在多台机器上运行相同的数据库这一事实。我有一个繁重的读取应用程序,所以我正在使用一个具有复制功能的环境——大部分负载都在从属设备上。我注意到,即使主机上的负载最小,这种现象也更多地发生在那里。即使我没有看到锁定问题,也许是 Innodb/Mysql 在(线程)并发方面遇到了问题?回想一下,从站上的更新将是单线程的。

MySQL 版本 5.1.48

更新

我认为我对我的案子的问题有线索。在我的一些服务器上,我注意到这种现象比其他服务器更多。看到不同服务器之间有什么不同,并调整了一些东西,我被带到了MySQL innodb system variableinnodb_flush_log_at_trx_commit

我觉得文档有点难读,但innodb_flush_log_at_trx_commit 可以取值 1,2,0:

  • 对于 1,日志缓冲区被刷新到 每次提交的日志文件和日志 每次提交时,文件都会刷新到磁盘。
  • 对于 2,日志缓冲区被刷新到 每次提交的日志文件和日志 文件大约每 1-2 秒刷新到磁盘一次。
  • 对于 0,日志缓冲区被刷新到 每秒的日志文件,以及日志 文件每秒刷新到磁盘。

实际上,按照报告和记录的顺序 (1,2,0),您应该获得更高的交易业绩以增加风险。

话虽如此,我发现带有innodb_flush_log_at_trx_commit=0 的服务器的性能比带有innodb_flush_log_at_trx_commit=2 的服务器性能更差(即“长时间更新”的10-100 倍)。此外,当我将其切换为 2 时,坏实例的情况立即得到改善(请注意,您可以随时更改它)。

所以,我的问题是,你的设置是什么?请注意,我并不是在责怪这个参数,而是强调它的上下文与这个问题有关。

【讨论】:

  • 我最近升级到 5.1.48。我认为这是查询缓存。我把它关掉了,事情开始变得更好了。
  • 大约一年前我确实遇到过类似的问题,当时我的 query_cache 太大,所以当一大块数据失效时,整个服务器会锁定长达 30 秒。关于这一点,请参阅:dom.as/2009/07/08/query-cache-tuning
  • 我在我的 CentOS 系统上发现了同样的问题。在 innodb_flush_log_at_trx_commit=1 的情况下,我的 UPDATE 需要 0.040 秒才能完成,其中 99% 的时间处于“关闭表”状态。当我切换到 0 或 2 时,我的整个 UPDATE 在 0.0002 秒内完成!
  • 这非常有帮助!我运行了一个将行插入数据库的脚本,并且花了大约 15 分钟来插入少于 20k 的元组。将该变量从 1 更改为 0 在大约 3-4 秒内插入超过 30k 行!
  • 可能我来的有点晚了,但是由于这个回复还是搜索引擎建议的,所以使用值2会破坏mysql的ACID合规性,小心!
【解决方案2】:

我在使用 INNODB 表时遇到了这个问题。 (而且 INNODB 索引的重写速度甚至比 MISAM 还要慢)

我想您正在对其他一些表执行多个其他查询,所以问题是 MySQL 必须处理文件中的磁盘写入变得更大并且需要为这些文件分配额外的空间。

如果您使用MYISAM 表,我强烈建议您使用

LOAD DATA INFILE 'file-on-disk' INTO TABLE `tablename` 

命令; MYISAM 的速度非常快(即使使用主键),文件可以格式化为 csv,您可以指定列名(或者您可以将 NULL 作为自动增量字段的值)。

View MYSQL doc here.

【讨论】:

    【解决方案3】:

    我要给你的第一个提示是禁用自动提交功能而不是手动提交。

    LOCK TABLES a WRITE;
    ... DO INSERTS HERE
    UNLOCK TABLES;
    

    这有利于性能,因为索引缓冲区仅在所有 INSERT 语句完成后刷新到磁盘一次。通常,索引缓冲区刷新的次数与 INSERT 语句的次数一样多。

    但最好你能做到,如果在你的应用程序中可以做到这一点,你可以通过一次选择进行批量插入。

    这是通过矢量绑定完成的,这是您可以采用的最快方式。

    Instead
    of:
    "INSERT INTO tableName values()"
    DO
    "INSERT INTO tableName values(),(),(),().......(n) " ,
    

    但仅当参数向量绑定可以与您使用的 mysql 驱动程序结合时才考虑此选项。

    否则我会倾向于第一种可能性,并为每 1000 次插入锁定表。不要将其锁定为 100k 插入,因为您会遇到缓冲区溢出。

    【讨论】:

    • 您使用什么编程语言?或许我可以给你举个例子。
    • 我正在使用红宝石。但是,我正在执行相同的插入 100,000 次,因为其中一些需要很长时间。我试图找出其中一些需要这么长时间的原因。
    • 那么您是否尝试锁定、解锁表并检查性能表现如何?
    • 我删除了索引,以免出现索引问题。但我仍然遇到同样的性能问题。
    • 我在非生产机器上没有这个问题,这让我认为这是由于临时“尖峰”或其他随机干扰插入的全局锁造成的。这将解释周期性插入延迟。或者也许只是在代码中的其他地方“大量”编写或两次编写的糟糕时间。
    【解决方案4】:

    您能否再创建一个包含 400(非空)列的表并再次运行您的测试?如果慢插入的数量变得更高,这可能表明 MySQL 正在浪费时间写入记录。 (我不知道它是如何工作的,但他可能正在分配更多的块,或者移动一些东西以避免碎片......真的不知道)

    【讨论】:

    • 我添加了更多列,我发现写入速度很慢。我想知道延迟是否与将数据写入硬盘有关。我们的硬盘似乎具有性能容量。
    • 即每秒 34 次写入。对于非索引表来说,这非常慢!
    • 我不知道,但你可以运行一些 MySQL 碎片整理工具,看看是否有帮助。我认为 MySQL 正在浪费您的时间寻找放置记录的好地方...
    【解决方案5】:

    我们升级到 MySQL 5.1,在此事件中,查询缓存成为问题,出现很多“释放项目?”线程状态。然后我们删除了查询缓存。

    升级到 MySQL 5.1 或删除查询缓存解决了这个问题。

    仅供参考,未来的读者。

    -丹尼尔

    【讨论】:

      【解决方案6】:

      我们遇到了完全相同的问题并在此报告: http://bugs.mysql.com/bug.php?id=62381

      我们正在使用 5.1.52,但还没有解决方案。我们可能需要关闭 QC 以避免这种性能问题。

      【讨论】:

        【解决方案7】:

        如果您使用 for 循环同时使用多个插入,那么请在每个循环后使用 PHP 的 sleep("time in seconds") 函数休息一下。

        【讨论】:

          【解决方案8】:

          在 Myisam Performance 上阅读: http://adminlinux.blogspot.com/2010/05/mysql-allocating-memory-for-caches.html

          搜索:

          'MyISAM 密钥块大小密钥块大小很重要'(减去单引号),这可能是正在发生的事情。我认为他们在 5.1 中修复了其中一些类型的问题

          【讨论】:

          • 问题是即使删除了密钥,这种情况仍然会发生。 IE,没有任何索引的表。
          • 可以理解,(我认为)MySQL 会阻止空间写入记录。所以有些东西(例如原因我不知道)说它阻塞了足够的空间来写入 5 条记录,在空间用完后 MySQL 需要为下一组记录保留另一块空间。这可能是发生的额外执行时间。
          【解决方案9】:

          你能检查磁盘子系统的统计数据吗? I/O 是否饱和?这听起来像是内部数据库工作正在将内容刷新到磁盘/日志。

          【讨论】:

          • 好吧,我无法对 IO 进行精确测量。但是我已经运行了“iostat -x 5”并且在运行时我没有看到任何关于 5-10% 的信息。由于“平均”,可能存在可能隐藏的临时峰值。除了观看 vmstat 之外,我不知道如何查询系统以获得更精细的测量结果,这也没有产生任何有用的信息
          • 我在 SQLServer 中看到了一些性能问题——当它不是我的代码时(大声笑),它通常是磁盘子系统。 SQLServer 有一个恢复间隔参数,SQLServer 在该参数中将事物刷新到磁盘。如果您真的在插入并且恢复间隔受到打击,那么性能会受到重创,这会导致应用程序冒泡,因为某些插入需要很长时间,实际上它与插入无关,只是一些内部SQLServer 中的内务管理 - 也可能是磁盘通道 - 心轴和/或磁盘控制器仅处于 100%
          【解决方案10】:

          要检查您的磁盘是否表现不佳,如果您在 Windows 中,您可以创建一个批处理 cmd 文件来创建 10,000 个文件:

          @echo OFF
          FOR /L %%G IN (1, 1, 10000) DO TIME /T > out%%G.txt
          

          将其保存在临时目录中,例如 test.cmd

          使用 /E:ON 参数启用运行 CMD 的命令扩展

          CMD.exe /E:ON
          

          然后运行批处理并查看第一个输出文件和最后一个输出文件之间的时间是否以秒或分钟为单位不同。

          在 Unix/Linux 上,您可以编写类似的 shell 脚本。

          【讨论】:

            【解决方案11】:

            服务器中是否有 SSD 驱动器?一些 SSD 驱动器会出现“studder”,这可能会导致您的症状。

            无论如何,我会尝试找出延迟是发生在 MySQL 中还是发生在磁盘子系统中。

            您的服务器是什么操作系统,MySQL 数据在什么文件系统上?

            【讨论】:

            • 我们有真正的 SCSI 硬盘。我们将 CentOS 5.4 和 xfs 用于文件系统/文件快照。我曾考虑过使用 FusionIO,但它从未真正获得关注。
            猜你喜欢
            • 1970-01-01
            • 2013-02-06
            • 1970-01-01
            • 2017-12-30
            相关资源
            最近更新 更多