使用索引日期时间列的MySQL性能问题


15

我试图解决以下问题大约一个小时,但仍然没有解决任何问题。

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

EXPLAIN

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

您还可以添加mysql版本吗,表的引擎是什么?(的myisam或innodb的)
ypercubeᵀᴹ

MyISAM和5.1.73-现在发布所有详细信息。
罗宾·海勒2014年

恐怕可能与该http列可为空有关。如果有时间,我明天会调查。
ypercubeᵀᴹ

恐怕可能与http列可为空有关。如果有时间,我明天会调查。您可以通过以下方式进行测试:创建一个相同的表(除外http NOT NULL),然后将所有数据复制到该表中(当然,除了带有http NULL的行之外)
。–ypercubeᵀᴹ2014年

将其更改为NOT NULL(这完全有可能,创建表时我不太在意)将查询(我的查询)的性能提高到大约1s-1.6s。感谢您到目前为止的努力。
罗宾·海勒

Answers:


10

我有三个建议

建议#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

试试看 !!!


我尝试了您给我的查询。#1的表现与其他建议或我的建议差不多,而第二个实际上却表现较差。支持指标也一样-使效果下降约75%。我现在将尝试更大的密钥缓冲区,无论如何,谢谢您!
罗宾·海勒

我接受了您的答案,尽管它不能解决问题,但具有较大的密钥缓冲区,但性能较好。关闭它,因为它是所有给定的最佳解决方案。谢谢!
罗宾·海勒

为了使建议2起作用,可能有必要在查询中添加“ USE INDEX”或“ FORCE INDEX”,至少这是我创建索引后要加快查询速度所必须要做的。
Johano Fierra

-2

将您的日期列类型更改为整数。以整数形式将日期存储为Unix日期。时间戳比int大很多。您会从中得到一些帮助。


2
你在开玩笑吗?双方INTTIMESTAMP需要4个字节。
ypercubeᵀᴹ

2
更不用说在将日期或时间戳记存储为整数时会丢失所有datetime函数。
ypercubeᵀᴹ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.