База даних MySQL InnoDB 'висить' на вибір


10

Я намагаюся виправити конфігурацію MySQL на нашому сервері. Специфіка нашого додатка полягає в тому, що в одній таблиці зберігається багато даних (на даний момент понад 300 мільйонів рядків). Ця таблиця використовується часто для вставок (вони постійно надходять).

Коли я запускаю запит вибору на цій таблиці, який займає більше декількох секунд, тоді всі вставки (точно виконуються) очікують на доступ до таблиці і робить наш додаток невідповідним.

Наскільки я знаю, InnoDB не робить жодних блокувань на столі, коли вибір працює. Чому тоді блокує таблицю блокування вибору?

Я намагався знайти причину у inatop, але я не впевнений, як інтерпретувати його вихід і де шукати. Скажіть мені, що вам потрібно, і я опублікую це тут.

+-----+---------+-----------+--------+---------+------+----------------+-----------------------------------------------------------------------------------------------------------------------------------+
| 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? Мені взагалі не потрібні транзакції за цією таблицею. Чи не буде таких замків у подібній ситуації (довгий вибір + багато швидких вставок)?

EDIT2:

Ось як виглядають вставні запити:

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      |         | 
+-------------+------------+----------------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+

EDIT 3:

Чому під час таких запитів моя заявка не відповідає? Чи не повинно це впливати лише на той "великий_таблиця"?

Можливо, щось не так з моїм конфігурацією mysql? Сервер - це 4-ядерний Xeon 2GHz з 16 Гб оперативної пам’яті. Він запускає 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;.
кванта

У Initotop ви можете натиснути L, щоб отримати вигляд замків. Як ваш додаток встановлює зв’язок? JDBC? Який рівень за замовчуваннямTransactionIsolation ви використовуєте?
HTTP500

Це додаток RoR, тому я думаю, що це за замовчуванням для MySQL (чи можу я якось перевірити його за допомогою SQL запиту?) Під час запуску цього вибору insinotop не показує жодних блокувань.
kaczor1984

@ kaczor1984 Ви можете перевірити рівень ізоляції за замовчуванням, виконавши: покажіть змінні типу "tx_isolation"; запит. За замовчуванням - ПОВТОРЕННЕ ЧИТАТИ Зауважте, що MVCC працює лише з ПОВТОРЕННОЮ ПРОЧИТАНОЮ та ПРОЧИТАНОЮ ЗВЕРНЕНОЮ. Не впевнений, що вирішує вашу проблему, але відповідь RolandoMySQLDBA була інформативною.
HTTP500

Оновлено мою відповідь аналізом запиту на Process ID 40
RolandoMySQLDBA,

Відповіді:


15

Будь ласка, уважно подивіться на список процесів та "показ двигуна innodb статус". Що ти бачиш ???

Ідентифікатори процесу 1,2,4,5,6,13 намагаються запустити COMMIT.

Хто все тримає ??? Ідентифікатор процесу 40 виконує запит проти великої таблиці.

Ідентифікатор процесу 40 працює 33 секунди. Ідентифікатори процесу 1,2,4,5,6,13 тривали менше 33 секунд. Ідентифікатор процесу 40 щось обробляє. Що затримується ???

Перш за все, запит лунає по кластерному індексу Large_table через MVCC .

У межах ідентифікаторів процесу 1,2,4,5,6,13 - це рядки, у яких є дані MVCC, що захищають його ізоляцію транзакцій. Ідентифікатор процесу 40 має запит, який проходить через рядки даних. Якщо в полі hotspot_id є індекс, цей ключ + ключ до фактичного рядка з кластерного індексу повинен виконувати внутрішнє блокування. (Примітка. За задумом, всі не унікальні індекси в InnoDB містять як ваш ключ (стовпець, який ви мали на меті індексувати), так і кластерний індексний ключ). Цей унікальний сценарій, по суті, "Невідмінна сила" відповідає Нерухомим Об'єктом.

По суті, COMMIT повинні чекати, поки можна безпечно застосувати зміни до big_table. Ваша ситуація не є унікальною, не разовою, не рідкісним явищем.

Насправді я відповів на три подібні запитання в DBA StackExchange. Запитання були подані тією ж особою, що стосуються однієї і тієї ж проблеми. Мої відповіді не були рішенням, але допомогли запитувачу зробити власний висновок, як впоратися зі своєю ситуацією.

На додаток до цих відповідей, я відповів на питання іншої людини про тупикові місця в InnoDB щодо SELECT .

Я сподіваюся, що мої минулі пости на цю тему допоможуть з’ясувати, що з вами відбувалося.

ОНОВЛЕННЯ 2011-08-25 08:10 EDT

Ось запит від ідентифікатора процесу 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);

Підзапит big_table_keys збирає необхідні 1000 ключів. Потім результат підзапиту INNER JOIN приєднується до big_table. Поки ключі витягуються замість цілих рядів. Це ще 799 000 рядків для читання. Є кращий спосіб отримати ці ключі, який веде нас до ...

ОСНОВНА Зміна №2: Створення індексів, які підтримують повторно налаштований запит

Оскільки рефакторований запит містить лише один підзапит, вам потрібно зробити лише один індекс. Ось цей індекс:

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

Чому саме цей показник? Подивіться на пункт WHERE. Hotspot_id - це статичне значення. Це змушує всі точки hotspot_ids формувати послідовний список в індексі. Тепер подивіться на пункт ЗАМОВИТИ ПО. Стовпець Discover_at - це, ймовірно, DATETIME або TIMESTAMP.

Природний порядок, представлений в індексі, такий:

  • Індекс містить список hostpot_ids
  • Кожен hotspot_id має упорядкований список полів виявлених_ат

Створення цього індексу також виключає внутрішнє сортування тимчасових таблиць.

Будь ласка, поставте ці дві основні зміни на місце, і ви побачите різницю в часі роботи.

Спробувати !!!

ОНОВЛЕННЯ 2011-08-25 08:15 EDT

Я переглянув ваші індекси. Ще потрібно створити індекс, який я запропонував.


Дякую за величезне пояснення, як це працює. Боюся, я не можу зрозуміти, як уникнути подібних ситуацій. Вкладиші повинні змінювати індекси та для вибору потрібно використовувати індекс на hotspot_id та Discover_at. Буду радий, якщо ви також можете прихилитись до моєї «ідеї» про перехід на MyISAM.
kaczor1984

Насправді, використання MyISAM може погіршити ситуацію, оскільки кожен INSERT, UPDATE та DELETE у MyISAM запускає блокування повного столу. Навіть якщо ви використовуєте LOW_PRIORITY INSERTs або INSERT DELAYED, все-таки запити стовпців все ще будуть виникати. Ви повинні вивчити сам запит, оскільки незалежно від двигуна зберігання запити можуть бути налаштовані навколо таких перешкод. Як мінімум, може знадобитися взагалі новий алгоритм. Я перегляну запит через пару хвилин ...
RolandoMySQLDBA

Я оновив свою першу публікацію, щоб ви могли бачити вставні запити та індекси в цю таблицю.
kaczor1984

Оновлено мою відповідь аналізом запиту на Process ID 40
RolandoMySQLDBA,

4
ви, сер, герой серед людей за ваші довгі відповіді mysql
Майк,

3

Вирішено!

Основною проблемою був query_cache. http://bugs.mysql.com/bug.php?id=21074

Після його відключення «заморозки» зникли.


А також велике спасибі @RolandoMySQLDBA за підказки щодо оптимізації моїх запитів та індексів.
kaczor1984
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.