我有一个名为“article”的表,其中包含1,145,141条记录,这些记录具有不同的字段,
"id," "uiqPID," "tenant," "status," "title," "body," "user_id," "category_id," "created_at," and "updated_at."
“status”列可以有三个值之一:“已发布”、“草稿"或”已存档“,计数如下:
- 发表:2篇
- 草稿:26,145
- 存档:1,118,993
我设置了以下索引:
1.“id”上的PRIMARY索引
- article_abstract_unq_id index on 'uiqPID'
- article_abstract_unq_id index on 'tenant'
- article_status_idx关于'status'的索引
- idx_composite_search index on 'id'
- idx_composite_search index on 'uiqPID'
- idx_composite_search索引在'created_at'上
我的问题是以下查询的性能,执行时间为5.7秒:
SELECT
a.id AS id,
a.created_at AS created_at
FROM
article a
WHERE
a.status = 'ARCHIVED'
ORDER BY a.created_at DESC
LIMIT 50;
但是,如果我删除WHERE条件或将其更改为.status = 'DRAFT',则查询将在1秒内完成。
在检查查询计划时,我注意到执行策略的不同。使用“存档”或“草稿”状态过滤器时,计划显示:
key: article_status_idx
Extra: Using index condition; Using filesort
但如果没有“存档”过滤器,该计划只是说:
key:
Extra: Using filesort
我的问题是:如何优化查询性能以过滤“已存档”状态,确保它的执行速度比当前的5.7秒快,类似于没有此条件或具有“草稿”状态的查询?
3条答案
按热度按时间vu8f3i0k1#
您可以创建一个与查询完全匹配的复合索引:
因此,DBMS可以转到索引中的status = 'ARCHIVED',读取前50个条目并完成任务。
https://dev.mysql.com/doc/refman/8.0/en/descending-indexes.html
lx0bsm1f2#
这里有一个可以提高速度的替代方法,试试吧:
说明:这个查询利用一个子查询,首先根据created_at列识别50个最近的“ARCHIVED”记录的id值。然后,它执行一个连接,从主表项目中检索附加列(id和created_at)。当您希望优化具有复杂筛选和排序的查询时,此方法可能非常有用。
请确保状态列上的article_status_idx索引以及created_at和id列上的idx_composite_search索引得到良好维护,以便高效执行。
如果你听从我的建议,别忘了我的分数,去享受吧。
cuxqih213#
你所经历的行为正是我所期望的。将1,118,993个数字-时间戳元组写入内存需要时间,特别是如果您按
created_at
对结果进行排序。您需要测试以下内容:如果执行此查询的时间与执行带有where子句的查询的时间大致相同,则不是where子句导致性能下降,而是对查询进行排序+将结果加载到内存中。也运行以下命令:
在这里,我们甚至没有订单。如果这同样很慢,那么你主要等待的是把这些东西写进内存。
无论如何,做这些测量,结果将确认慢是由where或order by子句引起的,或者不是。如果这些条款导致缓慢,然后让我知道在评论部分,我将提供优化该问题的方法。但很可能真实的问题是您正在等待执行完整的查询。
也许最好将查询分成几个分区,然后运行限制为0、10000的查询,然后限制为10000、10000等等,这样你就可以在等待其他结果的时候处理第一个结果,也许可以减少不耐烦的用户的沮丧。但这并不能改变加载所有这些东西需要时间的事实,即使您可以使这种等待对用户更友好,如果显示部分结果有一些有用的意义的话。
编辑
你可以尝试在创建复制表之前创建一个(status,created_at)键,也许这样的索引对你的性能会很有用,这个想法是为每个状态类型的created_at建立一个索引顺序。
如果其他方法都失败了,你可以创建一个这样的表:
然后你可以从
article_archive
中选择,比如:你甚至可以用
article
和id
来连接它。