我有一个奇怪的问题,我的朋友饲料查询-这里是背景:
我有三张桌子

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

在checkin表中-它列出了用户执行的活动。(这里有许多索引,但是有一个关于用户id,created和(用户id,created)的索引)。
用户表只是基本的用户信息,用户id上有一个索引。
friends表有一个用户id,target id,并且已被批准。在(用户id,已批准)字段上有一个索引。
在我的查询中,我试图只下拉任何用户的一个基本好友提要-因此我一直在这样做:
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并为所有用户的朋友及其活动创建_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

作为一种解释,用户id是用户id上的一个简单索引,而用户id是用户id上的一个索引,创建于。在friends表中,friends_lookup是用户id的索引,并且已被批准。
这是一个非常简单的查询,get在中完成:显示行0-14(总共15行,查询耗时0.0073秒)。
然而,当用户的好友活动不是最近的,并且没有太多数据时,同一个查询大约需要5-7秒,它与上一个查询有相同的解释,但需要更长的时间。
它似乎对更多的朋友没有影响,似乎随着最近的活动而加快。
有没有任何提示,任何人都必须优化这些查询,以确保它们以相同的速度运行而不受活动的影响?
服务器设置
这是一个运行16GB内存的专用MySQL服务器。它运行的是Ubuntu 10.10,MySQL的版本是5.1.49
更新
因此,大多数人都建议删除IN-piece并将其移动到内部连接:
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

这个问题更糟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

此查询的目标是在同一个查询中获取所有的friends活动和您的活动(而不必创建两个查询并将结果合并在一起并按创建的位置排序)。我也不能删除用户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

有什么建议吗?

最佳答案

所以。。你有一些事情要做。。
在解释计划中。。通常优化器会选择“键”中的内容,而不是可能的键中的内容。所以这就是为什么当数据不是最新的时候你需要扫描更多的记录。
仅在签入表上(用户id,创建位置)和创建位置是必需的。。用户id不需要另一个索引。。优化器将使用(user_id,created_at),因为user_id是第一个订单。
试试这个。。
使用join-between-friends和checkin并删除in子句,这样friends就成为驱动表,您应该首先在explain计划的执行路径上看到它。
完成1后,应确保签入正在执行路径中使用(用户id,已创建的)索引。
为签入表中的用户id为1的OR条件编写另一个查询。我认为你的数据集应该是互斥的,这样就可以了。。否则,您首先不需要在IN子句后面有or条件。
删除由它自己创建的用户id索引,因为您有在索引处创建的用户id。
--您的目标是使用键下的索引,而不仅仅是可能的键。
这应该考虑到旧的非最近的签入以及最近的签入。

10-06 16:17
查看更多