前言:从一句“事务失败了”追问到证据
应用捕获到数据库异常,往往只能得到一个错误码。但接下来该重试一条 SQL、回滚整个事务,还是先查询业务状态,答案并不相同。死锁、锁等待超时和提交响应丢失,表面都让一次调用失败,留下的数据库事实却可能完全不同。
本书以 MySQL 8.4 和 InnoDB 为基准,围绕账户、库存、订单和本地事件表,学习这些事实如何形成、怎样观察,以及怎样把业务约束写进可验证的事务。适合了解基本 SQL、会编写后端程序的读者;不要求先阅读整个数据库内核。
随书实验使用真正的 MySQL 服务。Python 与 PyMySQL 只是降低连接和并发实验的启动成本;隔离级别、锁、回滚、索引计划和提交结果都由 InnoDB 执行。没有使用字典或 mock 来替代数据库语义。
一条贯穿全书的业务约束
仓库里只剩一件商品,两位用户同时购买。最终库存没有负数,是否就说明没有超卖?如果两个请求都先读到 1,再分别把库存写成 0,两个订单都可能被确认。我们需要检查库存与订单共同满足的约束,而不是只盯着某个字段。
同一订单还会遇到重复提交、死锁、连接断开与事件发布。每增加一项能力,先问它依赖什么数据库保证,异常后又留下什么证据。整本书会不断回到这条线:业务事实、事务边界和恢复动作要相互匹配。
阅读与运行路线
前面的章节建立事务、快照、锁和索引模型;中间章节讨论并发库存、幂等、未知提交和 Outbox;后面的实验章带你读取实际输出,再做自己的故障评审。可以先运行实验,观察那些与直觉不同的结果,再回到对应原理。
本机没有数据库时,仓库的 GitHub Actions 使用隔离 MySQL 容器运行实验并保存证据。每个测试创建自己的随机数据库,结束后仅删除该测试创建的数据库。运行前仍应阅读实验说明,使用独立开发实例与公开的教学凭据,不应连接生产数据。
证据的范围
小数据集的执行计划能说明优化器在这个数据与配置下选择了什么,不能证明生产响应时间提升了多少。单节点事务能说明本地原子性,不能证明主从切换不丢数据或消息恰好发送一次。书中的案例都是教学场景,生产设计与未运行项会单独说明。
本文稿、EPUB 与在线阅读来自同一份 Markdown,代码附录从实际实验源码生成。希望读完之后,面对“数据库报错”,你能进一步说清:哪条语句失败、整个事务是否仍活跃、业务结果是否已提交,以及下一步凭什么执行。