好友提要的查询优化 - 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的主要内容,如果未能解决你的问题,请参考以下文章