好友提要的查询优化 - MySQL

Posted

技术标签:

【中文标题】好友提要的查询优化 - MySQL【英文标题】:Query Optimization for Friends Feed - MySQL 【发布时间】:2012-09-24 15:27:45 【问题描述】:

我在朋友提要查询方面遇到了奇怪的问题 - 这是背景:

我有 3 张桌子

checkin - around 13m records
users - around 250k records
friends - around 1.5m records

在签入表中 - 它列出了用户执行的活动。 (这里有很多索引,但是在 user_id、created_at 和 (user_id,created_at) 上有一个索引。 users 表只是基本的用户信息 user_id 上有一个索引。 朋友表有一个 user_id、target_id 和 is_approved。 (user_id, is_approved) 字段上有索引。

在我的查询中,我试图只下拉任何用户的基本朋友提要 - 所以我一直在这样做:

SELECT checkin_id, created_at
FROM checkin
WHERE (user_id IN (SELECT friend_id from friends where user_id = 1 and is_approved = 1) OR user_id = 1)
ORDER by created_at DESC
LIMIT 0, 15

查询的目标只是为所有用户的朋友以及他们的活动提取 checkin_id 和 created_at。这是一个非常简单的查询,但是当用户的朋友最近有大量活动时,这个查询很快,这里是解释:

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
 1  PRIMARY     checkin     index   user_id,user_id_2   created_at  8   NULL    15  Using where
 2  DEPENDENT SUBQUERY friends  eq_ref    user_id,friend_id,is_approved,friend_looku...     PRIMARY     8   const,func  1   Using where

作为解释,user_id 是 user_id 上的简单索引 - 而 user_id_2 是 user_id 和 created_at 上的索引。在friends表上,friends_lookup是user_id和is_approved的索引。

这是一个非常简单的查询,完成时间为:显示第 0 - 14 行(共 15 行,查询耗时 0.0073 秒)。

但是,当用户的好友活动不是最近的并且没有很多数据时,相同的查询大约需要 5-7 秒,并且它具有与上一个查询相同的 EXPLAIN - 但需要更长的时间。

似乎对更多朋友没有影响,似乎随着最近的活动而加速。

是否有任何提示可以让任何人优化这些查询,以确保无论活动​​如何都以相同的速度运行?

服务器设置

这是一个运行 16GB RAM 的专用 mysql 服务器。它运行的是 Ubuntu 10.10,MySQL 的版本是 5.1.49

更新

所以大多数人建议删除 IN 部分并将它们移动到 INNER JOIN 中:

SELECT c.checkin_id, c.created_at
FROM checkin c
INNER JOIN friends f ON c.user_id = f.friend_id
WHERE f.user_id =1
AND f.is_approved =1
ORDER BY c.created_at DESC
LIMIT 0 , 15

这个查询比 EXPLAIN 中报告的要差 10 倍:

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
 1  SIMPLE  f   ref     PRIMARY,user_id,friend_id,is_approved,friend_looku...   friend_lookup   5   const,const     938     Using temporary; Using filesort
 1  SIMPLE  c   ref     user_id,user_id_2   user_id     4   untappd_prod.f.friend_id    71  Using where

此查询的目标是在同一个查询中获取所有朋友的活动以及您的活动(而不必创建两个查询并将结果合并在一起并按 created_at 排序)。我也无法删除 user_id 上的索引,因为它是另一个查询的重要部分。

有趣的是,当我在一个没有大量活动的用户帐户上运行此查询时,我得到了这样的解释:

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
 1  SIMPLE  f   index_merge     PRIMARY,user_id,friend_id,is_approved,friend_looku...    user_id,friend_lookup  4,5     NULL    11  Using intersect(user_id,friend_lookup); Using wher...
 1  SIMPLE  c   ref     user_id,user_id_2   user_id     4   untappd_prod.f.friend_id    71  Using where

有什么建议吗?

【问题讨论】:

【参考方案1】:

所以..你这里发生了一些事情..

    在解释计划中.. 通常优化器会选择“key”中的内容而不是 possible_keys 中的内容。这就是为什么当数据不是最新的时需要扫描更多记录的原因。

    仅在签入表时 (user_id, created_at) 和 created_at 是必需的.. 您不需要 user_id 的另一个索引.. 优化器将使用 (user_id, created_at) 因为 user_id 是第一顺序。

试试这个..

    使用朋友之间的连接和签入并删除 in 子句,这样朋友就成为驱动表,您应该首先在解释计划的执行路径上看到它。

    完成 1 后,您应该确保签入在执行路径中使用 (user_id, created_dt) 索引。

    为签入表中的 user_id 为 1 的 OR 条件编写另一个查询。我认为您的数据集对于这两个集应该是互斥的,然后应该没问题.. 否则您不需要首先是 IN 子句之后的 OR 条件。

    删除 user_id 索引,因为你有 user_id,created_at 索引。

-- 你的目标是它使用键下的索引,而不仅仅是可能的键。

这应该处理较旧的非最近签入以及最近签入。

【讨论】:

请查看我上面对原始问题所做的更新 - 不幸的是,我没有得到想要的结果。 ok.. 你可以试试`SELECT checkin_id, created_at FROM checkin USE INDEX (user_id_2) WHERE (user_id IN (SELECTfriend_id from friends where user_id = 1 and is_approved = 1) OR user_id = 1) ORDER by created_at DESC LIMIT 0, 15 ` 强制 user_id_2 索引似乎不起作用,因为它显示在 possible_keys 上,但不包括在键区域中。它最终进行了全表扫描(从签到中拉回 1300 万条记录) 嗨 Gregavola,你能粘贴 show create table checkin\G 的结果吗?并显示创建表朋友\G;请。谢谢。 另一种解决方案是按 created_dt 分区【参考方案2】:

我的第一个建议是删除依赖子查询并将其转换为连接。我发现 MySQL 不擅长处理这些类型的查询。试试这个:

SELECT c.checkin_id, c.created_at
FROM checkin c
INNER JOIN friends f
   ON c.user_id = f.friend_id
WHERE f.user_id = 1
   AND f.is_approved = 1
ORDER by c.created_at DESC
LIMIT 0, 15

我的第二个建议是,因为您有一个专用服务器,所以您的所有表都使用 InnoDB 存储引擎。确保调整默认 InnoDB 设置,尤其是 innodb_buffer_pool_size:http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/

【讨论】:

这个查询实际上需要 5-8 秒在任何人的帐户上。它也只会从用户朋友而不是用户那里提取数据。它使朋友成为主表,并使用文件排序和临时表。由于我们拥有大量表,迁移到 InnoDB 可能是一件困难的事情。

以上是关于好友提要的查询优化 - MySQL的主要内容,如果未能解决你的问题,请参考以下文章

mysql 子查询 优化

MySql性能优化查询优化

mysql查询所用时间过长 如何优化?

mysql优化之查询优化

mysql多条件查询的优化

mysql查询优化器应该怎么使用