VarChar Pengindeksan MySQL

10

Saya mencoba mengindeks blogentriesdatabase saya untuk kinerja yang lebih baik tetapi menemukan masalah.

Berikut adalah strukturnya:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Kueri seperti berikut ini menggunakan indeks dengan benar:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | select_type | meja | ketik | mungkin_kunci | kunci | key_len | ref | baris | Ekstra |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | SEDERHANA | blogentries | indeks | NULL | UTAMA | 114 | NULL | 126 | Menggunakan indeks |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Namun, ketika saya menambahkan entry_idke dalam SELECTkueri itu menggunakan filesort

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | meja | ketik | mungkin_kunci | kunci | key_len | ref | baris | Ekstra |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | SEDERHANA | blogentries | SEMUA | NULL | NULL | NULL | NULL | 126 | Menggunakan filesort |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Saya bertanya-tanya mengapa ini terjadi dan bagaimana saya bisa menghindarinya? Apakah karena VarChar, dan itu harus diubah menjadi sesuatu yang lain?

Saya mencoba agar semua pertanyaan saya menggunakan indeks karena saya menjalankan nilai yang tinggi Handler_read_rnddan Handler_read_rnd_next.

Jika Anda memerlukan info lain, saya dapat mempostingnya juga.


sumber
filesort berarti melakukan sortir pada disk.
Kermit
Coba tambahkan WHERE 1=1ke permintaan kedua Anda.
Kermit
Versi MySQL yang mana ini? Berapa ukuran buffer sortir Anda ( SELECT @@sort_buffer_size)?
@njk filesort adalah hasil dari bagian 'ORDER BY' dari permintaan
1
@TashPemhiwa Belum tentu, lihat pernyataan pertama.
Kermit

Jawaban:

6

Karena Anda tidak memiliki WHEREklausa di salah satu kueri, Anda mengembalikan semua baris dalam kedua kasus, jadi saya pikir penggunaan atau tidak menggunakan indeks akan berdampak sangat kecil pada kinerja dalam contoh ini.

Joe Stefanelli
sumber
Tentunya MySQL harus menggunakan indeks untuk ORDER BY?
eggyal
@eggyal Tidak jika terlalu besar untuk memori.
Kermit
@ njk: Itu tidak masuk akal ... itu bisa melintasi indeks, secara berurutan, tanpa perlu memuat semuanya ke dalam memori. Hasil akan diurutkan tanpa perlu melakukan filesort.
eggyal
@eggyal Saya akan mempertanyakan ukuran varchar(5000).
Kermit
@ njk: Tapi kolom itu tidak ada dalam indeks atau digunakan dalam pengurutan.
eggyal
2

Seperti yang didokumentasikan dalam ORDER BYOptimasi :

Untuk kueri lambat yang filesorttidak digunakan, coba turunkan max_length_for_sort_datake nilai yang sesuai untuk memicu a filesort.

Dalam artikel blognya Apa sebenarnya read_rnd_buffer_size , Peter Zaitsev menjelaskan:

Bagi saya ini berarti sejak MySQL 4.1 opsi ini digunakan dalam berbagai kasus yang sempit - jika Anda mengambil beberapa bidang (kurang dari max_length_for_sort_data ) data harus disimpan dalam penyortir penyortiran dan mengurutkan file sehingga tidak perlu lagi read_rnd_buffer, jika kolom yang dipilih panjang sehingga lebih panjang dari max_length_for_sort_data sering berarti ada beberapa kolom TEXT / BLOB di antara mereka. Akan tetapi akan digunakan jika ada banyak kolom atau ada kolom VARCHAR panjang yang digunakan - hanya dibutuhkan beberapa UTF8 VARCHAR (255) untuk membuat baris yang lebih panjang dari max_length_for_sort_data dalam presentasi statisnya.

Ini menunjukkan bahwa max_length_for_sort_dataini adalah batas ukuran total kolom yang dipilih, di mana a filesortakan digunakan alih-alih pengurutan berbasis indeks.

Dalam kasus Anda, memilih entry_id(5002 byte) mengambil ukuran total dari nilai default 1KiB variabel ini dan oleh karena filesortitu digunakan. Untuk menaikkan batas menjadi 8KiB, Anda dapat melakukan:

