【问题标题】:MySQL Query Optimization for for Count Query计数查询的 MySQL 查询优化
【发布时间】:2020-05-21 12:03:56
【问题描述】:

我需要有关优化此计数查询的帮助。以下是表格:

activities
- activity_id (PRIMARY)
- item_id
- user_id
- created_at

INDEX: user_id_created_at (user_id, created_at)
INDEX: item_id (item_id)
retail_activities
- activity_id (PRIMARY)
- item_id
- created_at

INDEX: item_id (activity_id, item_id)
INDEX: item_id_created (activity_id, item_id, created_at)
users
- user_id (PRIMARY)
- is_private

INDEX: user_id_private (user_id, is_private)

目标

我想查找在某个日期范围内发生的 retail_activities 中的项目数和唯一用户数,其中活动表中的 item_id 不等于零售活动表中的 item_id ,并且用户不是私有的。

基本上,我想找到发生的零售活动总数以及在选定的 item_id 和选定的时间范围内执行的唯一用户总数。

所以查询将是(X 是一个整数,例如 1234 - 如果是项目,则它是 ID)

SELECT count(retail_activities.item_id) as total_items, count(distinct activities.user_id) as total_users 
from activities 
inner join retail_activities on retail_activities.activity_id = activities.activity_id 
inner join users on users.user_id = activities.user_id 
where users.is_private = 0 and 
activities.item_id != retail_activities.item_id and retail_activities.item_id = X and
retail_activities.created_at > "2019-11-22 00:00:00"

这个查询的解释如下:

1   SIMPLE  retail_activities   item_id_created item_id_created 5   const   812856  Using where; Using index
1   SIMPLE  activities  eq_ref  PRIMARY PRIMARY 4   retail_activities.activity_id   1   Using where
1   SIMPLE  users   eq_ref  PRIMARY PRIMARY 4   activities.user_id  1   Using where

因为这个项目有很多活动(350k),所以运行很慢(8-25s)。有什么办法可以加快速度?

【问题讨论】:

  • 这不是该查询的解释,ut_users 是什么? X 是什么?
  • @Strawberry 我刚刚更新了这个以删除这个错字并添加 X 的解释。
  • 在 1:1 关系中有两个表是不寻常的——我看到它们都有PRIMARY KEY(active_id)。请证明。
  • 或者retail_activities是一个many:many映射表?请提供SHOW CREATE TABLE;我认为你有一个错字。

标签: mysql sql optimization mariadb


【解决方案1】:

我建议你优化你的索引策略,尽可能使用哈希索引。

根据 MySQL 文档:

作为复合索引的替代方案,您可以引入一列 根据其他列的信息“散列”。如果这 列很短,相当独特,并且有索引,它可能会更快 比许多列上的“宽”索引。

另外,您不需要在retail_activities 表中显式地添加INDEX: item_id (activity_id, item_id)。那是因为您已经创建了一个涉及它们的复合主键。所以它也可以作为一个索引。

并研究索引合并优化。这是 MySQL journal doc。 并且,阅读optimizer switch 以确定连接的理想顺序以获得最佳性能。以下是一些我认为您会发现有用的信息:

更改加入顺序。实现这一点的方法包括加入顺序 优化器提示(参见第 8.9.3 节,“优化器提示”),STRAIGHT_JOIN 紧跟在 SELECT 和 STRAIGHT_JOIN 连接运算符之后。

【讨论】:

  • 我实际上在该索引 (INDEX: item_id (activity_id, item_id) 中输入了错误的索引,并更新了问题。不是复合键,activity_id是主键。
  • 还有STRAIGHT_JOIN,似乎更糟——它使用了另一个索引item_id, user_id,它扫描375698108行。
  • “散列”技巧很少值得拥有;坚持“复合”。并且“索引合并相交”可能总是比等效的复合索引慢。索引“提示”受到以下影响:“它们今天可能会有所帮助,但明天会使事情变得更糟。”
【解决方案2】:

对于这个查询:

select count(*) as total_items, count(distinct a.user_id) as total_users 
from activities a join
     retail_activities ra
     on ra.activity_id = a.activity_id join
     users u
     on u.user_id = a.user_id 
where u.is_private = 0 and 
      a.item_id <> ra.item_id and
      ra.item_id = X
      ra.created_at > '2019-11-22';

我会推荐以下索引:

  • retail_activities(item_id, created_at, activity_id)
  • activities(activity_id, item_id)(如果activity_id是主键则不需要)
  • users(user_id, is_private)

【讨论】:

  • 我实际上已经有了这个,只是没有记录下来。运行这个索引仍然需要大约 15-30 秒。我更新了问题。
  • @gregavola - retail_activities 的索引中列的顺序很重要。
  • @gregavola 。 . .正如 Rick 所指出的,列的顺序很重要。您没有指定的索引。
  • 谢谢,@RickJames - 做到了。感谢您指出这一点!
  • @gregavola 。 . .速度提升了多少?
猜你喜欢
  • 2011-05-22
  • 2013-04-16
  • 2012-10-09
  • 2023-04-01
  • 2011-01-22
  • 2011-07-07
  • 2018-12-21
  • 2010-12-15
相关资源
最近更新 更多