Loc*_*eyu 8 mysql myisam performance index select
我正在研究跟踪使用时间的分析系统的架构,并且需要查看特定日期范围内的总使用时间。
举一个简单的例子,这种类型的查询会经常运行:
select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
Run Code Online (Sandbox Code Playgroud)
此查询通常需要大约 7 秒的时间在一个人口密集的表上。它有大约 3500 万行,MyISAM on MySQL 在 Amazon RDS (db.m3.xlarge) 上运行。
去掉 WHERE 子句使查询只需要 4 秒,添加第二个子句 (time_off > XXX) 增加了 1.5 秒,使查询时间达到 8.5 秒。
因为我知道这些类型的查询会很常见,所以我想优化一些东西,使它们更快,理想情况下低于 5 秒。
我首先在 time_on 上添加一个索引,虽然这大大加快了 WHERE "=" 查询,但它对 ">" 查询没有影响。有没有办法创建一个可以加速 WHERE ">" 或 "<" 查询的索引?
或者如果对此类查询的性能有任何其他建议,请告诉我。
注意:我使用“diff_ms”字段作为非规范化步骤(它等于 time_off - time_on),它将聚合性能提高了大约 30%-40%。
我正在使用以下命令创建索引:
ALTER TABLE writetest_table ADD INDEX time_on (time_on) USING BTREE;
Run Code Online (Sandbox Code Playgroud)
在原始查询上运行“explain”(使用“time_on >”)表示 time_on 是一个“possible_key”,而 select_type 是“SIMPLE”。“额外”列显示“使用位置”,“类型”为“全部”。添加索引后,该表显示“time_on”是“MUL”键类型,这似乎是正确的,因为同一时间可以出现两次。
这是表架构:
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;
Run Code Online (Sandbox Code Playgroud)
更新:我根据 ypercube 的响应创建了以下索引,但这会将第一次查询的查询时间增加到 17 秒左右!
ALTER TABLE writetest_table ADD INDEX time_on__diff_ms__ix (time_on, diff_ms) ;
Run Code Online (Sandbox Code Playgroud)
更新 2:解释输出
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)
Run Code Online (Sandbox Code Playgroud)
更新 3:请求查询的结果
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)
Run Code Online (Sandbox Code Playgroud)
我想我开始明白了。
当我叫你跑的时候
SELECT time_on FROM writetest_table ORDER BY time_on LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
你说这是2015-07-13 15:11:56你的WHERE条款中的内容
当你进行查询时
select sum(diff_ms) from writetest_table;
Run Code Online (Sandbox Code Playgroud)
它执行了 3580 万行的全表扫描。
当你进行查询时
select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
Run Code Online (Sandbox Code Playgroud)
它执行了 3580 万行的完整索引扫描。
没有 WHERE 子句的查询速度更快是完全有道理的。为什么 ?
表扫描将在一次线性扫描中读取 3580 万行。
使用 WHERE 进行查询的 EXPLAIN 也发现了 3580 万行。索引扫描的行为会略有不同。虽然 BTREE 保持键的顺序,但它对于进行范围扫描来说很糟糕。在您的特定情况下,您正在执行最糟糕的范围扫描,其中的 BTREE 条目数与表中的行数相同。MySQL 必须遍历 BTREE 页(至少跨叶节点)才能读取值。此外,time_on必须按照索引指定的顺序对列进行比较。因此,非叶BTREE节点也必须被遍历。
请参阅我在 BTREE 上的帖子
Aug 06, 2013:在MySQL中,如果列X具有唯一值,那么UNIQUE索引和B树索引之间有什么区别Jun 28, 2012: MySQL 中 BTREE 的好处如果查询截至今天午夜
select sum(diff_ms) from writetest_table where time_on >= ("2015-07-14 00:00:00");
Run Code Online (Sandbox Code Playgroud)
甚至今天中午
select sum(diff_ms) from writetest_table where time_on >= ("2015-07-14 12:00:00");
Run Code Online (Sandbox Code Playgroud)
应该花费更少的时间。
故事寓意:不要使用执行等于目标表中行数的有序范围扫描的 WHERE 子句。
| 归档时间: |
|
| 查看次数: |
25455 次 |
| 最近记录: |