MySQL实例拖延“做SYNC索引”


12

问题

运行(主要是)一个带有InnoDB表的数据库的MySQL 5.6.20实例在1-4分钟的时间内偶尔出现所有更新操作的停顿,而所有INSERT,UPDATE和DELETE查询都保持“查询结束”状态。这显然是最不幸的。MySQL慢查询日志甚至记录了具有疯狂查询时间的最琐碎的查询,其中数百个具有相同的时间戳,对应于已解决停顿的时间点:

# Query_time: 101.743589  Lock_time: 0.000437 Rows_sent: 0  Rows_examined: 0
SET timestamp=1409573952;
INSERT INTO sessions (redirect_login2, data, hostname, fk_users_primary, fk_users, id_sessions, timestamp) VALUES (NULL, NULL, '192.168.10.151', NULL, 'anonymous', '64ef367018099de4d4183ffa3bc0848a', '1409573850');

设备统计信息显示出增加了,尽管在此时间段内I / O负载没有过多(在这种情况下,根据上述语句的时间戳,更新停顿了14:17:30-14:19:12):

# sar -d
[...]
02:15:01 PM       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
02:16:01 PM    dev8-0     41.53    207.43   1227.51     34.55      0.34      8.28      3.89     16.15
02:17:01 PM    dev8-0     59.41    137.71   2240.32     40.02      0.39      6.53      4.04     24.00
02:18:01 PM    dev8-0    122.08   2816.99   1633.44     36.45      3.84     31.46      1.21      2.88
02:19:01 PM    dev8-0    253.29   5559.84   3888.03     37.30      6.61     26.08      1.85      6.73
02:20:01 PM    dev8-0    101.74   1391.92   2786.41     41.07      1.69     16.57      3.55     36.17
[...]
# sar
[...]
02:15:01 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
02:16:01 PM     all     15.99      0.00     12.49      2.08      0.00     69.44
02:17:01 PM     all     13.67      0.00      9.45      3.15      0.00     73.73
02:18:01 PM     all     10.64      0.00      6.26     11.65      0.00     71.45
02:19:01 PM     all      3.83      0.00      2.42     24.84      0.00     68.91
02:20:01 PM     all     20.95      0.00     15.14      6.83      0.00     57.07

我经常在mysql慢日志中注意到,最老的查询停顿是使用VARCHAR主键和全文本搜索索引插入到大表(约10 M行)中:

