Mengapa MySQL melakukan serial I / O sinkron?

8

Ketika melihat permintaan yang sangat mengganggu atas tabel MyISAM yang membutuhkan waktu lama untuk dieksekusi pada beberapa kesempatan, saya telah mencatat bahwa MySQL tampaknya mengekspos pola I / O yang agak aneh: ketika mengeksekusi permintaan tunggal dan harus melakukan signifikan jumlah I / O (misalnya untuk pemindaian tabel atau ketika cache kosong sebagai akibatnya echo 3 > /proc/sys/vm/drop_cachessehingga indeks harus dimuat dari disk terlebih dahulu), ukuran antrian untuk perangkat blok yang mendasarinya mendekati nilai 1, dengan kinerja luar biasa dari hanya 4-5 MB / s:

root@mysql-test:~# iostat -xdm 5 /dev/sda
Linux 3.2.0-40-generic (mysql-test)  04/30/2014      _x86_64_        (4 CPU)

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.14    24.82   18.26   88.79     0.75     4.61   102.56     2.83   26.39   19.29   27.85   2.46  26.31

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00    69.29  151.52   72.73     5.31     0.59    53.95     1.21    5.39    7.84    0.29   4.39  98.51

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00   153.06  144.29  174.69     4.96     1.36    40.54     1.39    4.36    8.91    0.60   3.15 100.49

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00   105.75  150.92  109.03     4.53     0.85    42.41     1.29    4.96    8.15    0.54   3.90 101.36

Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00    48.89  156.36   51.72     5.28     0.76    59.38     1.28    6.16    8.02    0.55   4.77  99.23

Sementara 150 IOPS adalah disk tunggal dalam konfigurasi yang diberikan yang mampu menghasilkan I / O acak, hasilnya masih sangat mengejutkan saya karena saya berharap MySQL dapat menjalankan I / O asinkron untuk membaca dan mengambil sejumlah besar blok secara bersamaan alih-alih membaca dan mengevaluasinya satu per satu, secara efektif mengabaikan perolehan paralelisasi yang tersedia dalam konfigurasi RAID. Apa keputusan desain atau opsi konfigurasi yang bertanggung jawab untuk ini? Apakah ini masalah khusus platform?

Sementara saya telah menguji ini dengan tabel MyISAM besar-ish, saya melihat efek yang sama dengan tabel yang sama dikonversi ke InnoDB (meskipun tidak seburuk itu, permintaan sampel masih membutuhkan waktu 20-30 detik dengan sebagian besar waktu dihabiskan untuk membaca disk dengan panjang antrian 1) setelah saya me-restart daemon mysql dan oleh karena itu kolam buffer kosong. Saya juga telah memverifikasi bahwa masalah yang sama tetap pada 5,6 GA dan saat ini 5,7 tonggak sejarah 14 - selama saya menggunakan utas permintaan tunggal, MySQL tampaknya tidak dapat memparalelkan operasi I / O yang diperlukan untuk pemrosesan permintaan.


Sesuai permintaan beberapa detail tambahan pada skenario. Perilaku dapat diamati dengan banyak jenis kueri. Saya telah secara sewenang-wenang memilih satu untuk pengujian lebih lanjut yang berbunyi seperti ini:

SELECT herp.id, herp.firstname, herp.lastname, derp.label, herp.email, 
(SELECT CONCAT(label, " (", zip_code, " ", city,")" ) FROM subsidiaries WHERE subsidiaries.id=herp.subsidiary_id ) AS subsidiary, 
(SELECT COUNT(fk_herp) from herp_missing_data WHERE fk_herp=herp.id) AS missing_data
FROM herp LEFT JOIN derp ON derp.id=herp.fk_derp
WHERE (herp.fk_pools='123456')  AND herp.city LIKE '%Some City%' AND herp.active='yes' 
ORDER BY herp.id desc LIMIT 0,10;

Saya tahu bahwa ia memiliki beberapa ruang untuk optimasi, tetapi saya memutuskan untuk berhenti karena beberapa alasan dan berkonsentrasi pada mencari penjelasan umum untuk pola I / O yang tidak terduga yang saya lihat.

Tabel yang digunakan memang memiliki banyak data di dalamnya:

mysql> select table_name, engine, table_rows, data_length, index_length from information_schema.tables WHERE tables.TABLE_SCHEMA = 'mydb' and tables.table_name in ( 'herp', 'derp', 'missing_data', 'subsidiaries');
+-------------------------+--------+------------+-------------+--------------+
| table_name              | engine | table_rows | data_length | index_length |
+-------------------------+--------+------------+-------------+--------------+
| derp                    | MyISAM |      14085 |     1118676 |       165888 |
| herp                    | MyISAM |     821747 |   828106512 |    568057856 |
| missing_data            | MyISAM |    1220186 |    15862418 |     29238272 |
| subsidiaries            | MyISAM |       1499 |     6490308 |       103424 |
+-------------------------+--------+------------+-------------+--------------+
4 rows in set (0.00 sec)

