# 建表
CREATE TABLE `words` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`word` VARCHAR(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB;
# 插入数据
DELIMITER ;;
CREATE PROCEDURE idata()
BEGIN
DECLARE i INT;
SET i=0;
WHILE i<10000 DO
INSERT INTO words(word) VALUES(CONCAT(CHAR(97+(i DIV 1000)), CHAR(97+(i % 1000 DIV 100)), CHAR(97+(i % 100 DIV 10)), CHAR(97+(i % 10))));
SET i=i+1;
END WHILE;
END;;
DELIMITER ;
CALL idata();
# 每行随机一个数字之后,排序
select word from words order by rand() limit 3;
tmp_table_size
默认 16M,超过了才会转为磁盘临时表),回表过程只是简单地根据数据行的位置,直接访问内存得到数据,根本不会导致多访问磁盘,MySQL 就会选择 rowid 排序# 查看慢日志存储位置
show variables like '%slow%';
# 开启慢查询
set global slow_query_log=ON;
# 记录了包含所有执行时间超过参数 long_query_time 的 sql
set long_query_time=0;
select word from words order by rand() limit 3;
5. 小结:order by rand() 使用了内存临时表,内存临时表排序的时候使用了 rowid 排序方法
tmp_table_size
限制了内存临时表的大小,默认值是 16M。如果临时表大小超过了 tmp_table_size
,那么内存临时表就会转成磁盘临时表internal_tmp_disk_storage_engine
控制set tmp_table_size=1024;
set sort_buffer_size=32768;
set max_length_for_sort_data=16;
/* 打开 optimizer_trace,只对本线程有效 */
SET optimizer_trace='enabled=on';
/* 执行语句 */
select word from words order by rand() limit 3;
/* 查看 OPTIMIZER_TRACE 输出 */
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G
4. 分析
sort_buffer_size
的 32768 字节,但是 num_initial_chunks_spilled_to_disk 却为 0?使用了优先队列排序算法,其实只要取 R 的最小的 3 个 rowid
对于这 10000 个准备排序的 (R,rowid),先取前三行,构造成一个堆
取下一个行 (R’,rowid’),跟当前堆里面最大的 R 比较,如果 R’小于 R,把这个 (R,rowid) 从堆中去掉,换成 (R’,rowid’)
重复第 2 步,直到第 10000 个 (R’,rowid’) 完成比较
SELECT MAX(id),MIN(id) INTO @M,@N FROM t ;
SET @X= FLOOR((@M-@N+1)*RAND() + @N);
SELECT * FROM t WHERE id >= @X LIMIT 1;
SELECT COUNT(*) INTO @C FROM t;
SET @Y = FLOOR(@C * RAND());
SET @sql = CONCAT("select * from t limit ", @Y, ",1");
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;