order by rand() 实现随机获取消息

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: order by rand() 实现随机获取消息

业务场景

比如,要做一个学习单词的App,实现用户在打开首页的时候,随机显示三个单词:

// 创建单词表
mysql> create table words(
  `id` int NOT NULL AUTO_INCREMENT,
  `word` varchar(64) DEFAULT NULL,
  PRIMARY KEY(`id`)
)ENGINE=InnoDB;
// 插入10000行模拟数据
mysql> insert into words values(1, "abandon") (...) (...);
/********************************************************
*  @ order by rand() 
*  实现随机排序,随机选取三个单词
********************************************************/
mysql> select word from words order by rand() limit 3;
// 使用explain命令查看该语句的执行情况
mysql> explain select word from words order by rand() limit 3;

通过学习前面 Mysql数据库 order by 实现原理,我们知道,对于使用InnoDB引擎的表来说,全字段排序,可以不用回表,所以,会被优先采用。但是,对于内存表来说,数据已经读到内存了,无非是根据数据行的位置,直接访问内存的得到,不会再访问磁盘。所以,对于使用内存临时表进行的排序来说,优化器没有了回表的顾虑,rowid 排序会被优先采用。

内存临时表

explain结果,Extra字段 Using temporary表示使用临时表,Using filesort表示需要排序。

SQL语句执行流程

  1. 创建临时表,使用memory引擎。该临时表有两个字段,一个 double 类型字段,我们记为R,一个varchar(64)类型字段,我们记为W,表上没有索引。
  2. 根据主键id的顺序,从 words 表中读取 word 值。对于每个 word 值,调用rand()生成一个0~1之间的随机小数,放到R字段,并将对应的 word 放入W字段。
  3. 此时,临时表中有10000行数据,我们需要根据 R 的值进行排序;
  4. 初始化 sort_buffer,确定放入 doule 和 int 两个字段;(其中:double字段保存临时表的R值,int字段保存“位置信息”,后面我们解释为什么保存的是位置信息)
  5. 从内存临时表中一行一行读取R和 “位置信息”放入sort_buffer;
  6. 根据R字段进行排序,排序完,取前三行结果,依次到内存临时表根据位置信息取出word值,返回。

Mysql表是如何定位一条记录的

答案是:rowid,每个引擎用来唯一标记行信息的标识。

  • 对于创建了主键的 InnoDB 表来说,rowid 就是主键ID;
  • 没有创建主键的 InnoDB 表,系统会默认生成一个6字节的 rowid 作为主键;
  • 对于Memory引擎的表,“位置信息”可以理解为数组的下标。

磁盘临时表

临时表一定是内存临时表吗?不一定。

参数 tmp_table_size 限制了内存临时表的大小,默认16 M,如果超过最大限制,就会使用磁盘临时表,磁盘临时表使用的引擎默认是 InnoDB,当使用磁盘临时表的时候,对应的就是一个没有显示索引的InnoDB表的排序过程。

排序优化

经过上面的排序完成后,所有的数据都有序了,但是我们只需要前三个,有点浪费。所以Mysql在5.6版本之后引入新的排序算法:优先队列排序算法(堆排序)。

mysql> select word from words order by rand() limit 3;

如何得到严格的随机结果

  1. 扫描全表,得到总行数C
  2. 取得 Y=floor(C * rand())。floor取整。
  3. select * from words limit Y,1 取得一行

limit Y, 1; 和 id >= Y limit 1; 一样吗?

不一样,MySQL 处理 limit Y,1 的做法就是按顺序一个一个地读出来,丢掉前 Y 个,然后把下一个记录作为返回结果。

笔记参考于极客时间《MySQL实战45讲》

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
算法 安全 大数据
【C/C++ 随机函数行为】深入探索C++中的随机数:std::random_device与rand的行为分析(二)
【C/C++ 随机函数行为】深入探索C++中的随机数:std::random_device与rand的行为分析
51 0
|
2月前
|
算法 安全 数据安全/隐私保护
【C/C++ 随机函数行为】深入探索C++中的随机数:std::random_device与rand的行为分析(一)
【C/C++ 随机函数行为】深入探索C++中的随机数:std::random_device与rand的行为分析
48 0
|
5月前
ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多)
ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多)
41 0
|
5月前
随机ID
随机ID工具类的编写
26 0
|
编译器 C语言 C++
C++中rand随机数的用法
C++标准函数库提供一随机数生成器rand,返回0-RAND_MAX之间均匀分布的伪随机整数。 RAND_MAX必须至少为32767。rand()函数不接受参数,默认以1为种子(即起始值)。 随机数生成器总是以相同的种子开始,所以形成的伪随机数列也相同,失去了随机意义。(但这样便于程序调试)
|
Oracle 关系型数据库 MySQL
MySQL窗口函数——分组排序函数:number_rank(),rank(),dense_rank()
MySQL窗口函数——分组排序函数:number_rank(),rank(),dense_rank()
404 0
MySQL窗口函数——分组排序函数:number_rank(),rank(),dense_rank()
【LeetCode470】用 Rand7() 实现 Rand10()(拒绝采样)
已知 rand_N() 可以等概率的生成[1, N]范围的随机数 那么: (rand_X() - 1) × Y + rand_Y() => 可以等概率的生成[1, X * Y]范围的等概率随机数 即实现了 rand_XY()
100 0
【LeetCode470】用 Rand7() 实现 Rand10()(拒绝采样)
【1145】Hashing - Average Search Time (25分)【hash 平方探测法】
【1145】Hashing - Average Search Time (25分)【hash 平方探测法】 【1145】Hashing - Average Search Time (25分)【hash 平方探测法】
89 0
【1051】Pop Sequence (25 分)
【1051】Pop Sequence (25 分) 【1051】Pop Sequence (25 分)
77 0
|
Unix Go
Go中使用Seed得到重复随机数的问题
重复的随机数 废话不多说,首先我们来看使用seed的一个很神奇的现象。 func main() { for i := 0; i < 5; i++ { rand.Seed(time.