Die MySQL InnoDB-Datenbank hängt an ausgewählten Optionen

10

Ich versuche, die MySQL-Konfiguration auf unserem Server zu korrigieren. Die Besonderheit unserer App ist, dass viele Daten in einer einzigen Tabelle gespeichert sind (derzeit über 300 Millionen Zeilen). Diese Tabelle wird häufig für Einfügungen verwendet (sie kommen ständig vor).

Wenn ich eine Auswahlabfrage für diese Tabelle ausführe, die länger als einige Sekunden dauert, warten alle Einfügungen (genau Commits) auf den Tabellenzugriff und lassen unsere App nicht mehr reagieren.

Soweit ich weiß, macht InnoDB keine Sperren für den Tisch, wenn select ausgeführt wird. Warum ist die Select-Blocking-Tabelle dann?

Ich habe versucht, mit innotop einen Grund zu finden, bin mir aber nicht sicher, wie ich die Ausgabe interpretieren und wo ich suchen soll. Sag mir was du brauchst und ich werde es hier posten.

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

BEARBEITEN:

Vielen Dank für die Klarstellung.

Also muss ich meine Frage jetzt in zwei Fälle aufteilen.

  1. Ist es normal, dass die Sperre für diese einzelne Tabelle dazu führt, dass meine gesamte App "hängt"? Sollte die Datenbank nicht auf Abfragen zu anderen Tabellen reagieren? Vielleicht ist ein Puffer zu niedrig eingestellt?

  2. Hilft es, diese Tabelle auf MyISAM umzustellen? Ich brauche überhaupt keine Transaktionen für diesen Tisch. Wird es in einer solchen Situation keine anderen Sperren geben (lange Auswahl + viele schnelle Einfügungen)?

EDIT2:

So sehen Einfüge-Abfragen aus:

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

Darauf sind Indizes definiert:

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

Warum antwortet meine Bewerbung bei solchen Anfragen nicht? Sollte es nicht nur diese 'große_Tabelle' betreffen?

Vielleicht stimmt etwas mit meiner MySQL-Konfiguration nicht? Der Server ist ein 4-Kern-Xeon 2 GHz mit 16 GB RAM. Es läuft MySQL + Rails App

Meine Konfigurationsparameter:

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

Das Mysqltuner-Skript schlägt nur Folgendes vor:

long_query_time (<= 10)
innodb_buffer_pool_size (>= 62G)
kaczor1984
quelle
Bitte hängen Sie die Ausgabe von an show engine innodb status;.
Quanten
In innotop können Sie L drücken, um einen Blick in die Schleusen zu erhalten. Wie stellt Ihre App eine Verbindung her? JDBC? Welche defaultTransactionIsolation-Ebene verwenden Sie?
HTTP500
Da es sich um eine RoR-Anwendung handelt, ist sie vermutlich die Standardeinstellung für MySQL (kann ich sie irgendwie per SQL-Abfrage überprüfen?). innotop zeigt keine Sperren an, während diese Auswahl ausgeführt wird.
Kaczor1984
@ kaczor1984 Sie können die Standardisolationsstufe überprüfen, indem Sie Folgendes ausführen: show variables wie 'tx_isolation'; Abfrage. Die Standardeinstellung ist REPEATABLE READ. Beachten Sie, dass MVCC nur mit REPEATABLE READ und READ COMMITTED funktioniert. Ich bin mir nicht sicher, wie das Problem gelöst werden soll, aber die Antwort von RolandoMySQLDBA war informativ.
HTTP500
Aktualisierte meine Antwort mit einer Analyse der Abfrage bei Process ID 40
RolandoMySQLDBA

Antworten:

15

Bitte schauen Sie sich die Prozessliste und den 'show engine innodb status' genau an. Was siehst du ???

Die Prozess-IDs 1,2,4,5,6,13 versuchen alle, COMMIT auszuführen.

Wer hält alles hoch ??? Die Prozess-ID 40 führt eine Abfrage für large_table aus.

Die Prozess-ID 40 wird seit 33 Sekunden ausgeführt. Die Prozess-IDs 1,2,4,5,6,13 wurden weniger als 33 Sekunden ausgeführt. Die Prozess-ID 40 verarbeitet etwas. Warum die Verzögerung ???

Zunächst schlägt die Abfrage über MVCC auf den Clustered-Index von large_table ein .

Innerhalb der Prozess-IDs 1,2,4,5,6,13 befinden sich Zeilen, deren Transaktionsisolation durch MVCC-Daten geschützt ist. Die Prozess-ID 40 hat eine Abfrage, die durch Datenzeilen marschiert. Wenn für das Feld hotspot_id ein Index vorhanden ist, muss dieser Schlüssel + der Schlüssel für die tatsächliche Zeile aus dem Clustered-Index eine interne Sperre durchführen. (Hinweis: Alle nicht eindeutigen Indizes in InnoDB enthalten standardmäßig sowohl Ihren Schlüssel (die Spalte, die Sie indizieren wollten) als auch einen Clustered-Indexschlüssel.) Dieses einzigartige Szenario ist im Wesentlichen Unaufhaltsame Kraft trifft unbewegliches Objekt.

Im Wesentlichen müssen die COMMITs warten, bis es sicher ist, Änderungen gegen large_table anzuwenden. Ihre Situation ist nicht einzigartig, kein einmaliges, kein seltenes Phänomen.

