知识卡片

把大DELETE切成多个小批次,减少锁持有和复制延迟

普通读书笔记卡

内容

一次性执行影响大量行的语句(典型如按时间条件批量清理旧数据的DELETE), 表面上看是”一条语句搞定,效率最高”,但代价往往被低估:这一条语句要在 它触及的行范围上长时间持有锁,可能占满整个事务日志、耗尽系统资源,还会 阻塞其他本来很小但重要的并发查询,在有复制的架构下还会拉长复制延迟—— 因为这条大语句在主库上执行多久,从库重放它就要花多久,期间从库的复制 线程被这一条语句独占。解决办法不是让这条语句本身变快,而是把它”切分” 成多个功能完全一致、每次只处理一小部分的小查询,循环重复执行直到没有 更多行需要处理(例如按LIMIT 10000分批删除,直到某一批的影响行数为0)。 每一批之间还可以主动加入短暂停顿,把原本集中在一瞬间的压力摊开到更长 的时间窗口里,进一步压低对服务器整体负载和锁持有时间的影响。这个技巧 背后的思路,和 [[分片计数器把单行互斥锁拆成多行分散并发写入]]“把集中的资源争用拆散”是 同一类权衡:用更多次、更小粒度的操作,换取每次操作对系统的冲击更小、 可控性更强,代价是总执行时间可能变长、逻辑上多了一层循环控制。

参考来源

- 位置:《高性能MySQL:第3版》第6章"查询性能优化"6.3.2节"切分查询" (源文件:_epub-src/OEBPS/Text/part0013.xhtml) - 结论依据:原文明确"定期地清除大量数据时,如果用一个大的语句一次性 完成的话,则可能需要一次锁住很多数据、占满整个事务日志、耗尽系统 资源、阻塞很多小的但重要的查询。将一个大的DELETE语句切分成多个较小 的查询可以尽可能小地影响MySQL性能,同时还可以减少MySQL复制的延迟", 直接说明大语句的具体代价及切分成小批次的解决思路。 - 原始内容:将一个大的DELETE语句切分成多个较小的查询可以尽可能小地 影响MySQL性能,同时还可以减少MySQL复制的延迟……一次删除一万行数据 一般来说是一个比较高效而且对服务器影响也最小的做法。