内存临时表
order by rand()使用了内存临时表,内存临时表排序的时候使用了rowid排序方法。
Using temporary,表示的是需要使用临时表;
用order by rand() 的执行过程:
创建临时表 => 扫描全表,对于指定字段生成rand随机数 => 按照指定字段排序 => 初始化sort buffer 排序
tmp_table_size这个配置限制了内存临时表的大小,默认值是16M。如果临时表大小超过了tmp_table_size,那么内存临时表就会转成磁盘临时表。
磁盘临时表使用的引擎默认是InnoDB,是由参数internal_tmp_disk_storage_engine控制的。
优先队列算法
MySQL 5.6版本引入的一个新的排序算法,即:优先队列排序算法。
算法逻辑:
对于这准备排序的数据集,先取前N行,构造成一个堆;去下一行排序的值进行比较,每一次跟最大的值比较,如果小于则替换原有行,比较完成将得出所需前N的数据。
在OPTIMIZER_TRACE结果的filesort_priority_queue_optimization这个部分的chosen=true,就表示使用了优先队列排序算法。
只有获取排序数据的大小在sort_buffer_size 之内才会使用该算法,否则使用归并排序。
(18讲)
扫全表的一些坑
- 对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。
- 对字段进行运算操作,即便不会破坏有序性也不会使用索引。
- 输入参数字段与表结构定义的类型不一致,也会走全表扫描。
因为相当于将整列的类型进行转换后再进行比对查找(CAST(tradid AS signed int)).
在MySQL中,字符串和数字做比较的话,是将字符串转换成数字。 - 字段的字符集如果不一致,也会触发类型转换从而导致全表扫描。
(19讲)
一些阻塞语句返回的场景
第一类:查询长时间不返回
-
等MDL锁,对表结构进行更改的操作。(5.7已对mdl锁进行了优化)
通过查询sys.schema_table_lock_waits这张表,我们就可以直接找出造成阻塞的process id,把这个连接用kill 命令断开即可。mysql> select blocking_pid from sys.schema_table_lock_waits;
-
等flush
flush tables t with read lock;
# 如果没有指定具体的表名,则表示关闭MySQL里所有打开的表。
flush tables with read lock;
- 等行锁:在访某个记录时要加读锁,如果这时候已经有一个事务在这行记录上持有一个写锁,我们的select语句就会被堵住。5.7版本,可以通过sys.innodb_lock_waits 表查到。
mysql> select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`\G
#需要直接断开这个连接才能让她自动回滚。只是kill query 是无法释放改行的行锁。
mysql> kill pid
mysql> kill query pid
-
读模式:带lock in share mode的SQL语句,是当前读,直接读取当前记录最新的值。不带默认是一致性读,一致性读需要基于undo log执行后才能得到最新的值因此会比带lock in share mode的语句慢很多。
-
select for update :通过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。
for update 仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。InnoDB默认是行级别的锁,当有明确指定的主键时候,是行级锁。否则是表级别。