MySQL 单个 table 上的 ORDERBY 日期时间性能
MySQL Performance ORDERBY datetime on single table
我有一个 MySql 事件 table 像这样:
+---------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+----------------+
| EventId | int(11) | NO | PRI | NULL | auto_increment |
| ControllerId | int(11) | NO | MUL | NULL | |
| EventTypeId | int(11) | NO | MUL | NULL | |
| DateTime | datetime(3) | NO | MUL | NULL | |
| InputId | int(11) | YES | MUL | | |
...
| AdditionalInfo | text | YES | | | |
+---------------------+--------------+------+-----+---------+----------------+
目前它有 200M 条记录,并且是 运行 文件。为了加快速度,我不使用任何连接查询它,但现在我开始进行一些长 运行 查询。一个慢 运行 查询的例子是这样的:
SELECT E.*
FROM Event E
WHERE (E.EventTypeId != 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL))
AND E.EventTypeId != 27 AND E.EventTypeId != 12
AND E.ControllerId in (5190, 5191, 5192, 5193)
ORDER BY E.DateTime DESC
LIMIT 0, 200
该查询需要 5 分钟!解释的重要(我认为)部分如下:
"key_length": "7",
"rows_examined_per_scan": 180071,
"rows_produced_per_join": 125770,
"filtered": "0.06",
"cost_info": {
"read_cost": "284389.84",
"eval_cost": " 25154.17",
"prefix_cost": "309544.01",
"data_read_per_join": "20M"
},
现在,如果我删除查询末尾的 ORDER BY E.DateTime DESC
,大约需要 0.1 秒才能完成。我已经有了 DateTime 的索引。
我想我理解服务器必须读取所有 180k 的概念? WHERE
子句返回的行在返回给客户端之前对它们进行排序,但为什么要花这么长时间?有什么我可以做的吗?复合索引在这里有帮助吗?
排序 180k 行应该不需要 5 分钟,除非您的硬件真的非常慢。对于此查询:
SELECT E.*
FROM Event E
WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
) AND
E.EventTypeId NOT IN (12, 27) AND
E.ControllerId in (5190, 5191, 5192, 5193)
ORDER BY E.DateTime DESC
LIMIT 0, 200;
您可以尝试索引 (ControllerId, EventTypeId, InputId)
。但是,我猜这不会很好。
一种可能是使用上面的索引,然后一次做一个控制器:
(SELECT E.*
FROM Event E
WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
) AND
E.EventTypeId NOT IN (12, 27) AND
E.ControllerId = 5190
ORDER BY E.DateTime DESC
LIMIT 0, 200
) UNION ALL
(SELECT E.*
FROM Event E
WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
) AND
E.EventTypeId NOT IN (12, 27) AND
E.ControllerId = 5191
ORDER BY E.DateTime DESC
LIMIT 0, 200
)
. . .
ORDER BY DateTime DESC
LIMIT 0, 200;
索引可以更有效地用于每个子查询。
你对 JOINs
的恐惧是错误的。当然,有些 JOINs
是昂贵的,但其他一些通过避免 JOIN
.
会慢得多
您这里的查询无法通过 changing/adding 索引进行优化。
没有 ORDER BY
,它快速扫描 table 的 部分 ,找到 200 行并退出。使用 ORDER BY
,它必须扫描整个 table,排序,然后 剥离 200 行。
戈登索引可能是最好的选择。但是,如果 IN
列表是动态的,则意味着动态构建 UNION
。
MySQL 的较新版本将更好地利用他的 3 列 INDEX
因为过滤现在完全在 InnoDB 引擎中完成,而不是反弹回通用 'handler'.
这可以简化
(E.EventTypeId != 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL))
至
(E.EventTypeId != 4 或 E.InputId 不为空)
但它不会加快速度以引起注意。
请使用SHOW CREATE TABLE
;它比 DESCRIBE
.
更具描述性
INT
允许 +/-20 亿的巨大范围。需要那么多id吗?它需要 4 个字节。考虑其他大小,例如 SMALLINT UNSIGNED
(2 字节,0..65K)。缩小 table 大小对性能有一些影响。
你真的需要E.*
吗?如果您不需要所有的列,拼出您确实需要的列会更快 运行;在某些情况下明显更快。
还有一件事...如果您是 "paginating",那么有一个技巧可以用来处理 UNION
+ LIMIT
+ OFFSET
;见 here
我有一个 MySql 事件 table 像这样:
+---------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+----------------+
| EventId | int(11) | NO | PRI | NULL | auto_increment |
| ControllerId | int(11) | NO | MUL | NULL | |
| EventTypeId | int(11) | NO | MUL | NULL | |
| DateTime | datetime(3) | NO | MUL | NULL | |
| InputId | int(11) | YES | MUL | | |
...
| AdditionalInfo | text | YES | | | |
+---------------------+--------------+------+-----+---------+----------------+
目前它有 200M 条记录,并且是 运行 文件。为了加快速度,我不使用任何连接查询它,但现在我开始进行一些长 运行 查询。一个慢 运行 查询的例子是这样的:
SELECT E.*
FROM Event E
WHERE (E.EventTypeId != 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL))
AND E.EventTypeId != 27 AND E.EventTypeId != 12
AND E.ControllerId in (5190, 5191, 5192, 5193)
ORDER BY E.DateTime DESC
LIMIT 0, 200
该查询需要 5 分钟!解释的重要(我认为)部分如下:
"key_length": "7",
"rows_examined_per_scan": 180071,
"rows_produced_per_join": 125770,
"filtered": "0.06",
"cost_info": {
"read_cost": "284389.84",
"eval_cost": " 25154.17",
"prefix_cost": "309544.01",
"data_read_per_join": "20M"
},
现在,如果我删除查询末尾的 ORDER BY E.DateTime DESC
,大约需要 0.1 秒才能完成。我已经有了 DateTime 的索引。
我想我理解服务器必须读取所有 180k 的概念? WHERE
子句返回的行在返回给客户端之前对它们进行排序,但为什么要花这么长时间?有什么我可以做的吗?复合索引在这里有帮助吗?
排序 180k 行应该不需要 5 分钟,除非您的硬件真的非常慢。对于此查询:
SELECT E.*
FROM Event E
WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
) AND
E.EventTypeId NOT IN (12, 27) AND
E.ControllerId in (5190, 5191, 5192, 5193)
ORDER BY E.DateTime DESC
LIMIT 0, 200;
您可以尝试索引 (ControllerId, EventTypeId, InputId)
。但是,我猜这不会很好。
一种可能是使用上面的索引,然后一次做一个控制器:
(SELECT E.*
FROM Event E
WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
) AND
E.EventTypeId NOT IN (12, 27) AND
E.ControllerId = 5190
ORDER BY E.DateTime DESC
LIMIT 0, 200
) UNION ALL
(SELECT E.*
FROM Event E
WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
) AND
E.EventTypeId NOT IN (12, 27) AND
E.ControllerId = 5191
ORDER BY E.DateTime DESC
LIMIT 0, 200
)
. . .
ORDER BY DateTime DESC
LIMIT 0, 200;
索引可以更有效地用于每个子查询。
你对 JOINs
的恐惧是错误的。当然,有些 JOINs
是昂贵的,但其他一些通过避免 JOIN
.
您这里的查询无法通过 changing/adding 索引进行优化。
没有 ORDER BY
,它快速扫描 table 的 部分 ,找到 200 行并退出。使用 ORDER BY
,它必须扫描整个 table,排序,然后 剥离 200 行。
戈登索引可能是最好的选择。但是,如果 IN
列表是动态的,则意味着动态构建 UNION
。
MySQL 的较新版本将更好地利用他的 3 列 INDEX
因为过滤现在完全在 InnoDB 引擎中完成,而不是反弹回通用 'handler'.
这可以简化
(E.EventTypeId != 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL))
至 (E.EventTypeId != 4 或 E.InputId 不为空)
但它不会加快速度以引起注意。
请使用SHOW CREATE TABLE
;它比 DESCRIBE
.
INT
允许 +/-20 亿的巨大范围。需要那么多id吗?它需要 4 个字节。考虑其他大小,例如 SMALLINT UNSIGNED
(2 字节,0..65K)。缩小 table 大小对性能有一些影响。
你真的需要E.*
吗?如果您不需要所有的列,拼出您确实需要的列会更快 运行;在某些情况下明显更快。
还有一件事...如果您是 "paginating",那么有一个技巧可以用来处理 UNION
+ LIMIT
+ OFFSET
;见 here