本机用一个5万行GROUP BY的CTE验证:只引用一次时,WHERE条件被下推进CTE内部,GROUP BY退化成单个分组,整条查询在优化阶段就被常量折叠成了字面量,耗时只要0.0001秒左右;同一个CTE被自连接引用两次,优化器改为把它物化成一张临时表,耗时变成0.074秒。
本机5万行数据上验证:不加ORDER BY只需要1次排序;本以为外层ORDER BY和窗口函数的PARTITION BY+ORDER BY写成完全一样就能省掉一次排序,真实测出来并没有——窗口结果必须先落进临时表,顺序丢失,外层还是要重新排一次。
本机对5万行数据做ORDER BY,把sort_buffer_size分别调到32KB、默认256KB、64MB,用Sort_merge_passes这个状态变量验证:32KB真实触发了6次磁盘归并,默认256KB也真实触发了1次,只有调到64MB才真实做到0次。
本机把internal_tmp_mem_storage_engine切到旧版MEMORY引擎后验证:GROUP BY只聚合数字列,Created_tmp_disk_tables真实是0;一旦GROUP BY要带上一个真正的TEXT列,Created_tmp_disk_tables真实立刻变成1。换回默认的TempTable引擎,同一条查询又真实变回了0。
本机对同一个索引做ALTER TABLE ... INVISIBLE验证:索引可见时走索引范围扫描,真实耗时1.33毫秒;标记成不可见后优化器真实彻底无视它,退化成全表扫描,耗时变成18.5毫秒,慢了约14倍。
本机验证ORDER BY customer_id ASC, created_at DESC这种两列排序方向不一样的查询:普通复合索引真实还是要Sort;建一个方向完全匹配的降序复合索引后,Sort真实消失了,1.5万行的真实查询耗时从64.2毫秒降到26.0毫秒。
本机验证WHERE UPPER(note)='...'这种对列做了函数运算的查询:note列上一个普通索引真实完全帮不上忙,还是全表扫描;建一个函数索引后,真实变成Index lookup,5万行数据上从16.4毫秒降到0.22毫秒,快了约75倍。
本机对比同一个OR查询在高选择性(约7%行匹配)和低选择性(约92%行匹配)下的真实表现:高选择性时index_merge真实比全表扫描快3.3倍;低选择性时手动模拟index_merge的UNION写法,真实比优化器自己选的全表扫描慢了66%。
本机打开OPTIMIZER_TRACE,把上一篇index_merge的代价拆解成三个真实数字:读两条索引range本身只要355.08,排序rowid读磁盘要514.79,而按行ID去重真实要3657.61——去重这一步的代价,比另外两步加起来还高5倍多。
本机开两个真实会话验证:session A开一个REPEATABLE READ事务读到amount=148.08,session B把同一行改成9999.99并提交,A在自己事务内再读一次,真实还是148.08。用History list length验证:40个独立提交的事务真实让它从1涨到41。
本机在同一个REPEATABLE READ事务里连续做三次读:普通SELECT两次真实都读到快照里的1000.00,哪怕另一个会话中途已经把值改成500并提交;紧接着加上FOR UPDATE再读一次,真实立刻变成了500.00。
本机验证REPEATABLE READ下的幻读:session A用普通SELECT数了两次某个id范围的行数,B在中途真实插入一行新数据并提交,A的两次普通计数真实都是8;A紧接着换成SELECT...FOR UPDATE再数一次,真实变成了9——当前读能看见幻影行。