SET SESSION max_length_for_sort_data = 8192;
eggyal
sumber
Saya memiliki tabel dengan pengaturan yang sangat mirip dengan yang ini, dan pengaturan ini tampaknya tidak memicu perubahan dalam penggunaan filesort.
@muffinista: Itu menarik. Saya kira itu mungkin terkait dengan beberapa pengaturan buffer lainnya, per jawaban RolandoMySQLDBA ?
eggyal
2

Anda mendapatkan banyak tanggapan menarik di sini, tetapi tidak ada yang menjawab pertanyaan dengan tepat - mengapa ini terjadi? Seperti yang saya pahami, ketika kueri SELECT berisi data panjang variabel di MySQL, dan tidak ada indeks yang cocok dengan SEMUA kolom yang diminta, itu akan selalu menggunakan filesort. Ukuran data tidak terlalu relevan di sini. Sulit untuk menemukan jawaban langsung untuk pertanyaan ini dalam dokumentasi MySQL, tetapi di sini ada posting blog yang bagus di mana seseorang mengalami masalah yang sangat mirip dengan Anda.

Lihat juga: 10 Tips untuk Mengoptimalkan Permintaan MySQL (Itu tidak payah) .

Jadi, jika layak untuk memiliki indeks pada entry_id, maka Anda bisa menambahkannya dan siap. Tapi saya ragu itu pilihan, jadi apa yang harus dilakukan?

Apakah Anda harus melakukan sesuatu tentang ini adalah pertanyaan terpisah. Sangat penting untuk mengetahui bahwa 'filesort' tidak disebutkan dengan baik di MySQL - itu benar-benar hanya nama algoritma yang digunakan untuk mengurutkan permintaan khusus ini, dan dalam banyak kasus, semacam itu sebenarnya akan terjadi dalam memori. Jika Anda tidak berharap meja ini tumbuh banyak, itu mungkin bukan masalah besar.

Di sisi lain, jika tabel ini akan memiliki sejuta baris di dalamnya, Anda mungkin memiliki masalah. Jika Anda perlu mendukung pagination pertanyaan di tabel ini, maka Anda mungkin memiliki masalah kinerja yang sangat serius di sini. Dalam hal itu, mempartisi data panjang variabel Anda ke tabel baru, dan melakukan JOIN untuk mengambilnya adalah optimasi yang valid untuk dipertimbangkan.

Berikut adalah beberapa jawaban lain pada SO yang membahas pertanyaan ini:

Komunitas
sumber
Kueri pertama OP " berisi data panjang variabel di MySQL, dan tidak ada indeks yang cocok dengan SEMUA kolom yang diminta ", namun filesortternyata tidak digunakan dalam kasus itu. Saya juga berpikir bahwa bahkan menyortir meja kecil dalam memori saja bisa terbukti menjadi hit kinerja yang tidak dapat diterima: misalnya jika kueri dilakukan banyak (dan tabel berubah sehingga cache tidak dapat digunakan).
eggyal
Saya tidak punya waktu untuk mengujinya, tetapi saya ingin tahu apakah ini dipicu dengan memiliki VARCHAR yang membutuhkan 2 byte untuk menyimpan panjangnya seperti yang ditentukan dalam dev.mysql.com/doc/refman/5.1/en/char. html - jadi kueri pertama cocok dalam batas itu tetapi yang kedua tidak.
0

Coba tambahkan WHEREklausa ke dalam kueri Anda.

Indeks dapat digunakan bahkan jika ORDER BY tidak cocok dengan indeks dengan tepat, selama semua bagian indeks yang tidak digunakan dan semua kolom ORDER BY tambahan adalah konstanta dalam klausa WHERE . Dalam beberapa kasus, MySQL tidak dapat menggunakan indeks untuk menyelesaikan ORDER BY , meskipun masih menggunakan indeks untuk menemukan baris yang cocok dengan klausa WHERE .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


sumber
Tetapi dalam kasus ORDER BY ini persis cocok dengan indeks, sehingga tidak perlu memiliki WHEREklausa.
eggyal
Saya memiliki klausa "di mana" pada kueri aktual di situs, jadi saya tahu bukan itu penyebab pengurutan file. Saya bertanya-tanya apakah ini penggunaan varchar?
0