Sekarang ketika saya menjalankan kueri di atas tabel-tabel ini, saya mendapatkan waktu eksekusi lebih dari 1 menit sementara sistem tampaknya terus-menerus sibuk membaca data dari disk dengan satu utas.

Profil untuk eksekusi permintaan sampel (yang mengambil 1 menit 9,17 detik dalam contoh ini) terlihat seperti ini:

mysql> show profile for query 1;
+--------------------------------+-----------+
| Status                         | Duration  |
+--------------------------------+-----------+
| starting                       |  0.000118 |
| Waiting for query cache lock   |  0.000035 |
| init                           |  0.000033 |
| checking query cache for query |  0.000399 |
| checking permissions           |  0.000077 |
| checking permissions           |  0.000030 |
| checking permissions           |  0.000031 |
| checking permissions           |  0.000035 |
| Opening tables                 |  0.000158 |
| init                           |  0.000294 |
| System lock                    |  0.000056 |
| Waiting for query cache lock   |  0.000032 |
| System lock                    |  0.000116 |
| optimizing                     |  0.000063 |
| statistics                     |  0.001964 |
| preparing                      |  0.000104 |
| Sorting result                 |  0.000033 |
| executing                      |  0.000030 |
| Sending data                   |  2.031349 |
| optimizing                     |  0.000054 |
| statistics                     |  0.000039 |
| preparing                      |  0.000024 |
| executing                      |  0.000013 |
| Sending data                   |  0.000044 |
| optimizing                     |  0.000017 |
| statistics                     |  0.000021 |
| preparing                      |  0.000019 |
| executing                      |  0.000013 |
| Sending data                   | 21.477528 |
| executing                      |  0.000070 |
| Sending data                   |  0.000075 |
| executing                      |  0.000027 |
| Sending data                   | 45.692623 |
| end                            |  0.000076 |
| query end                      |  0.000036 |
| closing tables                 |  0.000109 |
| freeing items                  |  0.000067 |
| Waiting for query cache lock   |  0.000038 |
| freeing items                  |  0.000080 |
| Waiting for query cache lock   |  0.000044 |
| freeing items                  |  0.000037 |
| storing result in query cache  |  0.000033 |
| logging slow query             |  0.000103 |
| cleaning up                    |  0.000073 |
+--------------------------------+-----------+
44 rows in set, 1 warning (0.00 sec)
syneticon-dj
sumber
Apakah Anda memiliki test case yang dapat diulang (idealnya sederhana) yang dapat Anda jelaskan dengan lebih detail? Misalnya kueri yang menghasilkan perilaku ini? Dalam keadaan apa? Anda mulai menyusuri jalan itu dengan "echo 3> ..." & "restart daemon mysql" tetapi tidak merinci.
Scott Leadley
@ScottLeadley terima kasih telah melihat ini. Saya tidak berpikir saya akan dapat membuatnya "sederhana" - masalah ini hanya akan dapat diamati jika sejumlah besar data perlu dibaca untuk satu permintaan dan itu sebagian besar akan acak I / O. Tabel dan query cukup mudah dan sementara saya bisa memposting teks DDL dan Query, saya ragu ada orang yang bisa mereproduksi segera kecuali data tabel / indeks telah tumbuh hingga ratusan Megabyte.
syneticon-dj
Seperti yang Anda singgung, 5 ms yang menunggu untuk dibaca konsisten dengan latensi rotasi rata-rata satu 5400 RPM disk. Cari pertentangan saat membaca "sejumlah besar data ... sebagian besar I / O acak" akan menjelaskan hal itu. Mengenai RAID, Anda telah menyebutkannya, tetapi belum memberikan detail konfigurasi khusus ini.
Scott Leadley
Tidak yakin saya dapat membantu Anda secara langsung, karena saya tidak menjalankan konfigurasi Anda. Tetapi aturan praktis StackExchange adalah bahwa pertanyaan yang benar-benar bagus mendapat lebih banyak perhatian daripada hadiah. Menulis pertanyaan yang sempurna
Scott Leadley
@ScottLeadley 5 ms menunggu sebagian besar disebabkan oleh latensi sistem penyimpanan yang digunakan. Saya telah menguji ini dalam skenario yang berbeda - dari RAID10 4 disk yang sederhana hingga filer penyimpanan berjenjang dengan rak 16 disk dan dukungan SSD, hasilnya secara konsisten menunjukkan bahwa beban I / O tidak diparalelkan dan dengan demikian terikat pada latensi. Yang saya rasakan pada dasarnya salah. Saya telah menambahkan detail kueri ke pertanyaan, tetapi saya belum yakin mereka akan sangat membantu.
syneticon-dj

