Masalah kinerja MySQL menggunakan kolom datetime yang diindeks

14

Saya mencoba untuk menyelesaikan masalah berikut selama sekitar satu jam sekarang dan masih belum bisa menyelesaikannya.

Oke, saya punya meja (MyISAM):

+---------+-------------+------+-----+-------------------+----------------+
| Field   | Type        | Null | Key | Default           | Extra          |
+---------+-------------+------+-----+-------------------+----------------+
| id      | int(11)     | NO   | PRI | NULL              | auto_increment |
| http    | smallint(3) | YES  | MUL | 200               |                |
| elapsed | float(6,3)  | NO   |     | NULL              |                |
| cached  | tinyint(1)  | YES  |     | NULL              |                |
| ip      | int(11)     | NO   |     | NULL              |                |
| date    | timestamp   | NO   | MUL | CURRENT_TIMESTAMP |                |
+---------+-------------+------+-----+-------------------+----------------+

Tolong jangan pedulikan indeksnya, saya telah berusaha mencari solusi. Sekarang, inilah pertanyaan saya.

SELECT http,
COUNT( http )  AS count 
FROM reqs
WHERE DATE(date) >= cast(date_sub(date(NOW()),interval 24 hour) as datetime)
GROUP BY http
ORDER BY count;

tabel ini menyimpan informasi tentang permintaan web masuk sehingga basis datanya agak besar.

+-----------+
| count(id) |
+-----------+
|    782412 |
+-----------+

perhatikan bahwa tidak ada cara yang lebih baik untuk mengatur kunci primer karena kolom id akan menjadi satu-satunya pengidentifikasi unik yang saya miliki. Permintaan yang disebutkan di atas membutuhkan waktu sekitar 0,6-1,6 detik untuk dijalankan.

Indeks mana yang pintar? Saya pikir tanggal pengindeksan akan memberi saya kardinalitas "buruk" dan karenanya MySQL tidak akan menggunakannya. http juga merupakan pilihan yang buruk karena hanya ada sekitar 20 nilai yang berbeda.

Terima kasih atas bantuannya!

Pembaruan 1 Saya telah menambahkan indeks pada (http, tanggal) seperti yang disarankan ypercube:

mysql> CREATE INDEX httpDate ON reqs (http, date);

dan menggunakan kueri, tetapi kinerjanya sama buruknya. Indeks yang ditambahkan:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| reqs  |          0 | PRIMARY  |            1 | id          | A         |      798869 |     NULL | NULL   |      | BTREE      |         |
| reqs  |          1 | httpDate |            1 | http        | A         |          19 |     NULL | NULL   | YES  | BTREE      |         |
| reqs  |          1 | httpDate |            2 | date        | A         |       99858 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

dan MENJELASKAN

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| id | select_type        | table | type  | possible_keys | key      | key_len | ref  | rows  | Extra                                                     |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
|  1 | PRIMARY            | r     | range | NULL          | httpDate | 3       | NULL |    20 | Using index for group-by; Using temporary; Using filesort |
|  2 | DEPENDENT SUBQUERY | ri    | ref   | httpDate      | httpDate | 3       | func | 41768 | Using where; Using index                                  |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+

Versi server MySQL:

mysql> SHOW VARIABLES LIKE "%version%";
+-------------------------+---------------------+
| Variable_name           | Value               |
+-------------------------+---------------------+
| protocol_version        | 10                  |
| version                 | 5.1.73              |
| version_comment         | Source distribution |
| version_compile_machine | x86_64              |
| version_compile_os      | redhat-linux-gnu    |
+-------------------------+---------------------+
5 rows in set (0.00 sec)
Robin Heller
sumber
Bisakah Anda juga menambahkan versi mysql dan apa mesin dari tabel? (myisam or innodb)
ypercubeᵀᴹ
MyISAM dan 5.1.73 - semua detail sekarang ada di pos.
Robin Heller
Saya khawatir ini mungkin ada hubungannya dengan httpkolom yang dapat dibatalkan. Saya akan menyelidiki besok, jika saya punya waktu.
ypercubeᵀᴹ
Saya khawatir itu mungkin ada hubungannya dengan kolom http menjadi nullable. Saya akan menyelidiki besok, jika saya punya waktu. Anda dapat menguji dengan membuat tabel yang identik (kecuali dengan http NOT NULL) dan menyalin semua data ke dalamnya (kecuali baris dengan http NULL tentu saja.)
ypercubeᵀᴹ
Mengubahnya menjadi NOT NULL (yang sepenuhnya mungkin, saya tidak keberatan ketika membuat tabel) meningkatkan kinerja sekitar ~ 1s - 1.6s untuk kueri (permintaan saya). Terima kasih atas usaha Anda sampai sekarang.
Robin Heller

