我的表有超过800,000行。我需要提高查询的性能,该查询在一个时间间隔内提取行。
我的表:
CREATE TABLE `bets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
...
`stamp_end` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `bets_stamp_end_index` (`stamp_end`)
) ENGINE=InnoDB AUTO_INCREMENT=875534 DEFAULT CHARSET=utf8;
有时索引会与此查询一起使用:
EXPLAIN SELECT * FROM bets WHERE
bets.stamp_end BETWEEN '2016-05-01 00:00:00' AND '2016-06-01 00:00:00';
+----+-------------+-------+-------+----------------------+----------------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+----------------------+----------------------+---------+------+--------+-------------+
| 1 | SIMPLE | bets | **range** | bets_stamp_end_index | bets_stamp_end_index | 5 | NULL | **158210** | Using where |
+----+-------------+-------+-------+----------------------+----------------------+---------+------+--------+-------------+
当我编写此查询时,从不使用索引:
EXPLAIN SELECT * FROM bets WHERE
stamp_end >= DATE_SUB(DATE(NOW()), INTERVAL 56 DAY);
+----+-------------+-------+------+----------------------+------+---------+------+--------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+----------------------+------+---------+------+--------+-----------------------------+
| 1 | SIMPLE | bets | **ALL** | bets_stamp_end_index | NULL | NULL | NULL | **857651** | Using where; Using filesort |
+----+-------------+-------+------+----------------------+------+---------+------+--------+-----------------------------+
注意: 我已经运行了OPTIMIZE TABLE。第一个查询仅对某些日期间隔使用索引。
为什么不使用索引?任何解决方案?
答案 0 :(得分:0)
使用索引意味着在索引BTree和数据BTree之间来回弹跳。
如果需要访问的表少于约20%,那么弹跳是值得的。
如果需要访问更多内容,扫描表格实际上会更快,过滤掉WHERE
子句要排除的内容。
(20%是不精确的,优化程序并不总能做出正确的“截止”。)
答案 1 :(得分:-1)
<强>样品强>
MariaDB [bb]> CREATE TABLE `bets` (
-> `id` int(11) NOT NULL AUTO_INCREMENT,
-> `user_id` int(11) NOT NULL,
->
-> `stamp_end` timestamp NULL DEFAULT NULL,
-> PRIMARY KEY (`id`),
-> KEY `bets_stamp_end_index` (`stamp_end`)
-> ) ENGINE=InnoDB AUTO_INCREMENT=875534 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.13 sec)
MariaDB [bb]>
MariaDB [bb]> INSERT INTO bets (stamp_end)
-> SELECT DATE(now()) - INTERVAL seq HOUR FROM seq_1_to_300000;
Query OK, 300000 rows affected, 1 warning (3.27 sec)
Records: 300000 Duplicates: 0 Warnings: 1
MariaDB [bb]>
MariaDB [bb]> EXPLAIN SELECT * FROM bets WHERE
-> bets.stamp_end BETWEEN '2016-05-01 00:00:00' AND '2016-06-01 00:00:00';
+------+-------------+-------+-------+----------------------+----------------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+----------------------+----------------------+---------+------+------+-----------------------+
| 1 | SIMPLE | bets | range | bets_stamp_end_index | bets_stamp_end_index | 5 | NULL | 432 | Using index condition |
+------+-------------+-------+-------+----------------------+----------------------+---------+------+------+-----------------------+
1 row in set (0.03 sec)
MariaDB [bb]>
MariaDB [bb]> EXPLAIN SELECT * FROM bets WHERE
-> stamp_end >= DATE_SUB(DATE(NOW()), INTERVAL 56 DAY);
+------+-------------+-------+-------+----------------------+----------------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+-------+----------------------+----------------------+---------+------+------+-----------------------+
| 1 | SIMPLE | bets | range | bets_stamp_end_index | bets_stamp_end_index | 5 | NULL | 1343 | Using index condition |
+------+-------------+-------+-------+----------------------+----------------------+---------+------+------+-----------------------+
1 row in set (0.00 sec)
MariaDB [bb]>