Il database MySQL InnoDB "si blocca" sulle selezioni


10

Sto cercando di correggere la configurazione di MySQL sul nostro server. Le specifiche della nostra app sono che molti dati sono archiviati in un'unica tabella (attualmente oltre 300 milioni di righe). Questa tabella viene utilizzata spesso per gli inserti (vengono sempre).

Quando eseguo una query di selezione su quella tabella che richiede più di pochi secondi, tutti gli inserimenti (con precisione si impegna) sono in attesa dell'accesso alla tabella e rendono la nostra app non rispondente.

Per quanto ne so, InnoDB non esegue alcun blocco sul tavolo quando select è in esecuzione. Perché la tabella dei blocchi selezionati è quindi?

Ho provato a trovare un motivo con innotop ma non sono sicuro di come interpretarne l'output e dove cercare. Dimmi di cosa hai bisogno e lo posterò qui.

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

MODIFICARE:

Grazie per averlo chiarito.

Quindi ora devo dividere la mia domanda in due casi.

  1. È normale che il blocco su questa singola tabella causi il blocco dell'intera app. DB non dovrebbe rispondere ai quesiti su altre tabelle? Forse un buffer è impostato su un valore troppo basso?

  2. Il passaggio da questa tabella a MyISAM sarà di aiuto? Non ho assolutamente bisogno di transazioni su questa tabella. Non ci saranno altri blocchi in una situazione del genere (selezione lunga + molti inserimenti rapidi)?

EDIT2:

Ecco come appaiono le query di inserimento:

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')

Ecco cosa sono definiti gli indici:

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

MODIFICA 3:

Perché durante tali domande la mia domanda non risponde? Non dovrebbe interessare solo quella 'large_table'?

Forse qualcosa non va nella mia configurazione mysql? Il server è un Xeon 4G 2 core con 16 GB di RAM. Funziona con l'app MySQL + Rails

I miei parametri di configurazione:

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

Lo script di Mysqltuner suggerisce solo:

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

Aggiungi l'output di show engine innodb status;.
quanti

In innotop puoi premere L per avere una vista nei lucchetti. In che modo la tua app stabilisce una connessione? JDBC? Quale livello di DefaultTransactionIsolation stai usando?
HTTP500,

È un'applicazione RoR, quindi suppongo sia predefinita per MySQL (posso controllarlo in qualche modo con una query SQL?). innotop non mostra alcun blocco mentre questa selezione viene eseguita.
kaczor1984,

@ kaczor1984 Puoi controllare il livello di isolamento predefinito facendo un: mostra variabili come 'tx_isolation'; query. L'impostazione predefinita è REPEATABLE READ. Si noti che MVCC funziona solo con REPEATABLE READ e READ COMMITTED. Non sono sicuro di quale sia la soluzione al tuo problema, ma la risposta di RolandoMySQLDBA è stata istruttiva.
HTTP500,

Aggiornato la mia risposta con un'analisi della query al Process ID 40
RolandoMySQLDBA

Risposte:


15

Si prega di guardare attentamente la lista dei processi e lo "Mostra stato innodb del motore". Cosa vedi ???

Gli ID processo 1,2,4,5,6,13 stanno tutti cercando di eseguire COMMIT.

Chi regge tutto ??? L'ID processo 40 esegue una query su large_table.

L'ID processo 40 è in esecuzione da 33 secondi. ID processo 1,2,4,5,6,13 in esecuzione da meno di 33 secondi. L'ID processo 40 sta elaborando qualcosa. Qual è il sostegno ???

Prima di tutto, la query sta martellando sull'indice cluster di large_table tramite MVCC .