Jawaban:

10

Saya punya tiga saran

SARAN # 1: Tulis ulang kueri

Anda harus menulis ulang kueri sebagai berikut

SELECT http,
COUNT( http )  AS count 
FROM reqs
WHERE date >= ( DATE(NOW() - INTERVAL 1 DAY) + INTERVAL 0 SECOND )
GROUP BY http
ORDER BY count;

atau

SELECT * FROM
(
    SELECT http,
    COUNT( http )  AS count 
    FROM reqs
    WHERE date >= ( DATE(NOW() - INTERVAL 1 DAY) + INTERVAL 0 SECOND )
    GROUP BY http
) A ORDER BY count;

WHERE seharusnya tidak memiliki fungsi di kedua sisi dari tanda sama dengan. Memiliki tanggal di sisi kiri tanda sama dengan membuatnya lebih mudah bagi Pengoptimal Permintaan untuk menggunakan indeks terhadapnya.

SARAN # 2: Indeks Pendukung

Saya juga menyarankan indeks yang berbeda

ALTER TABLE reqs ADD INDEX date_http_ndx (date,http); -- not (http,date) 

Saya menyarankan urutan kolom ini karena semua dateentri akan bersebelahan dalam indeks. Kemudian, kueri hanya mengumpulkan httpnilai tanpa melewatkan celah http.

SARAN # 3: Penyangga Kunci Lebih Besar (Opsional)

MyISAM hanya menggunakan caching indeks. Karena kueri tidak boleh menyentuh .MYDfile, Anda harus menggunakan Buffer Kunci MyISAM sedikit lebih besar.

Untuk mengaturnya ke 256 juta

SET @newsize = 1024 * 1024 * 256;
SET GLOBAL key_buffer_size = @newsize;

Kemudian, atur my.cnf

[mysqld]
key_buffer_size = 256M

Restart MySQL tidak diperlukan

Cobalah !!!

RolandoMySQLDBA
sumber
Saya mencoba pertanyaan yang Anda berikan kepada saya. # 1 memiliki kinerja yang sama baiknya dengan saran saya atau saran saya yang lain, yang kedua benar-benar berkinerja lebih buruk. Hal yang sama untuk Indeks Pendukung - membuat kinerja turun sekitar 75 persen. Saya akan mencoba buffer kunci yang lebih besar sekarang, terima kasih!
Robin Heller
Saya menerima jawaban Anda meskipun itu tidak memperbaiki masalah, dengan buffer kunci yang lebih besar namun kinerjanya agak lebih baik. Menutup ini sebagai solusi terbaik dari semua yang diberikan. Terima kasih!
Robin Heller
Agar Saran # 2 berfungsi, mungkin perlu menambahkan "USE INDEX" atau "FORCE INDEX" dalam kueri, setidaknya itulah yang harus saya lakukan untuk mempercepat kueri saya setelah membuat indeks seperti itu.
Johano Fierra
-2

Ubah tipe kolom tanggal Anda menjadi bilangan bulat. Simpan tanggal sebagai tanggal Unix dalam bilangan bulat. Timestamp Jauh lebih besar dari int. Anda akan mendapatkan beberapa dari itu.

apachebeard
sumber
2
Apa Anda sedang bercanda? Keduanya INTdan TIMESTAMPmembutuhkan 4 byte.
ypercubeᵀᴹ
2
Tidak disebutkan bahwa Anda kehilangan semua fungsi datetime ketika Anda menyimpan tanggal atau cap waktu sebagai bilangan bulat.
ypercubeᵀᴹ