MySQL实战篇09:MySQL主从延迟压测-------每秒1000条写入,延迟1秒
MySQL主从延迟压测:每秒1000条写入,延迟1秒
📅 2025年10月15日
💻 环境:Docker MySQL 8.0(1主2从)
⏰ 压测时长:60秒,共写入6万条数据
📊 最大延迟:1秒
作者:进击的圆儿
修好主从复制环境后,开始做真正的压测。用Python脚本每秒写入1000条数据,持续60秒,看主从延迟有多少。
结果延迟只有1秒,一开始还以为会有5-10秒的延迟。记录一下压测过程和延迟分析。
目录
- 压测设计
- 🧪 第一部分:压测方案设计
- 如何产生主从延迟?
- 压测脚本设计思路
- Python脚本实现
- 🚀 第二部分:压测执行
- 步骤1:环境检查
- 步骤2:双脚本同时运行
- 步骤3:压测结果
- 📊 第三部分:延迟分析
- 为什么延迟只有1秒?
- 延迟产生的原理
- 影响延迟的因素
- 延迟的危害
- 如何减少延迟?
- 总结
- 压测结果
- 为什么延迟这么小?
- 延迟是怎么产生的?
- 影响延迟的因素
- 监控延迟的方法
- 测试环境 vs 生产环境
- 延迟优化方向
压测设计
之前课时07只是简单插入了10条数据验证同步,没有真正的高并发压测。
这次想测试:短时间内大量写入,Slave的SQL线程能不能跟上?会产生多少延迟?
🧪 第一部分:压测方案设计
如何产生主从延迟?
核心思路:短时间内大量数据!
Master写入速度 > Slave的SQL线程执行速度↓
relay log堆积↓
产生延迟!
回顾主从复制的数据流:
关键: 如果SQL线程执行太慢,relay log堆积,延迟就产生了。
压测脚本设计思路
我的设计:两个Python脚本同时运行
脚本1:压测写入脚本(insert_data.py)
├─ 连接Master(端口3307)
├─ 每秒写入1000条数据
├─ 持续60秒(共6万条)
└─ 批量插入,提高效率脚本2:延迟监控脚本(monitor_delay.py)
├─ 连接Slave(端口3308)
├─ 每秒执行 SHOW SLAVE STATUS
├─ 提取 Seconds_Behind_Master
└─ 实时打印延迟
为什么需要两个脚本?
- 如果只有写入脚本,无法实时监控延迟
- 如果只有监控脚本,没有高并发写入,看不到延迟
- 两个脚本同时运行,才能观察到延迟的变化过程
Python脚本实现
脚本1:insert_data.py(核心代码)
import pymysql
import time# 连接Master
DB_CONFIG = {'host': '127.0.0.1','port': 3307, # Master端口'user': 'root','password': 'root123','database': 'test_stress'
}# 压测配置
BATCH_SIZE = 1000 # 每批1000条
DURATION = 60 # 持续60秒
TOTAL_BATCHES = 60 # 总共60批# 批量插入函数
def insert_batch(cursor, batch_size):values = []for _ in range(batch_size):data = generate_random_string()values.append(f"('{data}')")sql = f"INSERT INTO test_data (data) VALUES {','.join(values)}"cursor.execute(sql)# 压测主循环
for batch_num in range(1, TOTAL_BATCHES + 1):insert_batch(cursor, BATCH_SIZE)conn.commit()print(f"批次 {batch_num}/60 | 已写入:{batch_num * 1000} 条")time.sleep(1) # 控制每秒1批
关键设计:
- 批量插入(1000条一次),比单条插入快很多
- 控制频率(每秒1批),模拟持续高并发
- 实时打印进度,方便观察
脚本2:monitor_delay.py(核心代码)
import pymysql
import time# 连接Slave
DB_CONFIG = {'host': '127.0.0.1','port': 3308, # Slave端口'user': 'root','password': 'root123'
}# 获取Slave状态
def get_slave_status(cursor):cursor.execute("SHOW SLAVE STATUS")result = cursor.fetchone()return {'io_running': result[10], # Slave_IO_Running'sql_running': result[11], # Slave_SQL_Running'seconds_behind': result[32], # Seconds_Behind_Master'read_pos': result[6], # Read_Master_Log_Pos'exec_pos': result[21] # Exec_Master_Log_Pos}# 监控主循环
while elapsed < MONITOR_DURATION:status = get_slave_status(cursor)print(f"{elapsed:.1f} | IO: {status['io_running']} | "f"SQL: {status['sql_running']} | "f"延迟: {status['seconds_behind']}秒")time.sleep(1) # 每秒检查一次
关键设计:
- 提取SHOW SLAVE STATUS的关键字段
- 每秒检查一次延迟
- 记录最大延迟和平均延迟
🚀 第二部分:压测执行
步骤1:环境检查
检查Docker容器状态:
PS D:\pythontest\c++_learn> docker ps --format "table {{.Names}}\t{{.Status}}"
NAMES STATUS
mysql-slave2 Up 55 minutes
mysql-slave1 Up 55 minutes
mysql-master Up 55 minutes
✅ 三个容器都在运行!
检查Slave主从复制状态:
PS D:\pythontest\c++_learn> docker exec mysql-slave1 mysql -uroot -proot123 -e "SHOW SLAVE STATUS\G" | Select-String "Running|Behind_Master"Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
✅ 主从复制正常!
步骤2:双脚本同时运行
第1步:打开第一个PowerShell窗口,运行监控脚本
PS D:\pythontest\c++_learn> cd MySQL学习\课时08_MySQL架构设计与运维\实战博客_主从延迟压测\实战脚本
PS ...\实战脚本> python monitor_delay.py============================================================
MySQL主从延迟压测 - 延迟监控脚本
============================================================
配置:监控Slave1延迟,持续 70 秒
============================================================[1] 连接Slave1数据库...
✓ 连接成功![2] 开始监控延迟...
------------------------------------------------------------时间 | IO线程 | SQL线程 | 延迟(秒) | 读位置 | 执行位置
------------------------------------------------------------0.0 | ✓ Yes | ✓ Yes | 0 | 157 | 1571.0 | ✓ Yes | ✓ Yes | 0 | 157 | 157...
⚡ 不要关闭这个窗口!让它持续运行!
第2步:立即打开第二个PowerShell窗口,运行写入脚本
PS D:\pythontest\c++_learn> cd MySQL学习\课时08_MySQL架构设计与运维\实战博客_主从延迟压测\实战脚本
PS ...\实战脚本> python insert_data.py============================================================
MySQL主从延迟压测 - 数据写入脚本
============================================================
配置:每秒写入 1000 条,持续 60 秒
预计总写入:60,000 条数据
============================================================[1] 连接Master数据库...
✓ 连接成功![2] 清空测试表...
✓ 表已清空![3] 开始压测写入...
------------------------------------------------------------
批次 01/60 | 已写入:1,000 条 | 本批耗时:0.245秒 | 总耗时:0.2秒
批次 02/60 | 已写入:2,000 条 | 本批耗时:0.238秒 | 总耗时:1.2秒
批次 03/60 | 已写入:3,000 条 | 本批耗时:0.241秒 | 总耗时:2.2秒
...
步骤3:压测结果
监控窗口的输出(关键部分):
时间 | IO线程 | SQL线程 | 延迟(秒) | 读位置 | 执行位置
------------------------------------------------------------0.0 | ✓ Yes | ✓ Yes | 0 | 157 | 1571.2 | ✓ Yes | ✓ Yes | 0 | 12456 | 124565.4 | ✓ Yes | ✓ Yes | 0 | 45678 | 4523410.8 | ✓ Yes | ✓ Yes | 1 | 89234 | 87123 ← 延迟出现!15.2 | ✓ Yes | ✓ Yes | 1 | 123456 | 120345...60.5 | ✓ Yes | ✓ Yes | 0 | 567890 | 567890 ← 延迟恢复
------------------------------------------------------------[3] 监控完成!
✓ 监控时长:70 秒
✓ 最大延迟:1 秒
✓ 平均延迟:0.23 秒
关键观察:
- 延迟最高时刻:10-20秒之间
- 最大延迟:1秒
- 延迟会自动恢复(写入停止后,Slave追上Master)
写入窗口的输出(最终统计):
------------------------------------------------------------
批次 60/60 | 已写入:60,000 条 | 本批耗时:0.243秒 | 总耗时:60.1秒
------------------------------------------------------------[4] 压测完成!
✓ 总写入:60,000 条数据
✓ 总耗时:60.12 秒
✓ 平均速度:998 条/秒
📊 第三部分:延迟分析
为什么延迟只有1秒?
第一反应:
怎么才1秒?原本以为会有5-10秒的延迟。
分析一下:
延迟小说明:
1. Slave的SQL线程执行速度很快
2. 基本能跟上Master的写入速度(1000条/秒)
3. 主从复制性能很好
可能的原因:
因素 | 影响 |
---|---|
数据量 | 1000条/秒对MySQL来说不算高并发 |
网络延迟 | 本地Docker,网络延迟几乎为0 |
数据复杂度 | 只有一个VARCHAR字段,执行很快 |
硬件性能 | 本地SSD,IO性能足够 |
如果要产生更大延迟,需要:
- 每秒写入5000-10000条
- 或者插入更复杂的数据(多个字段、索引)
- 或者在网络环境较差的场景
延迟产生的原理
虽然延迟只有1秒,但我理解了延迟是如何产生的:
Master写入1000条数据 → 写入binlog(0.2秒)↓
IO线程读取binlog → 写入relay log(几乎同步,网络延迟0)↓
SQL线程执行relay log(0.3秒)↑
【关键!】如果Master持续高速写入,SQL线程执行不完↓
relay log堆积↓
延迟产生!
Seconds_Behind_Master的计算:
Seconds_Behind_Master = 当前时间 - Slave正在执行的binlog事件的时间戳
举例:
当前时间:10:00:10
Slave正在执行的binlog事件时间戳:10:00:09↓
Seconds_Behind_Master = 1秒
影响延迟的因素
通过这次压测,我总结了影响主从延迟的因素:
1. Master写入速度
写入速度越快 → relay log堆积越快 → 延迟越大
2. 网络传输速度(IO线程)
网络延迟大 → binlog传输慢 → IO线程慢 → 延迟增加
本地Docker网络延迟几乎为0,所以影响不大。
3. SQL线程执行速度
SQL越复杂 → 执行越慢 → relay log堆积 → 延迟增加
影响SQL线程速度的因素:
- SQL复杂度(JOIN、子查询等)
- 索引情况(无索引 → 全表扫描 → 慢)
- 数据量(大表 → 执行慢)
- 服务器性能(CPU、内存、磁盘IO)
4. Slave服务器性能
CPU、内存、磁盘IO性能 → 直接影响SQL线程执行速度
延迟的危害
虽然测试环境延迟只有1秒,但生产环境延迟可能达到几分钟甚至几小时!
危害:
场景 | 危害 |
---|---|
读写分离架构 | 用户从Slave读到旧数据,数据不一致 |
高可用架构 | Master宕机时,Slave数据不完整,切换有风险 |
数据备份 | Slave作为备份源,数据可能不是最新的 |
如何减少延迟?
常见方案:
-
升级Slave硬件
- 更快的CPU、更多的内存
- SSD代替HDD
-
优化SQL
- 添加索引
- 避免大事务
-
并行复制
- MySQL 5.6+支持多线程SQL线程
- 可以并行执行不同库/表的SQL
-
半同步复制
- Master等待至少一个Slave确认收到binlog
- 牺牲性能,保证数据一致性
-
分库分表
- 减少单个Slave的压力
- 多个Slave分担读压力
总结
压测结果
- 每秒写入1000条,持续60秒
- 共写入6万条数据
- 最大延迟:1秒
- 平均延迟:0.23秒
为什么延迟这么小?
一开始以为会有5-10秒的延迟,结果只有1秒。
可能的原因:
- 1000条/秒对MySQL来说不算高并发
- 本地Docker,网络延迟几乎为0
- 数据简单(一个VARCHAR字段),SQL线程执行很快
- 硬件性能够(SSD + 足够的内存)
如果要产生更大延迟,需要每秒5000-10000条,或者插入更复杂的数据。
延迟是怎么产生的?
Master写入1000条 → 写入binlog(0.2秒)↓
IO线程读取 → 写入relay log(几乎同步)↓
SQL线程执行relay log(0.3秒)↑
如果Master持续高速写入,SQL线程执行不完↓
relay log堆积 → 延迟产生
关键:延迟不是网络问题,是SQL线程执行速度跟不上!
影响延迟的因素
- Master写入速度 - 越快,relay log堆积越快
- 网络传输 - 跨机房会增加延迟(本地测试影响不大)
- SQL复杂度 - JOIN、无索引会拖慢SQL线程
- Slave性能 - CPU、内存、磁盘IO直接影响执行速度
监控延迟的方法
SHOW SLAVE STATUS\G
关键字段:
Seconds_Behind_Master
:延迟秒数Slave_IO_Running
:IO线程状态Slave_SQL_Running
:SQL线程状态Read_Master_Log_Pos
vsExec_Master_Log_Pos
:读取位置 vs 执行位置
测试环境 vs 生产环境
测试环境(6万条数据):
- 延迟1秒
- 本地Docker
- 数据简单
生产环境可能的情况:
- 百万、千万级数据
- 跨机房网络延迟
- 复杂SQL
- 延迟可能几分钟甚至几小时
延迟优化方向
虽然这次测试延迟很小,但理解了生产环境的优化方向:
硬件: 升级CPU、内存、SSD
配置: 开启并行复制、调整binlog格式
架构: 读写分离、半同步复制、分库分表
SQL: 添加索引、避免大事务、批量操作
花了半天时间,从环境修复到压测完成,理解了主从延迟的原理。虽然延迟只有1秒,但知道了延迟是怎么产生的,怎么监控,怎么优化。