第十七讲

83 阅读3分钟

内存临时表

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默认是行级别的锁,当有明确指定的主键时候,是行级锁。否则是表级别。