
1. 高并发场景下的MySQL稳定性挑战在电商大促、秒杀活动或金融交易系统中数据库每秒需要处理成千上万的请求。我曾参与过一个票务系统的优化在演唱会门票开售时MySQL实例的QPS每秒查询量峰值达到3.2万此时系统开始出现响应延迟和部分请求超时。通过监控发现75%的延迟请求都集中在几个热门场次的库存更新操作上。这种场景下最典型的问题就是超卖当多个事务同时读取同一商品的库存比如剩余100张票都判断有库存后执行减库存操作最终导致实际售出数量超过库存量。更棘手的是这类问题在测试环境往往难以复现只有在真实的高并发压力下才会暴露。提示高并发不等于高流量关键指标是单位时间内对同一数据的争用程度。即使总QPS不高如果大量请求集中在少数数据行上同样会产生严重的并发问题。2. 事务机制ACID特性的实现原理2.1 原子性Atomicity的实现原子性依赖InnoDB的undo log实现。当执行一条UPDATE语句时先在undo log中记录修改前的数据镜像执行内存中数据页的修改生成redo log记录这次修改我曾遇到过一个案例批量更新10万条数据时服务器意外重启。重启后MySQL通过对比redo log和undo log自动回滚了未提交的部分修改保证了这10万条更新要么全部成功要么全部不生效。2.2 隔离性的四个级别不同的隔离级别通过锁和MVCC机制实现读未提交不加锁直接读最新数据读已提交每次读创建ReadView解决脏读可重复读MySQL默认第一次读创建ReadView并复用串行化所有读操作加共享锁在支付系统中我们遇到过这样的问题对账程序在读已提交级别下运行时由于其他事务不断提交新数据导致单次对账过程中看到的数据不一致。将隔离级别改为可重复读后问题解决但带来了更高的锁开销。3. 锁机制深度解析3.1 行锁的三种类型记录锁Record Lock锁定索引记录-- 对id1的记录加X锁 SELECT * FROM orders WHERE id1 FOR UPDATE;间隙锁Gap Lock锁定索引记录间的间隙-- 锁定id在(5,10)区间的间隙 SELECT * FROM orders WHERE id5 AND id10 FOR UPDATE;临键锁Next-Key Lock记录锁间隙锁的组合在用户积分变更场景中我们曾错误地在非唯一索引上使用临键锁导致大量更新请求串行化。通过改为只在主键上使用记录锁并发性能提升了8倍。3.2 死锁的产生与解决典型死锁场景事务A先锁定了行1然后请求行2事务B先锁定了行2然后请求行1互相等待形成死锁通过SHOW ENGINE INNODB STATUS可以查看最近发生的死锁信息。我们在日志系统中曾发现按不同顺序更新相同的两条日志记录会导致死锁解决方案是约定统一的更新顺序如按主键升序。4. 高并发优化实战方案4.1 库存扣减的三种模式悲观锁方案BEGIN; SELECT stock FROM products WHERE id1 FOR UPDATE; UPDATE products SET stockstock-1 WHERE id1; COMMIT;乐观锁方案UPDATE products SET stockstock-1 WHERE id1 AND stock1;预扣减异步确认方案适合秒杀先扣减Redis中的预扣库存异步队列处理数据库最终扣减在618大促中我们将热点商品的库存扣减从悲观锁改为乐观锁配合Redis缓存使下单TPS从1200提升到8500。4.2 索引设计对并发的影响不合理的索引会导致锁升级没有索引的更新会导致全表扫描MySQL会将所有扫描到的行加锁推荐为所有WHERE条件中的列建立合适索引我们曾有一个订单状态更新语句UPDATE orders SET status2 WHERE user_id100 AND status1;由于缺少(user_id, status)的联合索引导致锁定了上千条记录。添加索引后锁定行数降到个位数。5. 监控与调优要点5.1 关键性能指标锁等待时间SELECT * FROM sys.innodb_lock_waits;事务持续时间SELECT * FROM information_schema.INNODB_TRX;行锁冲突统计SHOW STATUS LIKE innodb_row_lock%;5.2 参数调优建议调整锁超时时间innodb_lock_wait_timeout5 # 默认50秒改为5秒优化事务大小单个事务影响行数控制在1000以内执行时间不超过1秒连接池配置最大连接数不超过CPU核心数的5倍启用连接复用在社交平台的私信系统中我们将innodb_lock_wait_timeout从默认50秒调整为3秒快速失败后重试的策略使系统在高峰期仍保持响应。6. 分布式事务的挑战当系统引入分库分表后单机事务机制不再适用。我们采用最终一致性方案处理跨库事务本地事务表消息队列方案// 伪代码示例 Transactional public void createOrder(Order order) { orderMapper.insert(order); // 本地事务 sendMessage(order); // 发到消息队列 }TCCTry-Confirm-Cancel模式Try阶段预留资源Confirm阶段确认使用Cancel阶段取消预留在跨境支付系统中由于网络延迟可能导致本地事务提交成功但消息发送失败。我们通过定时任务扫描本地事务表进行补偿确保最终一致性。