有关MySQL insert源码分析的文章,可以参看阿里云云栖社区的文章,例如:

  1. 一条insert语句的执行过程

基于insert源码分析,和MySQL事务的一般过程,可以看出insert语句执行过程中几个可能的瓶颈点,包括加锁,io和网络几个方面,

  1. 外键检查对主表加S行锁
  2. insert转update操作需要的对老记录index entry的行锁
  3. iops限制
  4. 写binlog
  5. semi-sync消息延迟

下面我们来看几个实际的例子,每个例子都会尽量和读者一起来分享一下根因定位方法和问题规避建议,

MDL锁等待阻塞DML语句的问题比较常见,一般都是因为表上有运行时间比较长的DDL语句在运行,比如Optimize, Truncate table,Alter table等,诊断方法也很简单, 通过运行show processlist,就会看到被阻塞的语句的状态是 ,如果权限足够的话,还可以看到blocker thread, 规避的方法是避免在业务高峰运行DDL语句,特别是耗时很长的对大表的DDL。

外键检查等待对主表记录的S锁

如果是insert的目标表有定义外键依赖,MySQL需要做参照完整性(RI)检查,会对主表的对应记录加S锁。如果主表记录上正好有没有提交的修改,就会带来insert事务的锁等待。 在笔者写这篇文章的时候,RDS的慢日志项目里面的lock time字段并没有清晰的反映这种因为RI带来的锁等待时间消耗。 在问题出现的当场,运行下面的query查看I_S表,找出阻塞关系。这种方法有其局限性,只能抓现行,如果阻塞已经消除了,后期就看不到了。

阿里云MySQL RDS有计划会改进慢SQL lock time的记录,记录外键检查对主表记录S锁的等待时间; 另外,还会将所有发生过阻塞的thread的信息记录在内存表里,供后期问题排查使用。

如果新插入的记录项已经存在,但已经被标记为已删除,或者是使用了INSERT ON DUPLICATE KEY UPDATE, MySQL会将insert操作转成update操作。就会对老的index 项目加上X锁(如果是cluster index对应的记录则加S锁),来确保原有记录的删除/修改事务已经提交。

上面是多条INSERT ON DUPLICATE KEY UPDATE并发执行导致的死锁,在master error log里面可以观测到。

这种问题的诊断可以通过查看慢日志的lock time字段来判断;如果发生了死锁,还可以查看master error log,也可类似MDL锁等待,从I_S表查看当前的阻塞关系。

iops限制

笔者曾经遇到过insert作业时不时会变慢,但前端业务并没有什么高峰尖刺。后台分析可以看到用户binlog使用很重,大概每2分钟就会产生将近600M的binglog文件,依赖rds的系统诊断工具上也能观察到iops 打满的问题。

因为慢insert有明显的时间规律,怀疑有定期批处理。和客户沟通后,确认客户有设置event scheduler,每分钟会进行全表的数据归集操作,io压力比较大。后期让客户调整他们的数据归集逻辑后,insert慢的问题消失。

这个调用栈很明显的说明了问题所在,就是binlog的磁盘没有剩余空间了,导致insert hang住。清理出足够的磁盘空间后,insert执行结束。

需要指出的是,MySQL 8.0增加了新的thread stage 来提示等待磁盘空间,来辅助诊断,对应的commit id是’6de594adf488add4514884d18c337745b1d227fb’。

semi-sync消息延迟

在有主备复制环境下,semi-sync消息延迟也可能导致insert变慢。笔者遇到一个慢insert,杜康慢日志显示此insert的执行时间正好是1秒。

查看master error log,发现在insert出现的时间点正好有这种信息

而系统参数rpl_semi_sync_master_timeout也正好是1秒。因此,可以判定是因为网络抖动,semi-sync消息延迟导致的insert变慢。

mysql insert语句执行慢的可能原因比较多,涵盖cpu,io,网络,锁等,在本文中分析了MySQL实例负载大,唯一键update、外键检查加锁带来的锁等待,semi-sync的网络开销,磁盘io限制,binlog日志满等导致的insert慢的问题。具体的原因需要具体分析,分析方法包括查看mysql已有的master-error log, slow log,另外,阿里云rds提供了强大的图形化工具包括我们查看系统负载曲线,慢日志详情,系统卡慢归总信息等,rds mysql内核团队有对定制系统诊断的能力,例如为慢日志添加更细的指标信息,包括锁等待时间等,并且也在一直加强对诊断问题缺失信息的补全,这些信息对于我们分析各种MySQL的性能问题都有很大的辅助。另外,保持MySQL版本的及时更新也是减少慢query或者是加快慢query诊断的推荐做法。