为什么插入查询偶尔需要很长时间才能完成?

时间:2010-09-15 23:05:14

标签: sql mysql performance query-cache

这是一个非常简单的问题。将数据插入表中通常可以正常工作,除了插入查询需要几秒钟的几次。 (我尝试批量插入数据。)因此,我为插入过程设置了一个模拟,以找出插入查询偶尔运行超过2秒的原因。约书亚建议可以调整索引文件;我删除了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 GB的ram / 8核@ 2.66GHz;突袭10个SCSI硬盘(SCSI II ???)

  • 我查询了硬盘驱动器和raid控制器:没有报告错误。 CPU闲置率约为50%。

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

  • 交换空间使用了156k(32 GB的ram)

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

11 个答案:

答案 0 :(得分:20)

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

我曾怀疑这是因为清除了脏页,刷新磁盘更改或隐藏的互斥锁,但我还没有将其缩小。

也排除了

  • 服务器负载 - 与高负载无关“
  • 引擎 - 使用InnoDB / MyISAM / Memory
  • MySQL查询缓存 - 无论是开启还是关闭
  • 记录轮换 - 事件中没有相关性

此时我唯一的另一个观察结果来自我在多台机器上运行相同数据库的事实。我有一个繁重的读取应用程序,所以我使用的是复制环境 - 大部分负载都在奴隶上。我注意到即使主机上的负载很小,这种现象也会发生。即使我没有看到锁定问题,也许它的Innodb / Mysql在(线程)并发方面遇到了麻烦?回想一下,slave上的更新将是单线程的。

MySQL Verion 5.1.48

<强>更新

我认为我对我的案件有问题。在我的一些服务器上,我注意到这种现象比其他服务器更多。看到不同服务器之间有什么不同,并调整周围的事情,我领先于MySQL innodb system variable innodb_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时,事情立即得到改善(注意你可以动态更改)。

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

答案 1 :(得分:1)

我使用INNODB表遇到了这个问题。 (和INNODB索引的重写速度甚至比MYISAM慢)

我想你在其他一些表上做了多个其他查询,所以问题在于MySQL必须处理更大的文件中的磁盘写入,并且需要为这些文件分配额外的空间。

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

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

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

View MYSQL doc here

答案 2 :(得分:1)

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

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

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

但是你可以做得最好,如果你的应用程序可以做到这一点,你只需一次选择即可进行批量插入。

这是通过Vector Binding完成的,这是你可以采用的最快方式。

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

但是,只有在您正在使用的mysql驱动程序可以进行参数向量绑定时才考虑此选项。

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

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

我们正在使用5.1.52并且还没有解决方案。我们可能需要关闭QC以避免此命中。

答案 5 :(得分:0)

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

搜索:

'MyISAM密钥块大小密钥块大小很重要'(减去单引号),这可能是正在发生的事情。我认为他们用5.1

解决了一些类型的问题

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

@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脚本。

答案 8 :(得分:0)

服务器中是否有SSD驱动器?一些SSD驱动器受到“研究者”的影响,这可能会导致您的症状。

在任何情况下,我都会尝试查看MySQL或磁盘子系统中是否发生延迟。

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

答案 9 :(得分:0)

我们升级到了MySQL 5.1,在这个事件中,查询缓存成了很多“Freeing items?”的问题。线程状态。然后我们删除了查询缓存。

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

仅供参考,未来的读者。

-daniel

答案 10 :(得分:0)

如果你在一个使用for循环使用多个插入,那么请在每个循环后使用PHP的睡眠(“以秒为单位的时间”)函数休息一下。