MySQL · 案例分析 · RDS MySQL线上实例insert慢常见原因分析

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 概述insert慢是经常被问到的问题,笔者尝试在本文中对这个问题做一个分类梳理,列举的线上例子会做简化,希望对读者有所启发。注意:因为阿里云MySQL线上实例还是以RDS 5.6为主体,本文的分析也是以5.6 innodb 引擎为主,其他版本的rds的实例可能略有差别。

概述

insert慢是经常被问到的问题,笔者尝试在本文中对这个问题做一个分类梳理,列举的线上例子会做简化,希望对读者有所启发。 注意:因为阿里云MySQL线上实例还是以RDS 5.6为主体,本文的分析也是以5.6 innodb 引擎为主,其他版本的rds的实例可能略有差别。

insert几个可能的性能瓶颈点

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

  1. 一条简单insert语句的调用栈
  2. 一条insert语句的执行过程

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

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

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

insert语句慢示例分析

MDL锁等待

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

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

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

SELECT
r.trx_id waiting_trx_id,
r.trx_mysql_thread_id waiting_thread,
r.trx_query waiting_query,
b.trx_id blocking_trx_id,
b.trx_mysql_thread_id blocking_thread,
b.trx_query blocking_query,
(Unix_timestamp() - Unix_timestamp(r.trx_started)) blocked_time
from information_schema.innodb_lock_waits w
inner join information_schema.innodb_trx b
on b.trx_id = w.blocking_trx_id
inner join information_schema.innodb_trx r
on r.trx_id = w.requesting_trx_id

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

外键检查对主表记录加锁慢的规避方法是加速释放主表的X行锁,避免长事务;或者是通过业务而不是MySQL数据库来保证参照完整性。

insert转update操作需要拿老记录index entry上的S/X锁

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

------------------------
LATEST DETECTED DEADLOCK
------------------------
...
*** (1) TRANSACTION:
TRANSACTION 82234303407, ACTIVE 0.000 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1184, 2 row lock(s), undo log entries 1
LOCK BLOCKING MySQL thread id: 1275826132 block 1242176372
MySQL thread id 1242176372, OS thread handle 0x2abbbf103700, query id 30359943998 ...  push_service update
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 12214 page no 672677 n bits 264 index ... of table ... trx id 82234303407 lock_mode X waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
...

*** (2) TRANSACTION:
TRANSACTION 82234303405, ACTIVE 0.001 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
INSERT INTO ... ON DUPLICATE KEY UPDATE ...
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 12214 page no 672677 n bits 264 index `...` of table ... trrx id 82234303405 lock_mode X
Record lock, heap no 10 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
...

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 12214 page no 672677 n bits 264 index `...` of table ...  trx id 82234303405 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
...

*** WE ROLL BACK TRANSACTION (1)

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

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

iops限制

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

iops full.png

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

写binlog延迟

笔者还遇到一条insert语句一直没有成功返回, show processlist显示该条insert语句的状态是query end。笔者打印了当时的pstack信息,发现了如下的调用栈

#0  0x0000003c00eab91d in nanosleep () from /lib64/libc.so.6
#1  0x0000003c00eab790 in sleep () from /lib64/libc.so.6
#2  0x00000000009967c6 in wait_for_free_space ()
#3  0x00000000009b29d9 in my_write ()
#4  0x000000000099a547 in my_b_flush_io_cache ()
#5  0x0000000000958892 in MYSQL_BIN_LOG::ordered_commit(THD*, bool, bool) ()
#6  0x0000000000959214 in MYSQL_BIN_LOG::commit(THD*, bool) ()
#7  0x000000000062c870 in ha_commit_trans(THD*, bool, bool) ()
#8  0x00000000008200e9 in trans_commit_stmt(THD*) ()
#9  0x000000000078aba1 in mysql_execute_command(THD*) ()
#10 0x00000000007910b0 in mysql_parse(THD*, char*, unsigned int, Parser_state*) ()
#11 0x0000000000792805 in dispatch_command(enum_server_command, THD*, char*, unsigned int) ()
#12 0x0000000000754535 in do_handle_one_connection(THD*) ()
#13 0x0000000000754589 in handle_one_connection ()
#14 0x0000003c01207851 in start_thread () from /lib64/libpthread.so.0
#15 0x0000003c00ee767d in clone () from /lib64/libc.so.6

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

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

semi-sync消息延迟

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

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

...[Warning] Timeout waiting for reply of binlog (file: mysql-bin.000903, pos: 2519326), semi-sync up to file mysql-bin.000903, position 2519003.

而系统参数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诊断的推荐做法。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
3月前
|
存储 消息中间件 监控
MySQL 到 ClickHouse 明细分析链路改造:数据校验、补偿与延迟治理
蒋星熠Jaxonic,数据领域技术深耕者。擅长MySQL到ClickHouse链路改造,精通实时同步、数据校验与延迟治理,致力于构建高性能、高一致性的数据架构体系。
MySQL 到 ClickHouse 明细分析链路改造:数据校验、补偿与延迟治理
|
4月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
181 3
|
4月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。
|
3月前
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
558 5
|
4月前
|
SQL 关系型数据库 MySQL
Mysql数据恢复—Mysql数据库delete删除后数据恢复案例
本地服务器,操作系统为windows server。服务器上部署mysql单实例,innodb引擎,独立表空间。未进行数据库备份,未开启binlog。 人为误操作使用Delete命令删除数据时未添加where子句,导致全表数据被删除。删除后未对该表进行任何操作。需要恢复误删除的数据。 在本案例中的mysql数据库未进行备份,也未开启binlog日志,无法直接还原数据库。
|
4月前
|
缓存 监控 关系型数据库
使用MYSQL Report分析数据库性能(上)
最终建议:当前系统是完美的读密集型负载模型,优化重点应放在减少行读取量和提高数据定位效率。通过索引优化、分区策略和内存缓存,预期可降低30%的CPU负载,同时保持100%的缓冲池命中率。建议每百万次查询后刷新统计信息以持续优化
264 6
|
4月前
|
缓存 监控 关系型数据库
使用MYSQL Report分析数据库性能(中)
使用MYSQL Report分析数据库性能
182 1
|
4月前
|
存储 弹性计算 关系型数据库
如何通过控制台创建RDS MySQL实例
本文介绍了通过控制台创建RDS MySQL实例的详细步骤,包括准备工作、选择计费方式、地域、实例规格、存储空间等关键配置,并指导用户完成下单与实例查看。
|
5月前
|
存储 关系型数据库 MySQL
深入理解MySQL索引类型及其应用场景分析。
通过以上介绍可以看出各类MySQL指标各自拥有明显利弊与最佳实践情墁,在实际业务处理过程中选择正确型号极其重要以确保系统运作流畅而稳健。
214 12
|
6月前
|
存储 SQL 关系型数据库
MySQL的Redo Log与Binlog机制对照分析
通过合理的配置和细致的管理,这两种日志机制相互配合,能够有效地提升MySQL数据库的可靠性和稳定性。
221 10

相关产品

  • 云数据库 RDS MySQL 版
  • 云数据库 RDS
  • 推荐镜像

    更多