MySQL忽略了时间戳列上的索引

时间:2016-05-19 11:03:10

标签: mysql database performance

我的表有超过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。第一个查询仅对某些日期间隔使用索引。

为什么不使用索引?任何解决方案?

2 个答案:

答案 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]>