Jawaban:

8

Pertama izinkan saya mengklarifikasi dengan mengkonfirmasi bahwa MyISAM tidak melakukan I / O asinkron, tetapi InnoDB melakukannya dan akan secara default dari MySQL 5.5. Sebelum 5,5 digunakan "simulasi AIO" dengan menggunakan utas pekerja.

Saya pikir penting juga untuk membedakan antara tiga situasi:

  1. Beberapa kueri mengeksekusi sekaligus
  2. Satu permintaan dieksekusi secara paralel
  3. Semacam membaca logis ke depan untuk pemindaian tabel / kasus yang jelas di mana halaman berikutnya terkenal.

Untuk (1) I / O akan dapat melakukan secara paralel untuk ini. Ada beberapa batasan dengan MyISAM: penguncian tabel dan kunci global yang melindungi key_buffer(cache indeks). InnoDB di MySQL 5.5+ benar-benar bersinar di sini.

Untuk (2) saat ini tidak didukung. Kasus penggunaan yang baik adalah dengan mempartisi, di mana Anda bisa mencari setiap tabel dipartisi secara paralel.

Untuk (3) InnoDB memiliki linear baca-depan untuk membaca sepenuhnya (kelompok 64 halaman) jika> 56 halaman dibaca (ini dapat dikonfigurasi), tetapi ada ruang untuk peningkatan lebih lanjut. Facebook telah menulis tentang menerapkan logis-readhead di cabang mereka (dengan keuntungan 10x pada tabel tablescan).

Morgan Tocker
sumber
Terima kasih, ini memberi saya pemahaman tambahan tentang apa yang saya lihat. Apakah itu secara umum berarti bahwa MyISAM tidak dapat memanfaatkan lebih dari satu nilai disk IOPS untuk beban single-threaded? Saya tidak dapat menemukan referensi untuk ini di dokumen - apakah Anda memiliki sesuatu yang berguna?
syneticon-dj
Iya. Saya tidak bisa memikirkan tempat di dokumen di mana ini akan terjadi.
Morgan Tocker
2

Saya harap missing_databukan MyISAM karena tabel MyISAM yang kosong biasanya memiliki 1024 byte .MYI. Ukuran byte yang bukan nol diharapkan dari MyISAM. Sebuah byte nol .MYIterdengar agak menyeramkan bagi saya.

Jika Anda menjalankan kueri metadata ini

select table_name, table_rows, data_length, index_length, engine
from information_schema.tables
WHERE tables.TABLE_SCHEMA = 'mydb'
and tables.table_name = 'missing_data';

dan mesin meja itu adalah MyISAM, Anda harus memperbaikinya.

SISI CATATAN: Jika engineadalah NULL, itu pandangan. Jika ini adalah tampilan atau bukan MyISAM, abaikan sisa posting saya dan tambahkan info itu ke pertanyaan. Jika tabelnya adalah MyISAM, baca terus ...

Menurut kueri metadata Anda, missing_data.MYDsekitar 46 juta.

Pertama, jalankan ini

SHOW CREATE TABLE mydb.missing_data\G

Anda akan mendapatkan deskripsi tabel atau pesan kesalahan yang mengatakan sesuatu seperti

ERROR 126 (HY000): Incorrect key file for table ...

Jika Anda mendapatkan deskripsi tabel dan itu adalah MyISAM, jalankan

OPTIMIZE TABLE mydb.missing_data;

Ini akan membuat ulang tabel tanpa fragmentasi dan menghitung statistik indeks segar. Jika tidak berhasil maka coba:

REPAIR TABLE mydb.missing_data;

Itu harus membuat ulang halaman indeks untuk MyISAM.

Agar aman (jika menggunakan MySQL 5.6), jalankan ini setelah perbaikan

FLUSH TABLES mydb.missing_data;

Pertanyaanmu

Indeks tabel Anda mungkin tidak dimuat ke dalam memori jika Pengoptimal Permintaan MySQL memutuskan untuk tidak menggunakan. Jika klausa WHERE Anda menentukan sejumlah besar baris harus dibaca dari indeks, MySQL Query Optimizer akan melihat bahwa ketika membuat paket EXPLAIN dan memutuskan untuk menggunakan pemindaian tabel penuh sebagai gantinya.

Operasi I / O paralel pada tabel MyISAM tidak dapat dicapai karena tidak dapat dikonfigurasi.

InnoDB dapat disetel untuk meningkatkan kinerja seperti itu.

RolandoMySQLDBA
sumber
Saya harus menekankan lagi: Jika mydb.missing_data adalah MyISAM dan memiliki indeks byte nol, pasti ada sesuatu yang salah.
RolandoMySQLDBA
Saya telah memperbarui data agar lebih koheren - sekarang menunjukkan hasil MyISAM saja dari satu host sehingga orang tidak akan bingung.
syneticon-dj