优化我的mysql语句太慢了

优化我的mysql语句太慢了,sql,mysql,random,Sql,Mysql,Random,所以我有一个超过80000条记录的表,这个叫做系统。我还有另一张表,叫做follows 我需要我的语句从系统表中随机选择记录,其中当前userid下的下表中尚未列出该id 这就是我所拥有的: SELECT system.id, system.username, system.password, system.followed, system.isvalid, follo

所以我有一个超过80000条记录的表,这个叫做系统。我还有另一张表,叫做follows

我需要我的语句从系统表中随机选择记录,其中当前userid下的下表中尚未列出该id

这就是我所拥有的:

    SELECT system.id, 
           system.username, 
           system.password, 
           system.followed, 
           system.isvalid, 
           follows.userid, 
           follows.systemid
      FROM system
  LEFT JOIN follows ON system.id = follows.systemid
                   AND follows.userid = 2 
      WHERE system.followed = 0 
        AND system.isvalid = 1
        AND follows.systemid IS NULL
   ORDER BY RAND()
      LIMIT 200
现在它工作得非常好,只是它甚至需要花整整一分钟的时间才能开始用它选择的记录处理手头的工作。此时,脚本通常会超时,而不会发生任何事情

有人能告诉我如何重做这个,所以同样的想法是这样做的,但它不是使用兰德订单?这似乎把事情拖慢了很多


谢谢

我不确定是否有一个简单的解决方案来取代您的查询,下面是一篇关于纠正此类问题的文章


您可以根据ID和当前时间生成一些伪随机值:

ORDER BY 37*(UNIX_TIMESTAMP() ^ system.id) & 0xffff

将从id混合咬入,然后只取最低的16个。

速度缓慢的主要原因有两个:

  • SQL必须首先为每一行发出一个随机数
  • 然后,必须根据该数字对行进行排序,以选择前200行
有一个技巧可以帮助这种情况,它需要一些准备工作,实现它的方式(以及它的相对兴趣)取决于您的实际用例

==>引入一个带有“随机类别”值的额外列以过滤掉大多数行

其思想是让整数值列具有随机分配的值,在准备时分配一次,其值介于0和9之间(或1和25…任意值)。然后需要将此列添加到查询中使用的索引中。最后,通过修改查询以在这个列上包含一个过滤器=一个特定的值(比如3),SQL需要处理的行数将减少10行(或者25行,具体取决于“随机类别”中的不同值的数量)

假设这个新列名为RandPreFilter,我们可以引入如下索引

CREATE [UNIQUE ?] INDEX  
ON system (id, RandPreFilter)
并按如下方式更改查询

SELECT system.id
     , system.username
     , system.password
     , system.followed
     , system.isvalid
     , follows.userid
     , follows.systemid
FROM system
LEFT JOIN follows ON system.id = follows.systemid
   AND follows.userid = 2 
WHERE system.followed=0 AND system.isvalid=1
   AND follows.systemid IS NULL

   AND RandPreFilter = 1 -- or other numbers, or possibly 
        -- FLOOR(1 + RAND() * 25)
ORDER BY RAND()
LIMIT 200

<> P>查询速度较慢的原因是,数据库需要保留所有生成的随机值和它们各自的数据的表示,然后才能从数据库返回单个行。您可以做的是通过使用RAND(RAND)来限制要优先考虑的候选行数。 使用这种方法,数据库可以以流式方式处理查询,而无需构建所有数据的大型中间表示。缺点是,您永远无法100%确保获得所需的样本数,因此您可能必须在使用较小的样本集或inc之前再次执行查询请记住添加样本(确保避免重复),直到获得所需的样本数量


如果您不要求查询为每次调用返回不同的结果,您还可以添加一个带有索引的预生成随机值列,并与上述技术相结合。这将允许您以公平的方式获取任意数量的样本,即使您添加或删除行,但对相同数据的相同查询当然会返回相同的结果结果集。

根据数据的随机性,可能需要对数据进行排序,并添加一个额外的“上次使用”日期时间列,并在使用数据后进行更新。然后按上次使用字段降序选择前n行

如果将其包装在一个准备好的语句中,则可以一次选择一个(半)随机结果,而不必担心逻辑


或者,为每一行指定一个顺序ID,并在代码中生成随机性,然后只提取所需的行。问题是,在订购之前会返回完整的记录集。

可能有点晚,但至少这里有一个额外的解决方案供将来考虑:

SELECT minSystem.id, 
    minSystem.username, 
    minSystem.password, 
    minSystem.followed, 
    minSystem.isvalid,
    randFollows.userid, 
    randFollows.systemid
FROM
(
    SELECT *
    FROM system
    WHERE system.followed = 0 AND system.isvalid = 1
) as minSystem
LEFT JOIN 
(
    SELECT * 
    FROM (
        SELECT *
        FROM follows
        WHERE follows.systemid IS NULL
    ) as minFollows
    WHERE rand() <= 200 * 1.5 / (SELECT count(*) FROM follows WHERE systemid IS NULL)
) as randFollows
ON minSystem.id = randFollows.systemid
LIMIT 200
选择minSystem.id,
minSystem.username,
minSystem.password,
接下来是,
minSystem.isvalid,
rand.userid,
rand.systemid
从…起
(
挑选*
从系统
其中system.isvalid=0和system.isvalid=1
)as minSystem
左连接
(
选择*
从(
挑选*
从下面
其中follows.systemid为NULL
)如下

其中rand()你的连接字段有什么索引?这可能是一个很大的瓶颈。我不太确定你的意思…@Brandon我知道现在做这件事有点晚了,但是如果你想用一种半简单化的方法来做,你可以把它放在一个子查询中。更多细节请参阅我的答案可能重复感谢,但这不是一种可行的方法这个查询是有效的。为什么不呢?在那篇文章中有很多不同的解决方案,其中一些我认为对你有用。你的id字段是自动递增字段吗?如果是的话,选择随机id的解决方案应该是有效的。