优化MySQL查询性能以选择“已存档”状态

avkwfej4  于 2023-10-15  发布在  Mysql
关注(0)|答案(3)|浏览(131)

我有一个名为“article”的表,其中包含1,145,141条记录,这些记录具有不同的字段,

  1. "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索引

  1. article_abstract_unq_id index on 'uiqPID'
  2. article_abstract_unq_id index on 'tenant'
  3. article_status_idx关于'status'的索引
  4. idx_composite_search index on 'id'
  5. idx_composite_search index on 'uiqPID'
  6. idx_composite_search索引在'created_at'上
    我的问题是以下查询的性能,执行时间为5.7秒:
  1. SELECT
  2. a.id AS id,
  3. a.created_at AS created_at
  4. FROM
  5. article a
  6. WHERE
  7. a.status = 'ARCHIVED'
  8. ORDER BY a.created_at DESC
  9. LIMIT 50;

但是,如果我删除WHERE条件或将其更改为.status = 'DRAFT',则查询将在1秒内完成。
在检查查询计划时,我注意到执行策略的不同。使用“存档”或“草稿”状态过滤器时,计划显示:

  1. key: article_status_idx
  2. Extra: Using index condition; Using filesort

但如果没有“存档”过滤器,该计划只是说:

  1. key:
  2. Extra: Using filesort

我的问题是:如何优化查询性能以过滤“已存档”状态,确保它的执行速度比当前的5.7秒快,类似于没有此条件或具有“草稿”状态的查询?

vu8f3i0k

vu8f3i0k1#

您可以创建一个与查询完全匹配的复合索引:

  1. create index idx on article (status, created_at desc);

因此,DBMS可以转到索引中的status = 'ARCHIVED',读取前50个条目并完成任务。
https://dev.mysql.com/doc/refman/8.0/en/descending-indexes.html

lx0bsm1f

lx0bsm1f2#

这里有一个可以提高速度的替代方法,试试吧:

  1. SELECT
  2. a.id AS id,
  3. a.created_at AS created_at
  4. FROM (
  5. SELECT id
  6. FROM article
  7. WHERE status = 'ARCHIVED'
  8. ORDER BY created_at DESC
  9. LIMIT 50
  10. ) AS subquery
  11. JOIN article a ON subquery.id = a.id;

说明:这个查询利用一个子查询,首先根据created_at列识别50个最近的“ARCHIVED”记录的id值。然后,它执行一个连接,从主表项目中检索附加列(id和created_at)。当您希望优化具有复杂筛选和排序的查询时,此方法可能非常有用。
请确保状态列上的article_status_idx索引以及created_at和id列上的idx_composite_search索引得到良好维护,以便高效执行。
如果你听从我的建议,别忘了我的分数,去享受吧。

展开查看全部
cuxqih21

cuxqih213#

你所经历的行为正是我所期望的。将1,118,993个数字-时间戳元组写入内存需要时间,特别是如果您按created_at对结果进行排序。您需要测试以下内容:

  1. SELECT
  2. a.id AS id,
  3. a.created_at AS created_at
  4. FROM
  5. article a
  6. ORDER BY a.created_at DESC
  7. LIMIT 0, 1118993;

如果执行此查询的时间与执行带有where子句的查询的时间大致相同,则不是where子句导致性能下降,而是对查询进行排序+将结果加载到内存中。也运行以下命令:

  1. SELECT
  2. a.id AS id,
  3. a.created_at AS created_at
  4. FROM
  5. article a
  6. LIMIT 0, 1118993;

在这里,我们甚至没有订单。如果这同样很慢,那么你主要等待的是把这些东西写进内存。
无论如何,做这些测量,结果将确认慢是由where或order by子句引起的,或者不是。如果这些条款导致缓慢,然后让我知道在评论部分,我将提供优化该问题的方法。但很可能真实的问题是您正在等待执行完整的查询。
也许最好将查询分成几个分区,然后运行限制为0、10000的查询,然后限制为10000、10000等等,这样你就可以在等待其他结果的时候处理第一个结果,也许可以减少不耐烦的用户的沮丧。但这并不能改变加载所有这些东西需要时间的事实,即使您可以使这种等待对用户更友好,如果显示部分结果有一些有用的意义的话。
编辑
你可以尝试在创建复制表之前创建一个(status,created_at)键,也许这样的索引对你的性能会很有用,这个想法是为每个状态类型的created_at建立一个索引顺序。
如果其他方法都失败了,你可以创建一个这样的表:

  1. create table article_archive(
  2. id int primary key,
  3. created_at timestamp
  4. );
  5. insert into article_archive(id, created_at)
  6. select id, created_at
  7. from article
  8. where `status` = 'ARCHIVE';
  9. ALTER TABLE article_archive ADD INDEX (created_at DESC);

然后你可以从article_archive中选择,比如:

  1. select id, created_at
  2. from article_archive
  3. order by created_at desc
  4. limit 50;

你甚至可以用articleid来连接它。

展开查看全部

相关问题