MySQL InnoDB数据库在选择时“挂起”


10

我正在尝试在我们的服务器上修复MySQL配置。我们的应用程序的特点是很多数据存储在单个表中(当前超过3亿行)。该表经常用于插入(它们总是出现)。

当我在该表上花费超过几秒钟的时间运行选择查询时,所有插入(精确地提交)都在等待表访问,并使我们的应用程序无响应。

据我所知,当select运行时,InnoDB不会对表进行任何锁定。为什么选择阻止表呢?

我试图找到使用innotop的原因,但是我不确定如何解释它的输出以及在哪里进行搜索。告诉我您需要什么,我会在这里发布。

+-----+---------+-----------+--------+---------+------+----------------+-----------------------------------------------------------------------------------------------------------------------------------+
| Id  | User    | Host      | db     | Command | Time | State          | Info                                                                                                                              |
+-----+---------+-----------+--------+---------+------+----------------+-----------------------------------------------------------------------------------------------------------------------------------+
|   1 | root    | localhost | dbname | Query   |   29 | NULL           | COMMIT                                                                                                                            | 
|   2 | root    | localhost | dbname | Query   |   30 | NULL           | COMMIT                                                                                                                            | 
|   4 | root    | localhost | dbname | Query   |   29 | NULL           | COMMIT                                                                                                                            | 
|   5 | root    | localhost | dbname | Query   |   29 | NULL           | COMMIT                                                                                                                            | 
|   6 | root    | localhost | dbname | Query   |   25 | NULL           | COMMIT                                                                                                                            | 
|   7 | root    | localhost | dbname | Query   |    0 | NULL           | show full processlist                                                                                                             | 
|  13 | user    | localhost | dbname | Query   |   25 | NULL           | COMMIT                                                                                                                            | 
|  38 | user    | localhost | dbname | Sleep   |    0 |                | NULL                                                                                                                              | 
|  39 | user    | localhost | dbname | Sleep   | 9017 |                | NULL                                                                                                                              | 
|  40 | user    | localhost | dbname | Query   |   33 | Sorting result | SELECT * FROM `large_table` WHERE (`large_table`.`hotspot_id` = 3000064)  ORDER BY discovered_at LIMIT 799000, 1000 | 
|  60 | user    | localhost | dbname | Sleep   | 1033 |                | NULL                                                                                                                              | 
|  83 | root    | localhost | dbname | Sleep   | 3728 |                | NULL                                                                                                                              | 
| 112 | root    | localhost | NULL   | Sleep   |    6 |                | NULL                                                                                                                              | 
+-----+---------+-----------+--------+---------+------+----------------+-----------------------------------------------------------------------------------------------------------------------------------+


=====================================
110824 12:24:24 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 19 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 1521117, signal count 1471216
Mutex spin waits 0, rounds 20647617, OS waits 239914
RW-shared spins 2119697, OS waits 1037149; RW-excl spins 505734, OS waits 218177
------------
TRANSACTIONS
------------
Trx id counter 0 412917332
Purge done for trx's n:o < 0 412917135 undo n:o < 0 0
History list length 48
Total number of lock structs in row lock hash table 5
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, process no 28363, OS thread id 1092766032
MySQL thread id 83, query id 3249941 localhost root
---TRANSACTION 0 412901582, not started, process no 28363, OS thread id 1144449360
MySQL thread id 60, query id 3677008 localhost user
---TRANSACTION 0 412917189, not started, process no 28363, OS thread id 1144314192
MySQL thread id 43, query id 3905773 localhost root
---TRANSACTION 0 412534255, not started, process no 28363, OS thread id 1092630864
MySQL thread id 39, query id 14279 localhost user
---TRANSACTION 0 412917331, not started, process no 28363, OS thread id 1144179024
MySQL thread id 38, query id 3908045 localhost user
---TRANSACTION 0 412917201, not started, process no 28363, OS thread id 1092495696
MySQL thread id 13, query id 3908257 localhost user
---TRANSACTION 0 412538821, not started, process no 28363, OS thread id 1092360528
MySQL thread id 7, query id 3908258 localhost root
show engine innodb status
---TRANSACTION 0 412917330, ACTIVE 6 sec, process no 28363, OS thread id 1144043856
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 2, query id 3907373 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917331, sees < 0 412917131
---TRANSACTION 0 412917328, ACTIVE 6 sec, process no 28363, OS thread id 1092225360
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 6, query id 3907345 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917329, sees < 0 412917131
---TRANSACTION 0 412917326, ACTIVE 6 sec, process no 28363, OS thread id 1091955024
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 4, query id 3907335 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917327, sees < 0 412917131
---TRANSACTION 0 412917324, ACTIVE 6 sec, process no 28363, OS thread id 1092090192
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 5, query id 3907328 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917325, sees < 0 412917131
---TRANSACTION 0 412917321, ACTIVE (PREPARED) 7 sec, process no 28363, OS thread id 1143908688 preparing
2 lock struct(s), heap size 368, undo log entries 1
MySQL thread id 1, query id 3907125 localhost root
COMMIT
Trx read view will not see trx with id >= 0 412917322, sees < 0 412917131
---TRANSACTION 0 412917131, ACTIVE 20 sec, process no 28363, OS thread id 1074075984, thread declared inside InnoDB 111
mysql tables in use 1, locked 0
MySQL thread id 40, query id 3904958 localhost user Sorting result
SELECT * FROM `large_table` WHERE (`large_table`.`hotspot_id` = 3000064)  ORDER BY discovered_at LIMIT 848000, 1000
Trx read view will not see trx with id >= 0 412917132, sees < 0 412917132
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 1; buffer pool: 0
3510225 OS file reads, 284998 OS file writes, 202897 OS fsyncs
1.05 reads/s, 21299 avg bytes/read, 8.10 writes/s, 7.58 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 275, free list len 13392, seg size 13668,
489950 inserts, 491830 merged recs, 10986 merges
Hash table size 8850487, used cells 8127172, node heap has 32697 buffer(s)
71914.53 hash searches/s, 8701.91 non-hash searches/s
---
LOG
---
Log sequence number 157 3331524445
Log flushed up to   157 3331521939
Last checkpoint at  157 3326072846
1 pending log writes, 0 pending chkp writes
199025 log i/o's done, 7.53 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 4788954432; in additional pool allocated 1048576
Buffer pool size   262144
Free buffers       0
Database pages     229447
Modified db pages  1439
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 7453325, created 14887, written 118658
1.37 reads/s, 0.11 creates/s, 0.53 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
1 queries inside InnoDB, 0 queries in queue
7 read views open inside InnoDB
Main thread process no. 28363, id 1091684688, state: flushing log
Number of rows inserted 1093064, updated 249134, deleted 1405, read 1115880534
7.89 inserts/s, 2.47 updates/s, 0.05 deletes/s, 80953.21 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