Ich habe tatsächlich drei Fragen wie diese im DBA StackExchange beantwortet. Die Fragen wurden von derselben Person eingereicht , die sich auf dasselbe Problem bezog. Meine Antworten waren nicht die Lösung, sondern halfen dem Fragesteller, zu seiner eigenen Schlussfolgerung zu gelangen, wie er mit seiner Situation umgehen soll.

Zusätzlich zu diesen Antworten beantwortete ich die Frage einer anderen Person zu Deadlocks in InnoDB in Bezug auf SELECTs .

Ich hoffe, meine früheren Beiträge zu diesem Thema helfen zu klären, was mit Ihnen passiert ist.

UPDATE 25.08.2011 08:10 EDT

Hier ist die Abfrage von Prozess-ID 40

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

Zwei Beobachtungen:

  • Sie machen 'SELECT *'. Müssen Sie jede Spalte abrufen? Wenn Sie nur bestimmte Spalten benötigen, sollten Sie diese beschriften, da die temporäre Tabelle mit 1000 Zeilen möglicherweise größer ist als Sie wirklich benötigen.

  • Die WHERE- und ORDER BY-Klauseln verraten normalerweise Leistungsprobleme oder bringen das Tabellendesign zum Leuchten. Sie müssen einen Mechanismus erstellen, der das Sammeln von Schlüsseln beschleunigt, bevor Sie Daten sammeln.

Angesichts dieser beiden Beobachtungen müssen Sie zwei wichtige Änderungen vornehmen:

WICHTIGE ÄNDERUNG 1: Refaktorieren Sie die Abfrage

Gestalten Sie die Abfrage so um

  1. Schlüssel werden aus dem Index gesammelt
  2. nur 1000 oder sie werden gesammelt
  3. wieder mit dem Haupttisch verbunden

Hier ist die neue Abfrage, die diese drei Dinge tut

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

Die Unterabfrage large_table_keys sammelt die 1000 Schlüssel, die Sie benötigen. Das Ergebnis der Unterabfrage wird dann INNER JOINed zu large_table. Bisher werden die Schlüssel anstelle ganzer Zeilen abgerufen. Das sind immer noch 799.000 Zeilen zum Durchlesen. Es gibt einen besseren Weg, um diese Schlüssel zu bekommen, der uns zu ...

WICHTIGE ÄNDERUNG 2: Erstellen Sie Indizes, die die überarbeitete Abfrage unterstützen

Da die überarbeitete Abfrage nur eine Unterabfrage enthält, müssen Sie nur einen Index erstellen. Hier ist dieser Index:

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

Warum dieser spezielle Index? Schauen Sie sich die WHERE-Klausel an. Die hotspot_id ist ein statischer Wert. Dadurch bilden alle hotspot_ids eine sequentielle Liste im Index. Schauen Sie sich nun die ORDER BY-Klausel an. Die Spalte found_at ist wahrscheinlich ein Feld DATETIME oder TIMESTAMP.

Die natürliche Reihenfolge, die dies im Index darstellt, ist wie folgt:

  • Der Index enthält eine Liste der hostpot_ids
  • Jede hotspot_id verfügt über eine geordnete Liste der erkannten_at-Felder

Durch das Erstellen dieses Index wird auch das interne Sortieren von temporären Tabellen vermieden.

Bitte setzen Sie diese beiden wichtigen Änderungen ein und Sie werden einen Unterschied in der Laufzeit feststellen.

Versuche es !!!

UPDATE 25.08.2011 08:15 EDT

Ich habe mir deine Indexe angesehen. Sie müssen noch den von mir vorgeschlagenen Index erstellen.

RolandoMySQLDBA
quelle
Vielen Dank für eine große Erklärung, wie es funktioniert. Ich fürchte, ich kann nicht herausfinden, wie ich solche Situationen vermeiden kann. Einfügungen müssen Indizes ändern und select muss den Index für hotspot_id und found_at verwenden. Ich würde mich freuen, wenn Sie auch auf meine „Idee“, zu MyISAM zu wechseln, antworten könnten.
Kaczor1984
Tatsächlich könnte die Verwendung von MyISAM die Situation verschlimmern, da jedes EINFÜGEN, UPDATE und LÖSCHEN in MyISAM eine vollständige Tabellensperre auslöst. Selbst wenn Sie LOW_PRIORITY INSERTs oder INSERT DELAYED verwenden, werden weiterhin vollständige Tabellensperren gefunden. Sie sollten die Abfrage selbst untersuchen, da Abfragen unabhängig von der Speicher-Engine um solche Hindernisse herum optimiert werden können. Zumindest kann insgesamt ein neuer Algorithmus erforderlich sein. Ich werde die Abfrage in ein paar Minuten betrachten ...
RolandoMySQLDBA
Ich habe meinen ersten Beitrag aktualisiert, damit Sie Abfragen und Indizes für diese Tabelle einfügen können.
Kaczor1984
Aktualisierte meine Antwort mit einer Analyse der Abfrage bei Process ID 40
RolandoMySQLDBA
4
Sie, mein Herr, sind ein Held unter Männern für Ihre langen MySQL-Antworten
Mike
3

Gelöst!

Das Hauptproblem war query_cache. http://bugs.mysql.com/bug.php?id=21074

Nach dem Deaktivieren verschwanden die "Einfrierungen".

kaczor1984
quelle
Und auch ein großes Dankeschön an @RolandoMySQLDBA für Hinweise zur Optimierung meiner Abfragen und Indizes.
Kaczor1984