如何解决MySQL:范围和顺序的查询索引
我正在尝试获取包含range
和ORDER BY ... DESC
的查询以使用索引。
如果删除ORDER BY
并仅使用range
,则使用索引。
如果我删除range
并仅使用ORDER BY
,也会使用索引。
表格:
CREATE TABLE `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,`service` varchar(260) COLLATE utf8mb4_unicode_ci NOT NULL,`time` datetime NOT NULL,`country` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,`city` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`country_code` char(2) COLLATE utf8mb4_unicode_ci NOT NULL,`issue` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`latitude` double DEFAULT NULL,`longitude` double DEFAULT NULL,PRIMARY KEY (`id`),KEY `service_time_idx` (`service`,`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
使用range
和ORDER BY
查询(不使用索引):
MariaDB [m]> explain select service,time,country,city,country_code,issue,latitude,longitude
from messages
where service = 'myservice'
and time BETWEEN DATE_SUB( NOW(),INTERVAL 24 HOUR ) AND NOW()
order by time desc;
+------+-------------+---------------------------+-------+------------------+------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------------------------+-------+------------------+------------------+---------+------+------+-------------+
| 1 | SIMPLE | messages | range | service_time_idx | service_time_idx | 1047 | NULL | 1 | Using where |
+------+-------------+---------------------------+-------+------------------+------------------+---------+------+------+-------------+
仅使用range
(使用索引)进行查询。
MariaDB [m]> explain select service,INTERVAL 24 HOUR ) AND NOW();
+------+-------------+---------------------------+-------+------------------+------------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------------------------+-------+------------------+------------------+---------+------+------+-----------------------+
| 1 | SIMPLE | messages | range | service_time_idx | service_time_idx | 1047 | NULL | 1 | Using index condition |
+------+-------------+---------------------------+-------+------------------+------------------+---------+------+------+-----------------------+
1 row in set (0.004 sec)
仅使用ORDER BY
(使用索引)进行查询:
MariaDB [m]> explain select service,longitude
from messages
where service = 'myservice'
and time = '2020-10-03 09:51:25'
order by time desc;
+------+-------------+---------------------------+------+------------------+------------------+---------+-------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------------------------+------+------------------+------------------+---------+-------------+------+-----------------------+
| 1 | SIMPLE | messages | ref | service_time_idx | service_time_idx | 1047 | const,const | 1 | Using index condition |
+------+-------------+---------------------------+------+------------------+------------------+---------+-------------+------+-----------------------+
1 row in set,1 warning (0.001 sec)
奇怪的是,如果我只在range
和ORDER BY
列上进行选择,那么我可以使查询同时与service
和time
一起使用:
MariaDB [m]> explain select service,time from messages
where service = 'myservice'
and time BETWEEN DATE_SUB( NOW(),INTERVAL 24 HOUR ) AND NOW()
order by time desc;
+------+-------------+---------------------------+------+------------------+------------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+---------------------------+------+------------------+------------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | messages | ref | service_time_idx | service_time_idx | 1042 | const | 1 | Using where; Using index |
+------+-------------+---------------------------+------+------------------+------------------+---------+-------+------+--------------------------+
1 row in set (0.002 sec)
MariaDB [m]> EXPLAIN FORMAT=JSON select service,longitude
from messages
where service = 'myservice'
and time BETWEEN DATE_SUB( NOW(),INTERVAL 24 HOUR ) AND NOW()
order by time desc;
{
"query_block": {
"select_id": 1,"table": {
"table_name": "messages","access_type": "range","possible_keys": ["service_time_idx"],"key": "service_time_idx","key_length": "1047","used_key_parts": ["service","time"],"rows": 1,"filtered": 100,"attached_condition": "messages.service = 'myservice' and messages.`time` between <cache>(current_timestamp() - interval 24 hour) and <cache>(current_timestamp())"
}
}
}
如何获取上面的第一个查询以使用索引?
显然,如果需要,我可以用其他方式编写查询。
解决方法
您的INDEX(service,time)
很完美。 EXPLAIN
有点神秘;让我详细说明;
(但首先, time
是一个DATETIME,所以time = '12:10'
没有意义。)
这样我们就可以一次看到它们:
1 where service = 'myservice'
and time BETWEEN DATE_SUB( NOW(),INTERVAL 24 HOUR ) AND NOW()
order by time desc;
2 where service = 'myservice'
and time BETWEEN DATE_SUB( NOW(),INTERVAL 24 HOUR ) AND NOW();
3 where service = 'myservice'
and time = '2020-10-03 09:51:25'
order by time desc;
4 where service = 'myservice'
and time BETWEEN DATE_SUB( NOW(),INTERVAL 24 HOUR ) AND NOW()
order by time desc;
当您更改#3以针对日期时间进行测试时会发生什么?
所有人都有
where service = 'myservice'
所以最好有INDEX(service,...)
然后所有这些对象都通过“范围”或“ =”对time
进行了引用,因此下面是一个不错的列:INDEX(service,time)
现在,让我们看看ORDER BY
(如果有)是否可以利用INDEX(service,time)
。
- 整个
WHERE
已经处理完毕,因此可以移至GROUP BY
(在您的情况下不存在)和ORDER BY
上。 -
EXPLAIN
不提供是否使用线索。 (或者至少不是确定的线索) -
EXPLAIN FORMAT=JSON
将提供有力的线索。运行它。 - 我会说,在您所有的
ORDER BY
情况下,都会使用索引。 - 这是一种通过计算触摸的行数来确定它的技术:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#handler_counts
也许这令人困惑:
-
Extra = Using index
表示索引正在“覆盖”,这意味着SELECT
中 anywhere 的 all 列都包含在{{ 1}}。 -
INDEX(...)
指的是“ ICP”,与本次讨论无关。 -
Extra = Using index condition
中的上述两项都不是-但这并未说明是否正在使用某些Extra
。 -
INDEX
(您没有)表示没有Type=All
被使用。 。注意:优化程序有时会避开所有索引,并选择读取“所有”行。
除 -
Type
表示至少部分使用了索引。
INDEX
以外的您的查询应该对索引有利。服务的主要条件是索引的前导元素,然后是时间。因此,您应该从中优化查询。我所做的唯一一件事是删除了两者之间。由于您要查找给定24小时后的所有内容,因此,为什么不要比24小时前做得更好呢,除非您期望“ NOW()”部分中可能在一两秒钟之间输入大量内容。
您是否尝试过实际运行查询并从中查看时间性能?
const config = require('../config');
const db = require('../models');
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
const { validationResult } = require('express-validator');
const { User,Role } = db;
const { token_expiry_time,refresh_token_expiry_time } = config.authentication;
const signUp = (req,res) => {
const { email,phone,first_name,last_name,password } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
status: 'failed',message: errors.array(),});
}
bcrypt.hash(password,8,(err,hash) => {
if (err) {
return res.status(500).json({
status: 'error',message: err,});
}
const user = new User({
first_name: first_name,last_name: last_name,email: email,phone: phone,password: hash,});
Role.findOne({ name: 'is_user' },role) => {
if (err) {
res.status(500).json({
status: 'error',});
return;
}
user.roles = [role._id];
user.save((err) => {
if (err) {
return res.status(500).json({
status: 'error',});
}
return res.status(201).json({
status: 'success',data: {
user,},});
});
});
});
};
module.exports = {
signUp,};
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。