CREATE TABLE `files` (
  `id_files` varchar(32) NOT NULL DEFAULT '',
  `filename` varchar(100) NOT NULL DEFAULT '',
  `content` text,
  PRIMARY KEY (`id_files`),
  KEY `filename` (`filename`),
  FULLTEXT KEY `content` (`content`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

进一步的调查(即SHOW ENGINE INNODB STATUS)表明,它确实始终是对使用全文索引的表的更新,这将导致停顿。“ SHOW ENGINE INNODB STATUS”的相应TRANSACTIONS部分具有与以下两个条目类似的条目,它们表示最早的正在运行的事务:

---TRANSACTION 162269409, ACTIVE 122 sec doing SYNC index
6 lock struct(s), heap size 1184, 0 row lock(s), undo log entries 19942
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_1" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_2" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_3" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_4" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_5" trx id 162269409 lock mode IX
TABLE LOCK table "vw"."FTS_000000000000224a_00000000000036b9_INDEX_6" trx id 162269409 lock mode IX
---TRANSACTION 162269408, ACTIVE (PREPARED) 122 sec committing
mysql tables in use 1, locked 1
1 lock struct(s), heap size 360, 0 row lock(s), undo log entries 1
MySQL thread id 165998, OS thread handle 0x7fe0e239c700, query id 91208956 192.168.10.153 root query end
INSERT INTO files (id_files, filename, content) VALUES ('f19e63340fad44841580c0371bc51434', '1237716_File_70380a686effd6b66592bb5eeb3d9b06.doc', '[...]
TABLE LOCK table `vw`.`files` trx id 162269408 lock mode IX

因此,那里有一些繁重的全文本索引操作doing SYNC index停止对ANY表的所有后续更新。

从日志中看,它的undo log entries数量似乎以doing SYNC index〜150 / s的速度前进,直到达到20,000,此时操作已完成。

该特定表的FTS大小令人印象深刻:

# du -c FTS_000000000000224a_00000000000036b9_*
614404  FTS_000000000000224a_00000000000036b9_INDEX_1.ibd
2478084 FTS_000000000000224a_00000000000036b9_INDEX_2.ibd
1576964 FTS_000000000000224a_00000000000036b9_INDEX_3.ibd
1630212 FTS_000000000000224a_00000000000036b9_INDEX_4.ibd
1978372 FTS_000000000000224a_00000000000036b9_INDEX_5.ibd
1159172 FTS_000000000000224a_00000000000036b9_INDEX_6.ibd
9437208 total

尽管此问题也由FTS数据量明显较小的表触发,如下所示:

# du -c FTS_0000000000002467_0000000000003a21_INDEX*
49156   FTS_0000000000002467_0000000000003a21_INDEX_1.ibd
225284  FTS_0000000000002467_0000000000003a21_INDEX_2.ibd
147460  FTS_0000000000002467_0000000000003a21_INDEX_3.ibd
135172  FTS_0000000000002467_0000000000003a21_INDEX_4.ibd
155652  FTS_0000000000002467_0000000000003a21_INDEX_5.ibd
106500  FTS_0000000000002467_0000000000003a21_INDEX_6.ibd
819224  total

在这些情况下,停顿的时间也大致相同。我在bugs.mysql.com上打开了一个错误,以便开发人员可以对此进行调查。

摊位的性质首先使我怀疑是日志刷新活动的罪魁祸首,Percona上有关MySQL 5.5日志刷新性能问题的文章描述了非常相似的症状,但是进一步的事件表明,对数据库中的单个MyISAM表执行INSERT操作也受停顿的影响,因此这似乎不是仅InnoDB的问题。

然而,我决定跟踪的值Log sequence numberPages flushed up to“LOG”的部分输出SHOW ENGINE INNODB STATUS每隔10秒。实际上,由于两个值之间的差异正在减小,因此似乎在停滞期间正在进行冲洗活动:

Mon Sep 1 14:17:08 CEST 2014 LSN: 263992263703, Pages flushed: 263973405075, Difference: 18416 K
Mon Sep 1 14:17:19 CEST 2014 LSN: 263992826715, Pages flushed: 263973811282, Difference: 18569 K
Mon Sep 1 14:17:29 CEST 2014 LSN: 263993160647, Pages flushed: 263974544320, Difference: 18180 K
Mon Sep 1 14:17:39 CEST 2014 LSN: 263993539171, Pages flushed: 263974784191, Difference: 18315 K
Mon Sep 1 14:17:49 CEST 2014 LSN: 263993785507, Pages flushed: 263975990474, Difference: 17377 K
Mon Sep 1 14:17:59 CEST 2014 LSN: 263994298172, Pages flushed: 263976855227, Difference: 17034 K
Mon Sep 1 14:18:09 CEST 2014 LSN: 263994670794, Pages flushed: 263978062309, Difference: 16219 K
Mon Sep 1 14:18:19 CEST 2014 LSN: 263995014722, Pages flushed: 263983319652, Difference: 11420 K
Mon Sep 1 14:18:30 CEST 2014 LSN: 263995404674, Pages flushed: 263986138726, Difference: 9048 K
Mon Sep 1 14:18:40 CEST 2014 LSN: 263995718244, Pages flushed: 263988558036, Difference: 6992 K
Mon Sep 1 14:18:50 CEST 2014 LSN: 263996129424, Pages flushed: 263988808179, Difference: 7149 K
Mon Sep 1 14:19:00 CEST 2014 LSN: 263996517064, Pages flushed: 263992009344, Difference: 4402 K
Mon Sep 1 14:19:11 CEST 2014 LSN: 263996979188, Pages flushed: 263993364509, Difference: 3529 K
Mon Sep 1 14:19:21 CEST 2014 LSN: 263998880477, Pages flushed: 263993558842, Difference: 5196 K
Mon Sep 1 14:19:31 CEST 2014 LSN: 264001013381, Pages flushed: 263993568285, Difference: 7270 K
Mon Sep 1 14:19:41 CEST 2014 LSN: 264001933489, Pages flushed: 263993578961, Difference: 8158 K
Mon Sep 1 14:19:51 CEST 2014 LSN: 264004225438, Pages flushed: 263993585459, Difference: 10390 K

并且在14:19:11传播已达到最低点,因此潮红活动似乎在这里停止了,恰好与摊位的结束相吻合。但是这些要点使我不认为InnoDB日志刷新是原因:

  • 为了使刷新操作能够阻止对数据库的所有更新,它需要“同步”,这意味着必须占用7/8的日志空间
  • 它会在innodb_max_dirty_pages_pct填充级别开始之前执行“异步”冲洗阶段-我没有看到
  • 即使在停顿期间,LSN仍保持增加,因此日志活动并未完全停止
  • MyISAM表插入也受到影响
  • 用于自适应刷新的page_cleaner线程似乎可以完成其工作并刷新日志,而不会导致DML查询停止:

LSN-页面刷新

(数字([Log Sequence Number] - [Pages flushed up to]) / 1024来自SHOW ENGINE INNODB STATUS

通过设置innodb_adaptive_flushing_lwm=1,强制页面清理器比以前做更多的工作似乎可以缓解此问题。

error.log没有入口与档位相符。SHOW INNODB STATUS大约运行24小时后的摘要如下:

SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 789330
OS WAIT ARRAY INFO: signal count 1424848
Mutex spin waits 269678, rounds 3114657, OS waits 65965
RW-shared spins 941620, rounds 20437223, OS waits 442474
RW-excl spins 451007, rounds 13254440, OS waits 215151
Spin rounds per wait: 11.55 mutex, 21.70 RW-shared, 29.39 RW-excl
------------------------
LATEST DETECTED DEADLOCK
------------------------
2014-09-03 10:33:55 7fe0e2e44700
[...]
--------
FILE I/O
--------
[...]
932635 OS file reads, 2117126 OS file writes, 1193633 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 17.00 writes/s, 1.20 fsyncs/s
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Main thread process no. 54745, id 140604272338688, state: sleeping
Number of rows inserted 528904, updated 1596758, deleted 99860, read 3325217158
5.40 inserts/s, 10.40 updates/s, 0.00 deletes/s, 122969.21 reads/s

因此,是的,数据库确实有死锁,但死锁很少发生(“最新”已在读取统计信息之前的11小时左右被处理)。

我尝试在一段时间内跟踪“ SEMAPHORES”部分的值,特别是在正常运行情况下和停顿期间(我编写了一个小脚本来检查MySQL服务器的进程列表,并在日志输出中运行几个诊断命令,以防万一明显的失速)。由于数字是在不同的时间范围内获取的,因此我将结果归一化为事件/秒:

                          normal   stall
                          1h avg  1m avg
OS WAIT ARRAY INFO: 
    reservation count      5,74    1,00
    signal count          24,43    3,17
Mutex spin waits           1,32    5,67
    rounds                 8,33   25,85
    OS waits               0,16    0,43
RW-shared spins            9,52    0,76
    rounds               140,73    13,39
    OS waits               2,60    0,27
RW-excl spins              6,36    1,08
    rounds               178,42   16,51
    OS waits               2,38    0,20

我不太确定自己在这里看到的内容。大多数数字下降了一个数量级-可能是由于停止了更新操作,“ Mutex旋转等待”和“ Mutex旋转回合”均增加了4倍。

对此进行进一步调查,互斥锁(SHOW ENGINE INNODB MUTEX)列表在正常操作以及停顿期间均列出了约480个互斥锁条目。我已经启用innodb_status_output_locks了它是否可以给我更多细节。

配置变量

(我对其中大多数进行了修补,但未获得一定的成功):

mysql> show global variables where variable_name like 'innodb_adaptive_flush%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| innodb_adaptive_flushing     | ON    |
| innodb_adaptive_flushing_lwm | 1     |
+------------------------------+-------+
mysql> show global variables where variable_name like 'innodb_max_dirty_pages_pct%';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_max_dirty_pages_pct     | 50    |
| innodb_max_dirty_pages_pct_lwm | 10    |
+--------------------------------+-------+
mysql> show global variables where variable_name like 'innodb_log_%';
+-----------------------------+-----------+
| Variable_name               | Value     |
+-----------------------------+-----------+
| innodb_log_buffer_size      | 8388608   |
| innodb_log_compressed_pages | ON        |
| innodb_log_file_size        | 268435456 |
| innodb_log_files_in_group   | 2         |
| innodb_log_group_home_dir   | ./        |
+-----------------------------+-----------+
mysql> show global variables where variable_name like 'innodb_double%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| innodb_doublewrite | ON    |
+--------------------+-------+
mysql> show global variables where variable_name like 'innodb_buffer_pool%';
+-------------------------------------+----------------+
| Variable_name                       | Value          |
+-------------------------------------+----------------+
| innodb_buffer_pool_dump_at_shutdown | OFF            |
| innodb_buffer_pool_dump_now         | OFF            |
| innodb_buffer_pool_filename         | ib_buffer_pool |
| innodb_buffer_pool_instances        | 8              |
| innodb_buffer_pool_load_abort       | OFF            |
| innodb_buffer_pool_load_at_startup  | OFF            |
| innodb_buffer_pool_load_now         | OFF            |
| innodb_buffer_pool_size             | 29360128000    |
+-------------------------------------+----------------+
mysql> show global variables where variable_name like 'innodb_io_capacity%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_io_capacity     | 200   |
| innodb_io_capacity_max | 2000  |
+------------------------+-------+
mysql> show global variables where variable_name like 'innodb_lru_scan_depth%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_lru_scan_depth | 1024  |
+-----------------------+-------+

事情已经尝试过

  • 通过以下方式禁用查询缓存 SET GLOBAL query_cache_size=0
  • 增加到innodb_log_buffer_size128M
  • 和一起玩innodb_adaptive_flushinginnodb_max_dirty_pages_pct以及各自的_lwm值(在我更改之前,它们已设置为默认值)
  • 增加innodb_io_capacity(2000)和innodb_io_capacity_max(4000)
  • 设置 innodb_flush_log_at_trx_commit = 2
  • 与innodb_flush_method = O_DIRECT一起运行(是的,我们确实使用具有持久性写缓存的SAN)
  • 将/ sys / block / sda / queue / scheduler设置为noopdeadline

innodb_io_capacity,innodb_io_capacity_max和innodb_lru_scan_depth的值是多少?将它们设置为更高(更合适)的值有助于保持日志空间可用。
Morgan Tocker 2014年

默认值-200、2000和1024。我现在将它们更改为2000、4000和2000,并且LSN和Pages Flushed值之间的差异再次减小至<1,000K。但是我不确定这是否是对数问题首先是空间。
syneticon-dj

确实似乎并非如此。我仍然看到失速-它们的持续时间或发生频率没有太大变化。我的LSN /检查点日志记录显示了绝对较低的绝对价差数,该值在停顿过程中在1-2分钟内增加到大约3 M(可能是未完成的事务导致日志无法使用),随后在LSN之间成功刷新至接近零的价差从停顿已解决的时间点开始的检查点。
syneticon-dj

我不确定您是否应该将innodb_adaptive_flushing_lwm设置为1-这是自适应刷新启动的日志空间的百分比(默认值:10)。
Morgan Tocker 2014年

@MorganTocker我已设置此选项以确保自适应刷新在大多数情况下都会刷新任何内容,因为我怀疑日志空间利用率是问题的一部分。默认值也为10,我出于故障排除的目的将其更改。
syneticon-dj

Answers:


6

我们在Windows上运行的版本为5.6.12和5.6.16的两台服务器上看到了相同的问题,每个服务器上都有一对从属服务器。像您一样,我们被困了近两个月。

解决方案

set global binlog_order_commits = 0;

有关变量的详细信息,请参见 https://dev.mysql.com/doc/refman/5.6/en/replication-options-binary-log.html#sysvar_binlog_order_commits

说明

InnoDB全文使用缓存(默认大小为8M),其中包含需要应用于磁盘上实际全文索引的更改。

缓存填满后,将创建几个事务来执行合并缓存中包含的数据的工作-这往往是大量的随机IO,因此除非可以将整个全文索引加载到其中缓冲池,这是一个漫长而缓慢的事务。

在binlog_order_commits设置为true的情况下,所有包含插入和更新的事务(在长时间运行的fts_sync_index事务之后开始)必须等待其完成才可以提交。

如果启用了二进制日志记录,这只是一个问题


这看起来也很像是我所看到的问题的解决方案。您是如何提出解决方法的?另外,在我的情况下,全文索引适合缓冲池(大小约为30G),但操作似乎受延迟的限制很大。我的印象是,MySQL的I / O堆栈在处理存储延迟时效率极低,因此,这个问题可能是两者的结合-低效率以及二进制日志记录配置的默认错误。
syneticon-dj

我确实想知道,这么长的时间怎么会不为人知。当然,有更多的人在非SSD存储上运行启用了FTS和binlog的InnoDB吗?
syneticon-dj

运气。我到了与您相同的地步,在锁定期间我设法捕获了“ show engine innodb status”。我编写了一个小程序,该程序将在具有FTS索引的表中插入很多行,而另一个程序则更新了第二个表并记录了更新时间。我无法让FTS缓存刷新暂停来阻止更新一段时间,直到我经历了本地计算机和实时服务器之间的设置差异(一个接一个)。打开binlog会重新产生问题,因此我只阅读了binlog选项。
Daniel Golding

1
值得注意的是,MySQL开发团队最终(在等待15个月之后!)将报告的错误状态设置为“已验证”,并且至少开发团队中的某个人似乎正在考虑解决方案。不用说,我已经完成了MySQL。我希望永远是好的。
syneticon-dj

4

让我尝试描述日志刷新的历史性问题以及自适应刷新的工作方式:

  • 重做日志是环形缓冲区设计。它们仅被写入(在正常操作中从不读取)并提供崩溃恢复。我喜欢将环形缓冲器描述为类似于坦克的胎面。

  • 如果InnoDB包含尚未在磁盘上修改的更改,则它将无法覆盖日志文件空间。从历史上看,发生的事情是InnoDB每秒将尝试一定量的工作(由配置innodb_io_capacity),如果这还不够,您将达到完整的日志空间。由于需要同步冲洗才能突然释放空间,因此会发生停顿,这通常会使后台任务突然成为前台。

  • 为了解决这个问题,引入了自适应冲洗。在消耗10%(默认)日志空间的情况下,后台工作开始变得越来越激进。这样做的目的不是突然停顿,而是在性能上有一个“短暂的下降”。

  • 独立自适应潮红,它为您的工作负载提供足够的日志空间是很重要的(innodb_log_file_size4G的值现在是相当安全),并确保innodb_io_capacityinnodb_lru_scan_depth被设置为实际值。自适应冲洗10%innodb_adaptive_flushing_lwm不会让您感到很遥远,它更像是一种针对空间不足的防御机制。


2

只是为了给InnoDB带来一些争用缓解,您可以使用innodb_purge_threads

在MySQL 5.6之前,主线程会进行所有页面刷新。在MySQL 5.6中,一个单独的线程可以处理它。innodb_purge_threads在MySQL 5.5中,默认值为0,最大值为1。在MySQL 5.6中,默认值为1,最大值为32。

设置innodb_purge_threads实际上有什么作用?

非零值在一个或多个后台线程中运行清除操作,这可以减少InnoDB中的内部争用,从而提高可伸缩性。将该值增加到大于1会创建许多单独的清除线程,这可以提高在多个表上执行DML操作的系统上的效率。

我将从将innodb_purge_threads设置为4开始,看看是否减少了InnoDB页面刷新。

更新2014-09-02 12:33 EDT

Morgan Tocker在下面的评论中指出,页面清理器是受害者,MySQL 5.7可以解决该问题。尽管如此,您的情况还是在MySQL 5.6中。

我再次看了一下,发现您的innodb_max_dirty_pages_pct为50。

在MySQL 5.5及更高版本中,innodb_max_dirty_pages_pct的默认值为75。降低该值会增加刷新引起的停顿的可能性。我会做三(3)件事

更新2014-09-03 11:06 EDT

您可能需要更改冲洗行为

尝试动态设置以下内容

SET GLOBAL flush = 1;
SET GLOBAL flush_time = 10;

这些变量flushflush_time,将每隔10秒关闭表上打开的文件句柄,从而使刷新更具攻击性。MyISAM绝对可以从中受益,因为它不缓存数据。对MyISAM表的所有写操作都需要完整的表锁,然后是原子写操作,并且依赖于OS进行磁盘更改。

以这种方式刷新InnoDB将需要重新启动mysql。要查看的选项是innodb_flush_log_at_trx_commitinnodb_flush_method

重新启动之前,请添加这些

[mysqld]
flush = 1
flush_time = 10
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = O_DIRECT

在执行此方法之前,您应该检查日记是否存在问题。我看到O_DIRECT上这个很酷的mysqlperformanceblog帖子由于内核被伪造了。同一篇文章还提到MyISAM受感染。

我之前写过关于此帖子的文章:innodb_flush_method = O_DSYNC时,使用O_SYNC打开了ib_logfile

试试看 !!!


1
需要澄清的是:我认为此工作负荷会给页面清理程序线程(而不是清除线程)带来压力。多个页面清除程序是5.7的功能,但在5.6中仍然可以进行进一步的配置。参见:mysqlserverteam.com/mysql-5-7-improves-dml-directional-workloads
Morgan Tocker 2014年

@MorganTocker @RolandoMySQLDBA在sar -d输出中对我突出的一件事是,await在其中一个停顿期间,吞吐量几乎增长了十倍。您是否认为这里可能存在MySQL以外的问题,例如I / O调度程序或文件系统日志记录?
詹姆斯L

我正在更改您建议的大多数参数,但innodb_purge_threads(需要重新启动)除外。它对这个问题没有太大的作用。我被认为InnoDB引擎不是这里的问题,因为MyISAM表插入也停滞了。
syneticon-dj 2014年

请发布您的innodb_read_io_threads和innodb_write_io_threads设置。运行SHOW GLOBAL VARIABLES LIKE '%io_threads';
RolandoMySQLDBA 2014年

1
@ syneticon-dj如何从MySQL外部写入同一文件系统-那些文件也停滞了吗?
詹姆斯L
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.