Sejauh pengetahuan saya varchar hanya bisa menampung maksimum 8000 byte yang kira-kira 4000 karakter. Dengan demikian, 5000 tampaknya melebihi batas penyimpanan, dan dalam hal ini mungkin alasan mengapa penyortiran semakin kacau.

"varchar [(n | max)] Variabel-panjang, data karakter non-Unicode. n dapat berupa nilai dari 1 hingga 8.000. Maks menunjukkan bahwa ukuran penyimpanan maksimum adalah 2 ^ 31-1 byte. Ukuran penyimpanan adalah aktual panjang data yang dimasukkan + 2 byte. Data yang dimasukkan bisa panjangnya 0 karakter. Sinonim SQL-2003 untuk varchar beragam karakter atau karakter bervariasi. "

Semoga ini menjawab pertanyaan Anda


sumber
Seperti yang didokumentasikan di bawah The CHARand VARCHARType : " Nilai dalam kolom VARCHAR adalah string panjang variabel. Panjangnya dapat ditentukan sebagai nilai dari 0 hingga 255 sebelum MySQL 5.0.3, dan 0 hingga 65.535 dalam versi 5.0.3 dan versi yang lebih baru. Efektif panjang maksimum a VARCHARdi MySQL 5.0.3 dan yang lebih baru tunduk pada ukuran baris maksimum (65.535 byte, yang dibagi di antara semua kolom) dan set karakter yang digunakan. "
eggyal
0

Anda hanya memiliki 126 baris di meja Anda. Bahkan jika setiap baris berukuran maksimal 5KB, itu berarti bahwa ukuran total untuk membaca dari disk hanya sekitar 600KB - ini tidak banyak. Sejujurnya, jumlahnya sangat kecil, mungkin kurang dari ukuran cache sebagian besar drive disk modern.

Sekarang, jika server perlu mengambil data Anda untuk memenuhi permintaan Anda, operasi yang paling mahal adalah membacanya dari disk. Tapi, membacanya sesuai urutan indeks TIDAK selalu cara tercepat untuk melakukannya, terutama ketika jumlah data sangat kecil.

Dalam kasus Anda, JAUH lebih efisien untuk membaca seluruh data tabel dari disk sebagai blok tunggal ke dalam memori (mungkin hanya dalam satu operasi membaca disk atau mencari), dan kemudian mengurutkannya dalam RAM untuk memenuhi ORDER BY, yang instan dibandingkan dengan disk baca operasi. Jika server membaca data Anda sesuai dengan indeks, itu harus mengeluarkan hingga 126 (oops!) Operasi baca, mencari bolak-balik dalam file data yang sama berkali-kali.

Dengan kata lain, pemindaian berurutan TIDAK selalu merupakan hal yang buruk, dan mysql tidak selalu bodoh. Jika Anda mencoba memaksa mysql untuk menggunakan indeks itu, kemungkinan besar itu akan bekerja lebih lambat daripada pemindaian berurutan yang Anda miliki saat ini.

Dan alasan mengapa itu menggunakan indeks ketika bidang 5KB tidak dimasukkan adalah karena data yang diambil tidak membentuk 99% dari data dalam tabel. Saat Anda memasukkan bidang 5KB Anda, sekarang kueri harus membaca 99% data, dan lebih murah untuk membaca semuanya dan mengurutkannya dalam memori sesudahnya.

mvp
sumber
Sepertinya Anda mengacaukan sejumlah hal dari Cara Menghindari Pemindaian Tabel Penuh , yang berkaitan dengan penggunaan indeks dalam JOINkondisi dan WHEREklausa yang memuaskan , bukan ORDER BYklausa.
eggyal
Justru sebaliknya. Dalam hal ini pemindaian tabel penuh adalah hal yang BAIK hanya karena LEBIH CEPAT daripada membaca dengan urutan indeks.
0

Versi MySQL apa yang Anda gunakan?

DALAM 5.1, saya mencoba mengatur skenario Anda dan mengisi beberapa data dummy. Menggunakan SQL yang Anda berikan, saya hanya mendapatkan pemindaian tabel setiap kali sesuai dengan EXPLAIN. Secara default saat Anda menggunakan pesanan oleh MYSQL resort ke filesort bahkan jika indeks utama digunakan dalam urutan oleh.


sumber