简体   繁体   English

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

[英]Why does an insert query occasionally take so long to complete?

This is a pretty simple problem. 这是一个非常简单的问题。 Inserting data into the table normally works fine, except for a few times where the insert query takes a few seconds. 将数据插入表中通常可以正常工作,除了插入查询需要几秒钟的几次。 (I am not trying to bulk insert data.) So I setup a simulation for the insert process to find out why the insert query occasionally takes more than 2 seconds to run. (我不是试图批量插入数据。)因此,我为插入过程设置了一个模拟,以找出插入查询偶尔运行超过2秒的原因。 Joshua suggested that the index file may be being adjusted; 约书亚建议可以调整索引文件; I removed the id (primary key field), but the delay still happens. 我删除了id(主键字段),但延迟仍然发生。

I have a MyISAM table: daniel_test_insert (this table starts completely empty): 我有一个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) 
)

I insert data into it, and sometimes a insert query takes > 2 seconds to run. 我将数据插入其中,有时插入查询需要> 2秒才能运行。 There are no reads on this table - Only writes, in serial, by a single threaded program. 此表上没有读取 - 只能通过单线程程序串行写入。

I ran the exact same query 100,000 times to find why the query occasionall takes a long time. 我运行完全相同的查询100,000次,以查找查询时间为何需要很长时间。 So far, it appears to be a random occurrence. 到目前为止,它似乎是随机发生的。

This query for example took 4.194 seconds (a very long time for an insert): 例如,此查询花了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

(This is an abbreviated version of the SHOW PROFILE command, I threw out the columns that were all zero.) (这是SHOW PROFILE命令的缩写版本,我扔掉了全部为零的列。)

Now the update has an incredible number of context switches and minor page faults. 现在,更新具有令人难以置信的上下文切换次数和次要页面错误。 Opened_Tables increases about 1 per 10 seconds on this database (not running out of table_cache space) Opened_Tables在这个数据库上每10秒增加大约1个(没有用完table_cache空间)

Stats: 统计:

  • MySQL 5.0.89 MySQL 5.0.89

  • Hardware: 32 Gigs of ram / 8 cores @ 2.66GHz; 硬件:32 GB演出/ 8核@ 2.66GHz; raid 10 SCSI harddisks (SCSI II???) raid 10 SCSI硬盘(SCSI II ???)

  • I have had the hard drives and raid controller queried: No errors are being reported. 我查询了硬盘驱动器和raid控制器:没有报告错误。 CPUs are about 50% idle. CPU空闲率约为50%。

  • iostat -x 5 (reports less than 10% utilization for harddisks) top report load average about 10 for 1 minute (normal for our db machine) iostat -x 5(报告硬盘利用率低于10%)最高报告平均负载平均约为10分钟(我们的数据库机正常)

  • Swap space has 156k used (32 gigs of ram) 交换空间有156k使用(32演出的ram)

I'm at a loss to find out what is causing this performance lag. 我不知道是什么导致这种性能滞后。 This does NOT happen on our low-load slaves, only on our high load master. 这不会发生在我们的低负载从站上,只发生在我们的高负载主站上。 This also happens with memory and innodb tables. 内存和innodb表也会发生这种情况。 Does anyone have any suggestions? 有没有人有什么建议? (This is a production system, so nothing exotic!) (这是一个生产系统,所以没有异国情调!)

I have noticed the same phenomenon on my systems. 我在我的系统上发现了同样的现象。 Queries which normally take a millisecond will suddenly take 1-2 seconds. 通常需要一毫秒的查询将突然需要1-2秒。 All of my cases are simple, single table INSERT/UPDATE/REPLACE statements --- not on any SELECTs. 我的所有情况都是简单的单表INSERT / UPDATE / REPLACE语句---不在任何SELECT上。 No load, locking, or thread build up is evident. 没有明显的负载,锁定或螺纹堆积。

I had suspected that it's due to clearing out dirty pages, flushing changes to disk, or some hidden mutex, but I have yet to narrow it down. 我怀疑它是由于清除了脏页,刷新磁盘更改或一些隐藏的互斥锁,但我还没有缩小范围。

Also Ruled Out 也排除了

  • Server load -- no correlation with high load 服务器负载 - 与高负载无关
  • Engine -- happens with InnoDB/MyISAM/Memory 引擎 - 与InnoDB / MyISAM / Memory一起发生
  • MySQL Query Cache -- happens whether it's on or off MySQL查询缓存 - 无论是打开还是关闭
  • Log rotations -- no correlation in events 记录旋转 - 事件中没有相关性

