Saya sedang mengerjakan skema untuk sistem analitik yang melacak waktu penggunaan, dan ada kebutuhan untuk melihat total waktu penggunaan dalam rentang tanggal tertentu.
Untuk memberikan contoh sederhana, jenis kueri ini akan sering dijalankan:
select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
Kueri ini biasanya memakan waktu sekitar 7 detik pada tabel yang padat. Ini memiliki ~ 35 juta baris, MyISAM di MySQL berjalan di Amazon RDS (db.m3.xlarge).
Menyingkirkan klausa WHERE membuat kueri hanya membutuhkan waktu 4 detik, dan menambahkan klausa kedua (time_off> XXX) menambahkan 1,5 detik tambahan, menjadikan waktu kueri menjadi 8,5 detik.
Karena saya tahu jenis pertanyaan ini akan umum dilakukan, saya ingin mengoptimalkan beberapa hal sehingga lebih cepat, idealnya di bawah 5 detik.
Saya mulai dengan menambahkan indeks pada time_on, dan meskipun secara drastis mempercepat permintaan WHERE "=", itu tidak berpengaruh pada kueri ">". Apakah ada cara untuk membuat indeks yang akan mempercepat permintaan WHERE ">" atau "<"?
Atau jika ada saran lain tentang kinerja jenis permintaan ini, beri tahu saya.
Catatan: Saya menggunakan bidang "diff_ms" sebagai langkah denormalisasi (sama dengan time_off - time_on) yang meningkatkan kinerja agregasi sekitar 30% -40%.
Saya membuat indeks dengan perintah ini:
ALTER TABLE writetest_table ADD INDEX time_on (time_on) USING BTREE;
Menjalankan "jelaskan" pada permintaan asli (dengan "time_on>") mengatakan time_on adalah "mungkin_kunci" dan select_type adalah "SIMPLE". Kolom "ekstra" mengatakan "Menggunakan tempat", dan "ketik" adalah "SEMUA". Setelah indeks ditambahkan, tabel mengatakan "time_on" adalah tipe kunci "MUL", yang tampaknya benar karena waktu yang sama dapat hadir dua kali.
Berikut adalah skema tabel:
CREATE TABLE `writetest_table` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sessionID` int(11) DEFAULT NULL,
`time_on` timestamp NULL DEFAULT NULL,
`time_off` timestamp NULL DEFAULT NULL,
`diff_ms` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `time_on` (`time_on`)
) ENGINE=MyISAM AUTO_INCREMENT=50410902 DEFAULT CHARSET=latin1;
PEMBARUAN: Saya membuat indeks berikut berdasarkan respons ypercube, tetapi ini meningkatkan waktu kueri untuk kueri pertama menjadi sekitar 17 detik!
ALTER TABLE writetest_table ADD INDEX time_on__diff_ms__ix (time_on, diff_ms) ;
UPDATE 2: MENJELASKAN output
mysql> explain select sum(diff_ms) from writetest_table where time_on > '2015-07-13 15:11:56';
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| 1 | SIMPLE | writetest_table_old | index | time_on__diff_ms__ix | time_on__diff_ms__ix | 10 | NULL | 35831102 | Using where; Using index |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
1 row in set (0.00 sec)
Pembaruan 3: hasil dari permintaan yang diminta
mysql> SELECT time_on FROM writetest_table ORDER BY time_on LIMIT 1;
+---------------------+
| time_on |
+---------------------+
| 2015-07-13 15:11:56 |
+---------------------+
1 row in set (0.01 sec)
time_on
dandiff_ms
)? Apa yang terjadi jika Anda menambahkan dalam kueriWHERE ... AND diff_ms IS NOT NULL
?SELECT COUNT(*), COUNT(diff_ms) FROM writetest_table;
writetest_table_old
" sementara permintaan memilikifrom writetest_table
. Apakah itu salah ketik atau Anda menjalankan kueri di tabel yang berbeda?Jawaban:
Saya pikir saya mulai mengerti.
Ketika saya meminta Anda untuk lari
Anda mengatakan itu
2015-07-13 15:11:56
yang Anda miliki diWHERE
klausa AndaKetika Anda melakukan kueri
Itu melakukan pemindaian tabel penuh 35,8 juta baris.
Ketika Anda melakukan kueri
Itu melakukan pemindaian indeks penuh 35,8 juta baris.
Sangat masuk akal bahwa permintaan tanpa klausa WHERE lebih cepat. Mengapa
Pemindaian tabel akan membaca 35,8 juta baris dalam satu lintasan linear.
EXPLAIN pada kueri dengan WHERE juga menghasilkan 35,8 juta baris. Pemindaian indeks akan berperilaku sedikit berbeda. Sementara BTREE menjaga urutan kunci, mengerikan untuk melakukan pemindaian jangkauan. Dalam kasus khusus Anda, Anda melakukan pemindaian jangkauan terburuk yang mungkin, yang akan memiliki jumlah entri BTREE yang sama karena ada baris dalam tabel. MySQL harus melintasi halaman BTREE (setidaknya melintasi node daun) untuk membaca nilainya. Selain itu,
time_on
kolom harus dibandingkan sepanjang jalan dalam urutan yang ditentukan oleh indeks. Oleh karena itu, node BTREE non-daun harus dilalui juga.Silakan lihat posting saya di BTREEs
Aug 06, 2013
: Di MySQL jika kolom X memiliki nilai unik apa perbedaan antara indeks UNIQUE dan indeks B-TreeJun 28, 2012
: Manfaat BTREE di MySQLJika kueri per tengah malam hari ini
atau bahkan siang hari ini
itu harus memakan waktu lebih sedikit.
MORAL OF THE STORY: Jangan gunakan klausa WHERE yang melakukan pemindaian rentang yang sama dengan jumlah baris dalam tabel target.
sumber
Untuk permintaan spesifik:
indeks pada
(time_on, diff_ms)
akan menjadi pilihan terbaik. Jadi, jika kueri berjalan cukup sering atau efisiensinya sangat penting untuk aplikasi Anda, tambahkan indeks ini:(Tidak terkait dengan pertanyaan)
Dan sungguh, ubah mesin tabel ke InnoDB. Ini tahun 2015 dan pemakaman MyISAM beberapa tahun yang lalu.
(/ kata-kata kasar)
sumber
ALTER TABLE writetest_table DROP INDEX time_on;
, 2) menjalankanANALYZE TABLE writetest_table;
, dan 3) menjalankan kembali kueri. Apakah waktu kembali ke 7 detik?EXPLAIN select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
. Apakah indeks baru digunakan? Jika tidak digunakan, saya akan mengatakan itu adalah populasi kunci Anda, terutama jika time_on awal Anda hanya beberapa hari yang lalu. Karena jumlah baris meningkat dengan hari yang lebih berbeda, distribusi kunci harus turun dan EXPLAIN akan lebih baik .