如果查询中的日期时间有时区,MariaDB MySQL 查询要长得多,但如果没有添加时区,则非常快 - 为啥?
Posted
技术标签:
【中文标题】如果查询中的日期时间有时区,MariaDB MySQL 查询要长得多,但如果没有添加时区,则非常快 - 为啥?【英文标题】:MariaDB MySQL query is much longer if datetime in query has timezone, but very fast if no timezone is added - why?如果查询中的日期时间有时区,MariaDB MySQL 查询要长得多,但如果没有添加时区,则非常快 - 为什么? 【发布时间】:2021-06-04 01:09:47 【问题描述】:我在 MariaDB (mysql) 中有一个表,其中包含 start_date_time
类型为 DATETIME
的字段(未存储 TZ)。
当我做如下请求时,我得到
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
【问题讨论】:
第二个查询没有ORDER BY start_date_time
。我认为这是一个错字?
另外,请注明:1)表的主键是什么,2)表的索引列表(MariaDB术语中的“键”)。
MySQL 不支持包括时区在内的日期时间文字。见Date and Time Literals
【参考方案1】:
这看起来像是由于隐式类型转换导致的索引滥用情况。当没有发生类型转换时,它会扫描索引范围 - 找到两个边界值并返回它们之间的所有内容。如果它必须对存储的值应用某种转换函数,那么它会从头到尾扫描整个索引,并根据给定的谓词测试每一行。
我认为您可以尝试将查询中的常量显式转换为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)
这样的东西会处理得更好。
【讨论】:
【参考方案2】:一个可能的答案:'2021-03-04 00:00:00+00:00'
只是一个字符串,但'2021-03-04 00:00:00'
被识别为DATETIME
。也就是说,MySQL 不支持时区附加,不支持该语法,也不支持带有“T”和/或“Z”的语法。
由于是字符串,在比较之前可能需要将该列转换为字符串;禁止使用任何INDEX
。请提供两个查询的EXPLAIN FORMAT=JSON SELECT ...
。也许它会指出我的怀疑。
请提供SHOW CREATE TABLE
。听起来你有PRIMARY KEY(start_date_time)
,但我需要确定一下。
DATETIME
没有被 MySQL 调整为时区; TIMESTAMP
是。见(至少)SHOW VARIABLES LIKE '%zone%';
【讨论】:
这也是我的理解。 Date and Time Literals 的手册部分没有提及任何带时区的字符串。 @Rick James,我已将您要求的信息添加到问题中。 @Nick 当您从与数据库服务器所在的 TZ 不同的计算机上的客户端查询数据库时,TZ 怎么办?查询中的时间戳会被 TZ 校正还是精确匹配数据库内容? @AskarIbragimov - 我添加了一段关于“调整 TZ”的段落。 谢谢!我怎么能确定“因为它是一个字符串,所以在比较之前可能需要将该列转换为字符串;禁止使用任何索引” - 发生?以上是关于如果查询中的日期时间有时区,MariaDB MySQL 查询要长得多,但如果没有添加时区,则非常快 - 为啥?的主要内容,如果未能解决你的问题,请参考以下文章