All'interno degli ID processo 1,2,4,5,6,13 sono righe con dati MVCC a protezione dell'isolamento della transazione. L'ID processo 40 ha una query che sta marciando attraverso file di dati. Se sul campo hotspot_id è presente un indice, quella chiave + la chiave della riga effettiva dall'indice cluster deve eseguire un blocco interno. (Nota: in base alla progettazione, tutti gli indici non univoci in InnoDB contengono sia la chiave (la colonna che intendevi indicizzare) + una chiave di indice cluster. Questo scenario unico è essenzialmente la forza inarrestabile incontra l'oggetto immobile.

In sostanza, i COMMIT devono attendere fino a quando è sicuro applicare le modifiche contro large_table. La tua situazione non è unica, non è una tantum, non è un fenomeno raro.

In realtà ho risposto a tre domande come questa nel DBA StackExchange. Le domande sono state presentate dalla stessa persona in relazione allo stesso problema. Le mie risposte non furono la soluzione, ma aiutarono il presentatore di domande a giungere alle proprie conclusioni su come gestire la sua situazione.

Oltre a queste risposte, ho risposto alla domanda di un'altra persona sui deadlock in InnoDB per quanto riguarda i SELECT .

Spero che i miei post precedenti su questo argomento aiutino a chiarire cosa ti stava succedendo.

AGGIORNAMENTO 2011-08-25 08:10 EDT

Ecco la query dall'ID processo 40

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

Due osservazioni:

  • Stai facendo 'SELEZIONA *' devi recuperare ogni colonna? Se hai bisogno solo di colonne specifiche, dovresti etichettarle perché la tabella temporanea di 1000 righe potrebbe essere più grande del necessario.

  • Le clausole WHERE e ORDER BY di solito generano problemi di prestazioni o fanno brillare il design del tavolo. È necessario creare un meccanismo che acceleri la raccolta delle chiavi prima della raccolta dei dati.

Alla luce di queste due osservazioni, ci sono due importanti cambiamenti che è necessario apportare:

MAJOR CHANGE # 1: refactoring della query

Riprogettare la query in modo che

  1. le chiavi vengono raccolte dall'indice
  2. solo 1000 o loro sono raccolti
  3. tornato al tavolo principale

Ecco la nuova query che fa queste tre cose

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);

La subquery large_table_keys raccoglie le 1000 chiavi necessarie. Il risultato della sottoquery viene quindi INNER JOINed in large_table. Finora, le chiavi vengono recuperate anziché intere righe. Sono ancora 799.000 righe da leggere. C'è un modo migliore per ottenere quelle chiavi, che ci porta a ...

PRINCIPALE CAMBIAMENTO # 2: creare indici che supportano la query refactored

Poiché la query refactored presenta solo una sottoquery, è necessario creare solo un indice. Ecco quell'indice:

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

Perché questo indice particolare? Guarda la clausola WHERE. Hotspot_id è un valore statico. Questo fa sì che tutti gli hotspot_id formino un elenco sequenziale nell'indice. Ora, guarda la clausola ORDER BY. La colonna discover_at è probabilmente un campo DATETIME o TIMESTAMP.

L'ordine naturale che questo presenta nell'indice è il seguente:

  • L'indice presenta un elenco di hostpot_ids
  • Ogni hotspot_id ha un elenco ordinato di campi scoperti

La creazione di questo indice elimina anche l'ordinamento interno delle tabelle temporanee.

Si prega di mettere in atto questi due importanti cambiamenti e vedrete una differenza nel tempo di esecuzione.

Provaci !!!

AGGIORNAMENTO 2011-08-25 08:15 EDT

Ho guardato i tuoi indici. Hai ancora bisogno di creare l'indice che ho suggerito.


Grazie per un'enorme spiegazione su come funziona. Temo di non riuscire a capire come evitare situazioni del genere. Gli inserti devono modificare gli indici e selezionare deve utilizzare l'indice su hotspot_id e discover_at. Sarei felice se potessi anche rispondere alla mia "idea" di passare a MyISAM.
kaczor1984,

In realtà, l'utilizzo di MyISAM potrebbe peggiorare le cose perché ogni INSERT, UPDATE e DELETE in MyISAM attivano un blocco completo della tabella. Anche se utilizzi LOW_PRIORITY INSERT o INSERT DELAYED, verranno comunque rilevati blocchi completi della tabella. È necessario esplorare la query stessa poiché, indipendentemente dal motore di archiviazione, è possibile ottimizzare le query in base a tali ostacoli. Per lo meno, potrebbe essere necessario un nuovo algoritmo del tutto. Vedrò la domanda tra un paio di minuti ...
RolandoMySQLDBA il

Ho aggiornato il mio primo post in modo da poter vedere inserire query e indici in questa tabella.
kaczor1984,

Aggiornato la mia risposta con un'analisi della query al Process ID 40
RolandoMySQLDBA

4
signore, siete un eroe tra gli uomini per le vostre risposte mysql lunghe
Mike

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.