共计 1259 个字符,预计需要花费 4 分钟才能阅读完成。
【问题】
最近有台服务器比较频繁的 CPU 报警,表现的特征有 CPU sys 占比偏高,大量慢查询,大量并发线程堆积。后面开发对 insert 的相关业务限流后,服务器性能恢复正常。
【异常期间线程处理情况】
下图是当时生产环境异常时抓取的信息,该事务正在执行 insert,已经执行 5 秒,线程运行在 innodb 内核,状态是 thread declared inside InnoDB,还有 4906 tickets 可用
统计了下有 64 个线程在 innodb 层,同时看到还有 280 个线程在排队等待进入 innodb 线程,状态是 sleeping before entering InnoDB
innodb 层的并发线程执行的 SQL 比较慢,产生了阻塞,导致了 MySQL 的并发线程堆积。
【哪些 SQL 执行慢】
从正在执行的 SQL 中,看到了 insert 的慢查询 SQL 语句,统计了下这句 SQL 批量插入大于 342 条记录 (SQL 被截断)
【批量 insert 的性能测试】
类似这种批量的 insert SQL 会对 MySQL 性能造成影响吗,多大的批次比较合理呢,做了下面测试
在测试服务器上新建测试表(表结构同生产环境),并定义了 5 个插入脚本,分别为单条 insert,每 10 条 1 个批次 insert,每 50 条 1 个批次 insert,每 100 条 1 个批次 insert,每 340 条 1 个批次 insert
用压测工具模拟 512 个并发线程的情况下,不同类型的 SQL 插入 100W 条记录服务器的性能情况,下表是压测统计
| 数据量 | 并发线程 | 执行时间(秒) | 每秒 insert | 慢查询数量 | Context switch | CPU 使用率 | CPU sys 占比 |
普通 insert(1 条) | 1000000 | 512 | 33 | 3W | 0 | 79W | 73% | 39% |
批量 SQL(10 条) | 1000000 | 512 | 23 | 4.3W | 0 | 49W | 78% | 56% |
批量 SQL(50 条) | 1000000 | 512 | 21 | 4.7W | 0 | 42W | 80% | 60% |
批量 SQL(100 条) | 1000000 | 512 | 15 | 6.6W | 20 | 53W | 70% | 45% |
批量 SQL(340 条) | 1000000 | 512 | 15 | 6.6W | 200 | 38W | 86% | 70% |
下图是批量 SQL(340 条)执行时的性能情况,可以看到当每 100 条记录一个批次执行 insert 时,开始出现慢查询,每 340 条 1 个批次执行 insert 时,在高并发的情况下,会产生大量的慢查询,这个现象接近于我们目前生产环境异常时的情况
【优化方案】
对于 MySQL 需要插入大量数据时,每次单条的 insert 性能较差,为了提升 insert 性能,我们采用了每批次多条记录同时 insert 的方法。
但当批次增大到一定数量时,在高并发访问的情况下,单个批次执行的性能会出现较大的下降,出现大量慢查询,并发线程堆积,CPU 上升出现瓶颈,innodb 层的并发线程处理被慢查询阻塞,后面只能通过限流来缓解性能问题。
根据上面的测试结论, 建议控制热表单个批次 insert 的记录条数,最好单个批次控制在 10 条左右 (因为即使调大到 50 条,插入性能没有大的提升,在高并发场景下,首先要保证当前 SQL 的执行性能)。
【优化后 CPU 告警消失,运行平稳】
: