mysql - 如果查询中的日期时间有时区,MariaDB MySQL 查询要长得多,但如果没有添加时区,则非常快 - 为什么?
问题描述
我在 MariaDB (MySQL) 中有一个表,其start_date_time
字段类型为DATETIME
(no TZ stored) 。
当我按如下要求时,我得到
MariaDB [db]> explain
SELECT *
FROM mytable
WHERE start_date_time>= '2021-03-04 00:00:00+00:00'
AND start_date_time<='2021-03-04 11:08:00+00:00'
ORDER BY start_date_time;
+------+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
| 1 | SIMPLE | mytable | index | PRIMARY | PRIMARY | 5 | NULL | 4504011 | Using where |
+------+-------------+----------+-------+---------------+---------+---------+------+---------+-------------+
在实践中需要很长时间才能完成。该表包含多年的数据,请求大约需要 30 秒,看起来类似于完整扫描。
但是,如果我只是减少+00:00
查询时间(2021-03-04 00:00:00+00:00
变成2021-03-04 00:00:00
),解释如下所示,查询速度更快:
MariaDB [db]> explain
SELECT *
FROM mytable
WHERE start_date_time>= '2021-03-04 00:00:00'
AND start_date_time<='2021-03-04 11:08:00'
ORDER BY start_date_time;
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | mytable | range | PRIMARY | PRIMARY | 5 | NULL | 1 | Using where |
+------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.000 sec)
我想了解为什么会发生这种执行时间差异。理想情况下,我想在查询中保留时区,以避免数据库和客户端之间对时区的任何假设、转换或误解。
更新 根据要求,我还提供了下面的 JSON 格式的解释
第一次(慢)查询
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "mytable",
"access_type": "index",
"possible_keys": ["PRIMARY"],
"key": "PRIMARY",
"key_length": "5",
"used_key_parts": ["start_date_time"],
"rows": 11403954,
"filtered": 100,
"attached_condition": "mytable.start_date_time >= '2021-03-04 00:00:00+00:00' and mytable.start_date_time <= '2021-03-04 11:08:00+00:00'"
}
}
}
第二(快速)查询
| {
"query_block": {
"select_id": 1,
"table": {
"table_name": "mytable",
"access_type": "range",
"possible_keys": ["PRIMARY"],
"key": "PRIMARY",
"key_length": "5",
"used_key_parts": ["start_date_time"],
"rows": 1,
"filtered": 100,
"attached_condition": "mytable.start_date_time >= '2021-03-04 00:00:00' and mytable.start_date_time <= '2021-03-04 11:08:00'"
}
}
}
SHOW CREATE TABLE:
CREATE TABLE `mytable` (
`start_date_time` datetime NOT NULL,
... more fields...
PRIMARY KEY (`start_date_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
解决方案
由于隐式类型转换,这看起来像是索引滥用的情况。当没有发生类型转换时,它会扫描索引范围 - 找到两个边界值并返回它们之间的所有内容。如果它必须对存储的值应用某种转换函数,那么它会从头到尾扫描整个索引,并根据给定的谓词测试每一行。
我认为您可能会尝试将查询中的常量显式转换为DATETIME
以确认此猜想。像:
WHERE start_date_time >= cast('2021-03-04 00:00:00+00:00' as datetime)
AND start_date_time <= cast('2021-03-04 11:08:00+00:00' as datetime)
虽然,上面可能会丢弃时区信息。我相信,类似的东西convert_tz('2021-03-04 00:00:00', '+00:00', @@GLOBAL.time_zone)
会处理得更好。
推荐阅读
- windows - 为什么 Proof General 在 Windows 10 中如此缓慢?
- java - Java 通用问题:无法转换 Map
到 M 扩展地图 - angular8 - 来自 API 调用的 Angular 8 FormGroup 异步验证
- r - 根据 R 中的数据范围计算值
- c# - restsharp PUT 请求错误 --> “StatusCode: InternalServerError, Content-Type: application/json, Content-Length: -1)”
- javascript - 为什么我在使用 Ext JS 6.2.0 时遇到问题?
- alexa-skills-kit - 如何使用 JOVO 框架获取 Alexa 技能意图的实际话语文本
- excel - 有没有办法提高执行时间?
- html - 点击孩子时不要考虑父母
- python - 如何使用 python/selenium 自动执行网站上的按钮点击序列?