Basis data MySQL InnoDB 'hang' pada pilihan

10

Saya mencoba memperbaiki konfigurasi MySQL di server kami. Khusus aplikasi kami adalah bahwa banyak data disimpan dalam satu tabel (saat ini lebih dari 300 juta baris). Tabel ini sering digunakan untuk menyisipkan (mereka datang sepanjang waktu).

Ketika saya menjalankan kueri pemilihan pada tabel itu yang membutuhkan waktu lebih dari beberapa detik, maka semua sisipan (tepatnya komit) sedang menunggu akses tabel dan membuat aplikasi kita tidak responsif.

Sejauh yang saya tahu InnoDB tidak membuat kunci di atas meja ketika pilih sedang berjalan. Mengapa tabel pemblokiran pilih itu?

Saya mencoba mencari alasan dengan innotop tetapi saya tidak yakin bagaimana menafsirkan outputnya dan ke mana harus mencari. Katakan apa yang Anda butuhkan dan saya akan mempostingnya di sini.

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

EDIT:

Terima kasih telah menjelaskan ini.

Jadi saya harus membagi pertanyaan saya menjadi dua kasus sekarang.

  1. Apakah normal bahwa kunci pada tabel tunggal ini menyebabkan seluruh aplikasi saya 'hang'. Bukankah DB harus responsif untuk kueri ke tabel lain? Mungkin beberapa buffer diset terlalu rendah?

  2. Akankah beralih tabel ini ke MyISAM membantu? Saya tidak membutuhkan transaksi di meja ini sama sekali. Tidak akan ada kunci lain dalam situasi seperti itu (pilih panjang + banyak sisipan cepat)?

EDIT2:

Begitulah tampilan kueri insert:

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

Itulah yang didefinisikan indeks di atasnya:

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

Mengapa selama permintaan seperti itu seluruh aplikasi saya tidak merespons? Bukankah seharusnya hanya mempengaruhi 'large_table' itu?

Mungkin ada yang salah dengan konfigurasi mysql saya? Server adalah 4 inti Xeon 2GHz dengan RAM 16GB. Ini menjalankan Aplikasi MySQL + Rails

Parameter konfigurasi saya:

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

Skrip Mysqltuner hanya menyarankan:

long_query_time (<= 10)
innodb_buffer_pool_size (>= 62G)
kaczor1984
sumber
Harap tambahkan hasil show engine innodb status;.
quanta
Di innotop Anda dapat menekan L untuk mendapatkan tampilan ke dalam kunci. Bagaimana aplikasi Anda membuat koneksi? JDBC? Apa level defaultTransactionIsolation yang Anda gunakan?
HTTP500
Ini adalah aplikasi RoR jadi saya kira itu default untuk MySQL (dapatkah saya memeriksanya dengan query SQL?). innotop tidak menunjukkan kunci apa pun saat pemilihan ini berjalan.
kaczor1984
@ kaczor1984 Anda dapat memeriksa level isolasi default dengan melakukan: tampilkan variabel seperti 'tx_isolation'; pertanyaan. Default adalah REPEATABLE READ. Perhatikan bahwa MVCC hanya berfungsi dengan REPEATABLE READ dan READ COMMITTED. Tidak yakin apa solusi untuk masalah Anda tetapi jawaban RolandoMySQLDBAs informatif.
HTTP500
Memperbarui jawaban saya dengan analisis kueri di Process ID 40
RolandoMySQLDBA

Jawaban:

15

Harap perhatikan dengan cermat daftar proses dan 'tampilkan status innodb engine'. Apa yang kamu lihat ???

ID proses 1,2,4,5,6,13 semuanya mencoba menjalankan COMMIT.

Siapa yang menahan semuanya ??? ID proses 40 menjalankan kueri terhadap large_table.

ID proses 40 telah berjalan selama 33 detik. ID proses 1,2,4,5,6,13 telah berjalan kurang dari 33 detik. ID proses 40 sedang memproses sesuatu. Apa yang ditahan ???

Pertama-tama, kueri menggedor indeks cluster big_table via MVCC .

Dalam ID Proses 1,2,4,5,6,13 adalah baris yang memiliki Data MVCC melindungi isolasi transaksinya. ID proses 40 memiliki kueri yang berjalan melalui barisan data. Jika ada indeks di hotspot_id bidang, kunci itu + kunci ke baris aktual dari indeks berkerumun harus melakukan kunci internal. (Catatan: Sesuai desain, semua indeks tidak unik di InnoDB membawa kedua kunci Anda (kolom yang ingin Anda indeks) + kunci indeks yang dikelompokkan). Skenario unik ini pada dasarnya adalah Kekuatan Tak Terbendung memenuhi Objek Tak Bergerak.

Intinya, COMMIT harus menunggu hingga aman untuk menerapkan perubahan terhadap large_table. Situasi Anda tidak unik, tidak hanya satu kali, bukan fenomena langka.