The only other observation I have at this point is derived from the fact I'm running the same db on multiple machines. 我在这一点上唯一的另一个观察是从我在多台机器上运行相同的数据库的事实中得出的。 I have a heavy read application so I'm using an environment with replication -- most of the load is on the slaves. 我有一个繁重的读取应用程序,所以我使用的是复制环境 - 大部分负载都在奴隶上。 I've noticed that even though there is minimal load on the master, the phenomenon occurs more there. 我注意到即使主机上的负载很小,这种现象也会发生。 Even though I see no locking issues, maybe it's Innodb/Mysql having trouble with (thread) concurrency? 即使我没有看到锁定问题,也许它的Innodb / Mysql在(线程)并发方面遇到了麻烦? Recall that the updates on the slave will be single threaded. 回想一下,从站的更新将是单线程的。

MySQL Verion 5.1.48 MySQL Verion 5.1.48

Update 更新

I think I have a lead for the problem on my case. 我认为我的问题在我的案件中处于领先地位。 On some of my servers, I noticed this phenomenon on more than the others. 在我的一些服务器上,我注意到这种现象比其他服务器更多。 Seeing what was different between the different servers, and tweaking things around, I was lead to the MySQL innodb system variable innodb_flush_log_at_trx_commit . 看到不同服务器之间有什么不同,并调整周围的事情,我领导了MySQL innodb系统变量 innodb_flush_log_at_trx_commit

I found the doc a bit awkward to read, but innodb_flush_log_at_trx_commit can take the values of 1,2,0: 我发现doc有点难以阅读,但是innodb_flush_log_at_trx_commit可以取1,2,0的值:

  • For 1, the log buffer is flushed to the log file for every commit, and the log file is flushed to disk for every commit. 对于1,每次提交都会将日志缓冲区刷新到日志文件中,并且每次提交都会将日志文件刷新到磁盘。
  • For 2, the log buffer is flushed to the log file for every commit, and the log file is flushed to disk approximately every 1-2 seconds. 对于2,每次提交都会将日志缓冲区刷新到日志文件中,并且大约每1-2秒将日志文件刷新到磁盘。
  • For 0, the log buffer is flushed to the log file every second, and the log file is flushed to disk every second. 对于0,日志缓冲区每秒刷新到日志文件,日志文件每秒刷新到磁盘。

Effectively, in the order (1,2,0), as reported and documented, you're supposed to get with increasing performance in trade for increased risk. 实际上,按照报告和记录的顺序(1,2,0),您应该在交易中获得越来越高的风险。

Having said that, I found that the servers with innodb_flush_log_at_trx_commit=0 were performing worse (ie having 10-100 times more "long updates") than the servers with innodb_flush_log_at_trx_commit=2 . 说了这么多,我发现,与服务器innodb_flush_log_at_trx_commit=0是不是与服务器进行更糟糕的(即具有10-100倍以上的“长更新”) innodb_flush_log_at_trx_commit=2 Moreover, things immediately improved on the bad instances when I switched it to 2 (note you can change it on the fly). 此外,当我将其切换为2时,在不良实例上的情况会立即得到改善(请注意,您可以动态更改它)。

So, my question is, what is yours set to? 所以,我的问题是,你的目标是什么? Note that I'm not blaming this parameter, but rather highlighting that it's context is related to this issue. 请注意,我不是指责此参数,而是强调它的上下文与此问题有关。

I had this problem using INNODB tables. 我使用INNODB表遇到了这个问题。 (and INNODB indexes are even slower to rewrite than MYISAM) (和INNODB索引重写比MYISAM更慢)

I suppose you are doing multiple other queries on some other tables, so the problem would be that MySQL has to handle disk writes in files that get larger and needs to allocate additional space to those files. 我想你在其他一些表上做了多个其他查询,所以问题是MySQL必须处理更大的文件中的磁盘写入,并需要为这些文件分配额外的空间。

If you use MYISAM tables I strongly suggest using 如果您使用MYISAM表我强烈建议使用

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

command; 命令; MYISAM is sensationally fast with this (even with primary keys) and the file can be formatted as csv and you can specify the column names (or you can put NULL as the value for the autoincrement field). MYISAM的速度非常快(即使使用主键),文件可以格式化为csv,您可以指定列名称(或者可以将NULL作为自动增量字段的值)。

View MYSQL doc here . 在此处查看MYSQL文档

The first Tip I would give you, is to disable the autocommit functionality and than commit manually. 我给你的第一个提示是禁用自动提交功能,而不是手动提交。

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