编辑:

感谢您澄清这一点。

因此,我现在必须将问题分为两种情况。

  1. 单个表上的锁定导致我的整个应用程序“挂起”是否正常?DB是否应该响应其他表的查询?也许某些缓冲区设置得太低?

  2. 将此表切换到MyISAM是否有帮助?我根本不需要这张桌子上的交易。这样的情况下不会有其他锁定(长选择+许多快速插入)吗?

编辑2:

这就是插入查询的样子:

INSERT INTO `large_table` (`device_address`, `hotspot_id`, `minute`, `created_at`, `updated_at`, `discovered_with_hci`, `hour`, `rssi`, `day`, `device_class`, `discovered_at`) VALUES('10:40:03:90:10:40', 3000008, 1, '2011-08-22 05:01:08', '2011-08-22 05:01:08', -1, 5, -79, '2011-08-22 05:01:01', '0', '2011-08-22 05:01:01')

那就是在其上定义的索引:

+-------------+------------+----------------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table       | Non_unique | Key_name                                     | Seq_in_index | Column_name         | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------------+------------+----------------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+
| large_table |          0 | PRIMARY                                      |            1 | id                  | A         |    92396334 |     NULL | NULL   |      | BTREE      |         | 
| large_table |          1 | index_large_table_on_discovered_with_hci     |            1 | discovered_with_hci | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_hotspot_id              |            1 | hotspot_id          | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_day_and_hour_and_minute |            1 | day                 | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_day_and_hour_and_minute |            2 | hour                | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_day_and_hour_and_minute |            3 | minute              | A         |      537187 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_created_at              |            1 | created_at          | A         |     8399666 |     NULL | NULL   | YES  | BTREE      |         | 
| large_table |          1 | index_large_table_on_rssi                    |            1 | rssi                | A         |          18 |     NULL | NULL   | YES  | BTREE      |         | 
+-------------+------------+----------------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+

编辑3:

为什么在整个查询过程中我的应用程序都没有响应?它不应该只影响那个“大表”吗?

也许我的mysql配置有问题?服务器是具有16GB RAM的4核Xeon 2GHz。它运行MySQL + Rails App

我的配置参数:

skip-external-locking
key_buffer              = 64M
max_allowed_packet      = 16M
thread_stack            = 128K
thread_cache_size       = 8
query_cache_size        = 32M
tmp_table_size          = 64M
max_heap_table_size     = 64M
table_cache             = 256
read_rnd_buffer_size    = 512K
sort_buffer_size        = 2M

myisam-recover          = BACKUP
max_connections         = 200

query_cache_limit       = 1M

long_query_time = 200

max_binlog_size         = 100M

innodb_buffer_pool_size = 4G
safe-updates
max_join_size=100000000

Mysqltuner脚本仅建议:

long_query_time (<= 10)
innodb_buffer_pool_size (>= 62G)

请附加的输出show engine innodb status;
量子

在innotop中,您可以按L来查看锁。您的应用如何建立连接?JDBC?您正在使用哪个defaultTransactionIsolation级别?
HTTP500

这是一个RoR应用程序,因此我想它是MySQL的默认设置(我可以通过SQL查询以某种方式对其进行检查吗?)。选择运行时,innotop不显示任何锁定。
kaczor1984

