1. mysql> select rand();
  2. +--------------------+
  3. | rand() |
  4. +--------------------+
  5. | 0.1689035568096708 |
  6. +--------------------+
  7. 1 row in set (0.00 sec)

select title, description from film ORDER BY RAND() LIMIT 1;

  • 1.创建一个临时表,临时表的字段为rand、title、description
  • 2.从表中取出一行,调用RAND(),将结果和数据放入临时表,以此类推
  • 3.针对临时表,将rand字段+行位置(主键)放入sort_buffer
  • 4.对sort_buffer排序,取出第一个的行位置(主键),查询临时表

有点乱,看下面的例子
select title, description from film ORDER BY RAND() LIMIT 1;

  1. 创建一个临时表,临时表的字段为rand、title、description

image.png

  1. 从表(源表)中取出一行,调用RAND(),将结果和数据放入临时表,以此类推

比如有1000行表记录,得到1000行的中间结果放入临时表中
image.png

  1. 针对临时表,将rand字段+行位置(主键)放入sort_buffer

image.png
因为这个临时表可能是内存表也可能是磁盘表。

  1. 对sort_buffer排序,取出第一个的行位置(主键),回到临时表,查询临时表

image.png

为什么会慢?

  • SQL执行过程中出现了两次中间结果,都是全长度的
  • 仅需要一个随机结果,缺经历了不必要的排序(虽然优化过)
  • 调用了很多次RAND()。

    select max(film_id),min(film_id) into @M,@N from film ;
    set @X= floor((@M-@N+1)*rand() + @N);
    select title, description from film where film_id >= @X limit 1;
    
  • 选取数据中最大的ID和最小的ID

  • 使用RAND()随机选取一个ID
  • 直接查询此ID的数据条目

    解决方法—业务方案

  • 查询数据表总数total

  • total范围内,随机选取一个数字r
  • 执行以下SQL:

select title, description from film limit r, 1;

总结

  • ORDER BY RAND() limit 1 是效率很低的随机查询方式
  • 原因主要是,上述方法有两次中间结果,还有排序过程
  • 可以通过修改SQL,选取随机主键方式,提升性能
  • 最好还是在业务中处理,尽量减少复杂SQL