Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

MySQL MyISAM slow count() query meskipun mencakup indeks

Inilah yang terjadi.

The SELECT COUNT (...) icd_index where icd='25000'

akan menggunakan indeks, yang merupakan BTree terpisah dari data. Tapi itu memindai dengan cara ini:

  1. Temukan entri pertama yang memiliki icd='25000'. Ini hampir seketika.
  2. Scan maju sampai menemukan perubahan pada icd. Ini akan memindai dalam indeks saja, tidak menyentuh data. Menurut EXPLAIN, akan ada sekitar 910.104 entri indeks untuk dipindai.

Sekarang mari kita lihat BTree untuk indeks itu. Berdasarkan bidang dalam indeks, setiap baris akan tepat 22 byte, ditambah akan ada beberapa overhead (perkiraan 40%). Blok indeks MyISAM adalah 1KB (lih 16KB InnoDB). Saya akan memperkirakan 33 baris per blok. 910.104/33 mengatakan sekitar 27K blok perlu dibaca untuk melakukan COUNT. (Catatan COUNT(core_id) perlu memeriksa core_id karena null, COUNT(*) tidak; ini adalah perbedaan kecil.) Membaca 27K blok pada hard drive biasa membutuhkan waktu sekitar 270 detik. Anda beruntung bisa menyelesaikannya dalam 60 detik.

Proses kedua menemukan semua blok tersebut di key_buffer (dengan asumsi key_buffer_size setidaknya 27MB), jadi tidak perlu menunggu disk. Oleh karena itu jauh lebih cepat. (Ini mengabaikan cache Kueri, yang sebaiknya Anda hapus atau gunakan SQL_NO_CACHE.)

5.6 kebetulan tidak relevan (tapi terima kasih telah menyebutkannya), karena proses ini tidak berubah sejak 4.0 atau sebelumnya (kecuali bahwa utf8 tidak ada; lebih lanjut tentang itu di bawah).

Beralih ke InnoDB akan membantu dalam beberapa cara. KUNCI UTAMA akan 'dikelompokkan' dengan data, tidak disimpan sebagai BTree yang terpisah. Oleh karena itu, begitu data atau PK di-cache, yang lain segera tersedia. Jumlah blok akan lebih seperti 5K, tetapi akan menjadi blok 16KB. Ini mungkin dapat dimuat lebih cepat jika cache dingin.

Anda bertanya " Apakah saya memerlukan indeks pada icd saja? " -- Nah itu akan mengecilkan ukuran MyISAM BTree menjadi sekitar 21 byte per baris, jadi ukuran BTree akan menjadi sekitar 21/27, tidak banyak peningkatan (setidaknya untuk situasi cold-cache).

Pikiran lain adalah, jika icd selalu numerik dan selalu numerik, untuk menggunakan MEDIUMINT UNSIGNED , dan tempelkan ZEROFILL jika dapat memiliki nol di depan.

Ups, saya gagal memperhatikan SET KARAKTER. (Saya telah memperbaiki angka-angka di atas, tetapi izinkan saya menguraikannya.)

  • CHAR(5) memungkinkan untuk 5 karakter .
  • ascii membutuhkan 1 byte per karakter .
  • utf8 membutuhkan waktu hingga 3 byte per karakter .
  • Jadi, CHAR(5) CHARACTER SET utf8 membutuhkan 15 byte selalu .

Mengubah kolom menjadi CHAR(5) CHARACTER SET ascii akan mengecilkannya menjadi 5 byte.

Mengubahnya menjadi MEDIUMINT UNSIGNED ZEROFILL akan mengecilkannya menjadi 3 byte.

Mengecilkan data akan mempercepat I/O dengan jumlah yang kira-kira proporsional (setelah mengizinkan 6 byte lagi untuk dua bidang lainnya.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mengapa contoh pilih untuk pembaruan ini berfungsi?

  2. Bagaimana cara mendapatkan hanya node anak kedalaman tingkat pertama?

  3. Tingkatkan MySQL ke MariaDB 10 (Bagian 2 – Tingkatkan MariaDB/MySQL 5.5 ke Versi 10.0)

  4. Gunakan subquery yang berkorelasi pada dua kolom

  5. Bagaimana saya bisa bergabung dengan A dengan B dan juga B dengan C pada satu waktu?