@ kaczor1984您可以通过执行以下操作来检查默认的隔离级别:show'tx_isolation'之类的变量;查询。默认值为可重复读取。请注意,MVCC仅与REPEATABLE READ和READ COMMITTED一起使用。不确定您的问题的解决方案是什么,但是RolandoMySQLDBA的回答很有帮助。
HTTP500

更新我的回答与进程ID 40的查询分析
RolandoMySQLDBA

Answers:


15

请仔细查看进程列表和“显示引擎的innodb状态”。你看到了什么 ???

进程ID 1,2,4,5,6,13都试图运行COMMIT。

谁在阻止一切???进程ID 40正在对large_table运行查询。

进程ID 40已运行33秒。进程ID 1,2,4,5,6,13的运行时间少于33秒。进程ID 40正在处理某些内容。有什么阻碍???

首先,查询通过MVCC敲击large_table的聚集索引

在进程ID 1,2,4,5,6,13中是具有MVCC数据保护其事务隔离的行。进程ID 40具有正在遍历数据行的查询。如果字段hotspot_id上有索引,则该键+聚簇索引中指向实际行的键必须执行内部锁定。(注意:根据设计,InnoDB中的所有非唯一索引都同时包含您的键(您要索引的列)和聚集的索引键)。这种独特的场景本质上是“不可阻挡的力量遇到不可移动的物体”。

本质上,COMMIT必须等待,直到可以安全地对large_table进行更改为止。您的情况不是唯一的,不是一次性的,不是罕见的现象。

实际上,我在DBA StackExchange中回答了三个这样的问题。这些问题是由与同一问题相关的同一个人提交的。我的答案不是解决方案,而是帮助问题提交者就如何处理自己的情况得出自己的结论。

除了这些答案之外,我还回答了另一个人有关InnoDB中与SELECTs有关的死锁的问题

希望我以前在该主题上发表的文章有助于弄清您的情况。

更新2011-08-25 08:10 EDT

这是来自进程ID 40的查询

SELECT * FROM `large_table`
WHERE (`large_table`.`hotspot_id` = 3000064)
ORDER BY discovered_at LIMIT 799000, 1000;

两个观察:

  • 您正在执行“ SELECT *”,是否需要获取每一列?如果只需要特定的列,则应为它们加上标签,因为1000行的临时表可能比实际需要的大。

  • WHERE和ORDER BY子句通常会遗漏性能问题或使表设计脱颖而出。您需要创建一种机制,该机制将在收集数据之前加快密钥收集的速度。

根据这两个观察,您必须进行两个主要更改:

主要变更#1:重构查询

重新设计查询,以便

  1. 密钥是从索引中收集的
  2. 只有1000或它们被收集
  3. 加入主表

这是执行这三件事的新查询

SELECT large_table.* FROM
large_table INNER JOIN
(
    SELECT hotspot_id,discovered_at
    FROM large_table
    WHERE hotspot_id = 3000064
    ORDER BY discovered_at
    LIMIT 799000,1000
) large_table_keys
USING (hotspot_id,discovered_at);

子查询large_table_keys收集您需要的1000个键。然后,将子查询的结果INNER JOINed到large_table。到目前为止,检索的是键而不是整个行。仍然需要读取799,000行。有一种更好的方法来获取这些密钥,这使我们能够...

主要更改#2:创建支持重构查询的索引

由于重构查询仅具有一个子查询,因此您只需建立一个索引。这是该索引:

ALTER TABLE large_table ADD INDEX hotspot_discovered_ndx (hotspot_id,discovered_at);

为什么要使用这个特定索引?查看WHERE子句。hotspot_id是静态值。这使得所有hotspot_ids在索引中形成一个顺序列表。现在,查看ORDER BY子句。created_at列可能是DATETIME或TIMESTAMP字段。

它在索引中呈现的自然顺序如下:

  • 索引具有hostpot_ids列表
  • 每个hotspot_id都有一个Discovered_at字段的有序列表

进行此索引还可以避免对临时表进行内部排序。

请把这两个主要更改放在适当的位置,您会发现运行时间有所不同。

试试看 !!!

更新2011-08-25 08:15 EDT

我看了你的索引。您仍然需要创建我建议的索引。


感谢您提供大量解释。恐怕我不知道该如何避免这种情况。插入必须修改索引,而选择必须在hotspot_id和founded_at上使用索引。如果您也能解决我转向MyISAM的“想法”,我将感到很高兴。
kaczor1984

实际上,使用MyISAM可能会使情况变得更糟,因为MyISAM中的每个INSERT,UPDATE和DELETE都会触发全表锁定。即使您使用LOW_PRIORITY INSERT或INSERT DELAYED,仍然会遇到全表锁。您应该探索查询本身,因为无论存储引擎如何,都可以围绕这些障碍来调整查询。至少可能完全需要一种新算法。我将在几分钟后查看查询...
RolandoMySQLDBA 2011年

我已经更新了第一篇文章,因此您可以在此表上看到插入查询和索引。
kaczor1984

更新我的回答与进程ID 40的查询分析
RolandoMySQLDBA

4
您的先生是长期的mysql解答中的英雄之一
Mike

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.