This benefits performance because the index buffer is flushed to disk only once, after all INSERT statements have completed. 这有利于提高性能,因为在完成所有INSERT语句之后,索引缓冲区仅刷新到磁盘一次。 Normally, there would be as many index buffer flushes as there are INSERT statements. 通常,存在与INSERT语句一样多的索引缓冲区刷新。

But propably best you can do, and if that is possible in your application, you do a bulk insert with one single select. 但是你可以做到最好,如果你的应用程序可以做到这一点,你可以用一个选择进行批量插入。

This is done via Vector Binding and it's the fastest way you can go. 这是通过Vector Binding完成的,这是您可以采用的最快方式。

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

But consider this option only if parameter vector binding is possible with your mysql driver you're using. 但是,只有在您使用的mysql驱动程序可以进行参数向量绑定时才考虑此选项。

Otherwise I would tend to the first possibility and LOCK the table for every 1000 inserts. 否则我倾向于第一种可能性并且每1000次插入就锁定表格。 Don't lock it for 100k inserts, because you'l get a buffer overflow. 不要锁定100k插入,因为你会得到缓冲区溢出。

Can you create one more table with 400 (not null) columns and run your test again? 您可以再创建一个包含400(非空)列的表并再次运行测试吗? If the number of slow inserts became higher this could indicate MySQL is wasting time writing your records. 如果慢插入的数量变得更高,这可能表明MySQL正在浪费时间编写您的记录。 (I dont know how it works, but he may be alocating more blocks, or moving something to avoid fragmentation.... really dont know) (我不知道它是如何工作的,但他可能会更多的块,或者移动一些东西以避免碎片....真的不知道)

We hit exactly the same issue and reported here: http://bugs.mysql.com/bug.php?id=62381 我们遇到了完全相同的问题,并在此处报告: http//bugs.mysql.com/bug.php?id = 62381

We are using 5.1.52 and don't have solution yet. 我们正在使用5.1.52并且还没有解决方案。 We may need to turn QC off to avoid this perf hit. 我们可能需要关闭QC以避免此命中。

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

Search for: 搜索:

'The MyISAM key block size The key block size is important' (minus the single quotes), this could be what's going on. 'MyISAM密钥块大小密钥块大小很重要'(减去单引号),这可能是正在发生的事情。 I think they fixed some of these types of issues with 5.1 我认为他们用5.1修复了这些类型的问题

Can you check the stats on the disk subsystem? 你能查看磁盘子系统上的统计数据吗? Is the I/O satuated? I / O是否饱和? This sounds like internal DB work going on flushing stuff to disk/log. 这听起来像内部数据库工作正在将内容刷新到磁盘/日志。

To check if your disk is behaving badly, and if you're in Windows, you can create a batch cmd file that creates 10,000 files: 要检查磁盘是否表现不佳,如果您使用的是Windows,则可以创建一个创建10,000个文件的批处理cmd文件:

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

save it in a temp dir, like test.cmd 将它保存在临时目录中,例如test.cmd

Enable command extensions running CMD with the /E:ON parameter 使用/ E:ON参数启用运行CMD的命令扩展

CMD.exe /E:ON

Then run your batch and see if the time between the first and the last out file differ in seconds or minutes. 然后运行批处理,查看第一个和最后一个输出文件之间的时间是否以秒或分钟为单位。

On Unix/Linux you can write a similare shell script. 在Unix / Linux上,您可以编写类似的shell脚本。

By any chance is there an SSD drive in the server? 服务器中是否有SSD驱动器? Some SSD drives suffer from 'studder', which could cause your symptom. 一些SSD驱动器遭受'studder',这可能会导致您的症状。

In any case, I would try to find out if the delay is occurring in MySQL or in the disk subsystem. 无论如何,我会试着找出MySQL或磁盘子系统中是否发生了延迟。

What OS is your server, and what file system is the MySQL data on? 您的服务器是什么操作系统,MySQL数据是什么文件系统?

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

We upgraded to MySQL 5.1 and during this event the Query cache became an issue with a lot of "Freeing items?" 我们升级到了MySQL 5.1,在这个事件中,查询缓存成了很多“Freeing items?”的问题。 thread states. 线程状态。 We then removed the query cache. 然后我们删除了查询缓存。

Either the upgrade to MySQL 5.1 or removing the query cache resolved this issue. 升级到MySQL 5.1或删除查询缓存都解决了这个问题。

FYI, to future readers. 仅供参考,对未来的读者。

-daniel -daniel

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM