Pilih kueri membutuhkan waktu lebih lama dari yang seharusnya

9

Saya memiliki tabel database MySQL dengan hampir 23 juta catatan. Tabel ini tidak memiliki kunci utama, karena tidak ada yang unik. Ini memiliki 2 kolom, keduanya diindeks. Di bawah ini adalah strukturnya:

masukkan deskripsi gambar di sini

Berikut adalah beberapa datanya:

masukkan deskripsi gambar di sini

Sekarang, saya menjalankan kueri sederhana:

SELECT `indexVal` FROM `key_word` WHERE `hashed_word`='001'

Sayangnya, ini membutuhkan waktu lebih dari 5 detik untuk mengambil data dan menunjukkannya kepada saya. Tabel masa depan saya akan memiliki 150 miliar catatan, jadi kali ini sangat sangat tinggi.

Saya menjalankan Explainperintah untuk melihat apa yang terjadi. Hasilnya di bawah.

masukkan deskripsi gambar di sini

Kemudian saya menjalankan Profile menggunakan perintah di bawah ini.

SET profiling=1;
SELECT `indexVal` FROM `key_word` WHERE `hashed_word` = '001';
SHOW profile;

Di bawah ini adalah hasil dari profil:

masukkan deskripsi gambar di sini

Di bawah ini adalah beberapa informasi lebih lanjut tentang meja saya:

masukkan deskripsi gambar di sini

Jadi, mengapa ini memakan waktu begitu lama? Mereka juga diindeks! Di masa depan, saya harus menjalankan banyak LIKEperintah, jadi ini terlalu banyak waktu. Apa yang salah?

Jus lemon
sumber
"Tabel ini tidak memiliki kunci utama, karena tidak ada yang unik." Ya, benar ... Saatnya memeriksa kembali desain Anda. Semua tabel harus memiliki kunci primer (atau unik).
ypercubeᵀᴹ

Jawaban:

10

Anda bertanya " mengapa ini terlalu lama ?". Anda juga berkata " Sayangnya, ini membutuhkan waktu lebih dari 5 detik untuk mengambil data dan menunjukkannya kepada saya ". Juga, Anda melaporkan keluaran profil dari kueri Anda.

Seperti yang Anda lihat sendiri, jumlah waktu yang dilaporkan oleh profiler untuk setiap langkah dihitung hingga 0,000154 detik. Jadi, dari sudut pandang profiler, kueri diselesaikan dalam waktu seperti itu (0,000154).

Jadi mengapa Anda mendapatkan hasil dalam " ... lebih dari 5 detik? ".

Anda bilang Anda memfilter tabel catatan 23 juta dengan bidang 3 char. Sayangnya Anda tidak memberi tahu kami berapa banyak rekaman yang dikembalikan kueri Anda ... tetapi berkat EXPLAIN SELECT yang disediakan, sepertinya kueri Anda mengembalikan 336052 catatan.

Tampaknya juga semua aktivitas Anda dijalankan melalui beberapa GUI (PHPMyAdmin?).

Jadi, setelah semua hal di atas, kami dapat merumuskan kembali pertanyaan awal Anda sebagai:

"Mengapa saya mendapatkan, dalam GUI saya, 336.052 catatan ditampilkan dalam lebih dari 5 detik, jika waktu eksekusi MySQL untuk permintaan terkait adalah 0,000154 detik?"

Jawabannya, menurut saya, cukup sederhana: 5 detik adalah waktu (benar-benar rendah, memang) untuk membiarkan 336.052 catatan berjalan di sepanjang jalur: mesin MySQL => MySQL client libraries => PHP modul MySQL => Apache => Network = > tumpukan TCP / IP PC Anda => Browser => parser / builder DOM / dll. => Halaman HTML yang di-render.

Adapun pengalaman saya sebelumnya, waktu yang dibutuhkan oleh transmisi hasil "biasanya" jauh lebih tinggi daripada waktu yang dibutuhkan untuk mengambil data tersebut. Ini berlaku terutama ketika perpustakaan seperti PHP-MySQL atau Perl-DBD-MySQL terlibat: mereka benar-benar membutuhkan banyak waktu untuk mengambil catatan, setelah MySQL telah mengidentifikasi (... dan mengekstraksi) semuanya dengan benar.

Bagaimana cara mengatasi masalah ini?

Sekali lagi, cukup mudah: apakah Anda benar-benar yakin bahwa Anda memerlukan SEMUA catatan 336.052, dalam satu set data, keseluruhan,?

  • Jika jawaban Anda benar-benar "YA! Saya membutuhkan semuanya", daripada aplikasi Anda akan menangani PAGINASI dan / atau PENGGUNA-Interaksi dengan sendirinya dan ... setelah mengumpulkan semua data seperti itu, mungkin akan menghabiskan banyak waktu berinteraksi dengan pengguna tanpa memerlukan interaksi MySQL lebih lanjut. Dalam kasus seperti itu, menunggu selama 5 detik (atau bahkan lebih) seharusnya tidak menjadi masalah;

  • Jika jawaban Anda adalah "TIDAK, saya ingin berurusan dengan ukuran dataset yang lebih 'manusia'", daripada Anda harus mempersempit kueri Anda (setidaknya) sehingga itu akan memberi Anda lebih banyak dataset "manusia" (puluhan atau, ratusan, paling banyak, catatan). Dalam kasus seperti itu, saya yakin Anda akan mendapatkan hasil Anda dalam waktu yang lebih singkat.


BTW: ini adalah masalah yang sama persis dengan yang Anda alami di posting lain ini , di ServerFault: 88 detik untuk membiarkan 132M catatan perjalanan sepanjang .... jalur sihir tidak terkait mysql :-)

Damiano Verzulli
sumber
Saya mengharapkan balasan dari op.
Jnanaranjan
5
  1. Periksa mysql innodb_buffer_pool_size . Itu harus cukup besar - semakin banyak, semakin baik. Tapi jangan terlalu banyak untuk menghindari bertukar OS.

    show variables like 'innodb_buffer_pool_size'

    akan menampilkan ukuran buffer dalam byte.

  2. Periksa kueri lebih dari sekali. Proses pertama mungkin terlalu lama karena data harus dibaca dari disk ke dalam memori. Saat Anda menjalankan kueri pertama kali, data masih belum dalam buffer innodb dan harus dibaca dari disk. Yang jauh lebih lambat daripada jika data sudah dalam cache. Jadi jalankan query beberapa kali untuk memastikan itu dilayani dari cache.

  3. Nonaktifkan cache permintaan karena setiap proses yang dijalankan akan dipenuhi darinya dan akan membiaskan hasil pengujian. Ada mekanisme di MySQL, yang disebut "cache permintaan" yang dirancang untuk menyimpan kueri beserta hasilnya. Jadi kali kedua MySQL diminta untuk menjalankan kueri, ia dapat mem-bypass eksekusi dan mengambil hasil dari cache kueri.

  4. Pertimbangkan untuk menggunakan "indeks penutup":

    ALTER TABLE key_word ADD KEY IX_hashed_word_indexVal (hashed_word, indexVal);

Ini akan jauh lebih efisien, sejak saat itu MySQL dapat memenuhi permintaan kueri hanya dari indeks.

Boris
sumber