Mysql

使用索引日期時間列的 MySQL 性能問題

  • September 7, 2017

我現在嘗試解決以下問題大約一個小時,但仍然沒有進一步解決。

好的,我有一張桌子(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 |                |
+---------+-------------+------+-----+-------------------+----------------+

請不要介意索引,我一直在嘗試尋找解決方案。現在,這是我的查詢。

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;

該表儲存有關傳入 Web 請求的資訊,因此它是一個相當大的數據庫。

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

請注意,沒有更好的方法來設置主鍵,因為id列將是我擁有的唯一唯一標識符。上面提到的查詢執行大約需要 0.6-1.6 秒。

哪個索引會很聰明?我認為索引日期會給我“壞”的基數,因此 MySQL 不會使用它。http也是一個糟糕的選擇,因為只有大約 20 種不同的可能值。

謝謝你的幫助!

更新 1我按照 ypercube 的建議在*(http, date)上添加了一個索引:*

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

並使用了他的查詢,但它的表現同樣糟糕。添加的索引:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 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      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

解釋

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| 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                                  |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+

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)

我有三個建議

建議#1:重寫查詢

您應該按如下方式重寫查詢

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

要麼

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 不應該在等號兩邊都有函式。在等號的左側有日期使查詢優化器更容易使用索引。

建議#2:支持索引

我還建議使用不同的索引

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

我建議使用這種列順序,因為這些date條目在索引中都是連續的。然後,查詢只是收集http值而不跳過http.

建議 #3:更大的密鑰緩衝區(可選)

MyISAM 只使用索引記憶體。由於查詢不應觸及.MYD文件,因此您應該使用稍大的 MyISAM 密鑰緩衝區。

將其設置為 256M

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

然後,將其設置在my.cnf

[mysqld]
key_buffer_size = 256M

不需要重啟 MySQL

試一試 !!!

引用自:https://dba.stackexchange.com/questions/73598