志在巅峰的攀登者,不会陶醉在沿途的某个脚印之中
如下我这里有一张抽题记录表
我需要随机从中选4条数据,于是我会这样写(随机排序,取前4个)
执行时间 6.73秒,这个时间是绝对无法容忍的,你知道这个过程发生了什么吗???
select * from question_extracting order by rand() limit 4
上述这一句话先随机排序,再取前4条,它的执行过程如下:
在上述过程中,在第二步扫描了 question_extracting 全表 4974098 行 ,在第三步中也扫描了临时表 4974098 行 ,然后在最后一步取数据又扫描了4行数据,所以这一次查询总共扫描了 4974098 +4974098 + 4 次。
在上述这个过程中,order by rand() 使用了内存临时表,内存临时表排序的时候 使用了 rowid 排序方法,这个临时表,没有主键ID,由临时生成的长度为 6 字节的 rowid 来作为主键。
在 MySql 中,参数 tmp_table_size 限制了内存临时表的大小,默认值是 16M,如果临时表大小超过了 tmp_table_size,那么内存临时表就会转成磁盘临时表。
使用磁盘临时表的时候,就是对一个没有显式索引的 InnoDB 表进行排序。
在这个过程中使用的是 MySQL 5.6 版本引入的优先队列排序算法,如我们上述的随机取值,按照 优先队列排序算法 只需要取出 临时表中最小的 4 个 A 值对应的数据就可以,这个过程可简单描述如下:
随机取一条数据,按照最开始的写法如下:
随机算法的正确姿势
你并没有看错,这就是正确的写法
select max(id),min(id) into @A,@B from question_extracting ;
set @C= floor((@A-@B+1)*rand() + @B);
select * from question_extracting where id >= @C limit 1;
可描述如下:
完毕
不局限于思维,不局限于语言限制,才是编程的最高境界。
推荐阅读:MySql 你真的会使用字符串索引吗?