Saya sebenarnya menjawab tiga pertanyaan seperti ini di DBA StackExchange. Pertanyaan diajukan oleh orang yang sama terkait dengan satu masalah yang sama. Jawaban saya bukanlah solusi tetapi membantu pengaju pertanyaan sampai pada kesimpulannya sendiri tentang bagaimana menangani situasinya.

Selain jawaban itu, saya menjawab pertanyaan orang lain tentang kebuntuan di InnoDB sehubungan dengan SELECT .

Saya harap posting saya yang lalu tentang hal ini membantu memperjelas apa yang terjadi pada Anda.

UPDATE 2011-08-25 08:10 EDT

Berikut ini permintaan dari Process ID 40

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

Dua pengamatan:

  • Anda melakukan 'SELECT *', apakah Anda perlu mengambil setiap kolom? Jika Anda hanya membutuhkan kolom tertentu, Anda harus memberi labelnya karena tabel temp 1000 baris bisa lebih besar dari yang sebenarnya Anda butuhkan.

  • Klausa WHERE dan ORDER BY biasanya memberikan masalah kinerja atau membuat desain tabel bersinar. Anda perlu membuat mekanisme yang akan mempercepat pengumpulan kunci sebelum mengumpulkan data.

Mengingat dua pengamatan ini, ada dua perubahan besar yang harus Anda lakukan:

PERUBAHAN UTAMA # 1: Refactor permintaan

Mendesain ulang kueri sehingga

  1. kunci dikumpulkan dari indeks
  2. hanya 1000 atau mereka yang mengumpulkan
  3. bergabung kembali ke meja utama

Inilah permintaan baru yang melakukan tiga hal ini

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

Subquery large_table_keys mengumpulkan 1000 kunci yang Anda butuhkan. Hasil dari subquery ini kemudian INNER BERGABUNG ke large_table. Sejauh ini, tombol diambil alih-alih seluruh baris. Itu masih 799.000 baris untuk dibaca. Ada cara yang lebih baik untuk mendapatkan kunci itu, yang menuntun kita ke ...

PERUBAHAN UTAMA # 2: Buat Indeks yang Mendukung Permintaan Refactored

Karena permintaan refactored hanya menampilkan satu subquery, Anda hanya perlu membuat satu indeks. Inilah indeks itu:

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

Mengapa indeks khusus ini? Lihatlah klausa WHERE. Hotspot_id adalah nilai statis. Ini membuat semua hotspot_ids membentuk daftar berurutan dalam indeks. Sekarang, lihat klausa ORDER BY. Kolom yang ditemukan mungkin adalah bidang DATETIME atau TIMESTAMP.

Urutan alami yang disajikan dalam indeks adalah sebagai berikut:

  • Index menampilkan daftar hostpot_ids
  • Setiap hotspot_id memiliki daftar bidang bidang yang ditemukan

Membuat indeks ini juga menghilangkan melakukan penyortiran internal tabel temp.

Harap lakukan dua perubahan besar ini di tempat dan Anda akan melihat perbedaan dalam waktu berjalan.

Cobalah !!!

UPDATE 2011-08-25 08:15 EDT

Saya melihat indeks Anda. Anda masih perlu membuat indeks yang saya sarankan.

RolandoMySQLDBA
sumber
Terima kasih atas penjelasan yang luar biasa tentang cara kerjanya. Saya khawatir saya tidak tahu bagaimana cara menghindari situasi seperti itu. Sisipan harus memodifikasi indeks dan memilih harus menggunakan indeks pada hotspot_id dan found_at. Saya akan senang jika Anda juga bisa menjawab 'ide' saya untuk beralih ke MyISAM.
kaczor1984
Sebenarnya, menggunakan MyISAM dapat memperburuk keadaan karena setiap INSERT, UPDATE, dan DELETE di MyISAM memicu kunci tabel penuh. Bahkan jika Anda menggunakan LOW_PRIORITY INSERTs atau INSERT DELAYED, kunci tabel penuh masih akan ditemui. Anda harus menjelajahi kueri itu sendiri karena terlepas dari mesin penyimpanan, kueri dapat disesuaikan dengan kendala tersebut. Paling tidak, algoritma baru sama sekali mungkin diperlukan. Saya akan melihat permintaan dalam beberapa menit ...
RolandoMySQLDBA
Saya telah memperbarui posting pertama saya sehingga Anda dapat melihat kueri dan indeks pada tabel ini.
kaczor1984
Memperbarui jawaban saya dengan analisis kueri di Process ID 40
RolandoMySQLDBA
4
Anda tuan adalah pahlawan di antara laki-laki untuk jawaban
Mike
3

Terpecahkan!

Masalah utama adalah query_cache. http://bugs.mysql.com/bug.php?id=21074

Setelah menonaktifkannya, 'macet' menghilang.

kaczor1984
sumber
Dan juga terima kasih banyak kepada @Rando_MySQLDBA untuk petunjuk tentang mengoptimalkan kueri dan indeks saya.
kaczor1984