Saya mencoba mengindeks blogentries
database 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_id
ke dalam SELECT
kueri 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_rnd
dan Handler_read_rnd_next
.
Jika Anda memerlukan info lain, saya dapat mempostingnya juga.
WHERE 1=1
ke permintaan kedua Anda.SELECT @@sort_buffer_size
)?Jawaban:
Karena Anda tidak memiliki
WHERE
klausa 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.sumber
ORDER BY
?varchar(5000)
.Seperti yang didokumentasikan dalam
ORDER BY
Optimasi :Dalam artikel blognya Apa sebenarnya read_rnd_buffer_size , Peter Zaitsev menjelaskan:
Ini menunjukkan bahwa
max_length_for_sort_data
ini adalah batas ukuran total kolom yang dipilih, di mana afilesort
akan 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 karenafilesort
itu digunakan. Untuk menaikkan batas menjadi 8KiB, Anda dapat melakukan:sumber
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:
sumber
filesort
ternyata 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).Coba tambahkan
WHERE
klausa ke dalam kueri Anda.http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
sumber
ORDER BY
ini persis cocok dengan indeks, sehingga tidak perlu memilikiWHERE
klausa.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
CHAR
andVARCHAR
Type : " 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 aVARCHAR
di 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. "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.
sumber
JOIN
kondisi danWHERE
klausa yang memuaskan , bukanORDER BY
klausa.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