前事不忘,后事之师,不忘国耻!

 用户注册  找回密码
 用户注册
搜索
查看: 20|回复: 0

[开发应用] MySQL InnoDB 存储引擎与事务原理

[复制链接]

[开发应用] MySQL InnoDB 存储引擎与事务原理

[复制链接]
dbaai

主题

0

回帖

28

积分

DBAAI

积分
28
昨天 14:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?用户注册

×
MySQL InnoDB 存储引擎与事务原理


一、具体的问题

很多刚接触 MySQL 的开发,建表从没管过存储引擎,直到遇到"事务没回滚""行锁锁错行""断电后数据丢了"才回头补课。其实这几个现象的答案都指向同一个东西:你用的存储引擎是不是 InnoDB,以及你是否真的理解了事务的 ACID 是怎么落地的。本文把这两点一次说清,让你建表和下 SQL 时心里有数。

二、核心原理

1. 为什么默认是 InnoDB

InnoDB 支持事务、行级锁、外键和崩溃恢复(redo/undo 日志);而老的 MyISAM 只有表锁、无事务、断电易损。MySQL 5.5 起默认引擎就是 InnoDB。所以新表基本都该用 InnoDB,除非是纯只读、不在乎事务的日志表才考虑 MyISAM。

2. 事务的 ACID 靠什么实现


  • 原子性(A):undo log 记录每行修改前的镜像,回滚时按它反向恢复。
  • 一致性(C):由应用逻辑 + 约束(主键、唯一、外键)共同保证。
  • 隔离性(I):靠锁 + MVCC 多版本并发控制,让读写互不阻塞。
  • 持久性(D):redo log 先写(WAL),事务提交即保证落盘,断电也不丢已提交数据。


3. 四个隔离级别与典型现象

READ UNCOMMITTED(会脏读)> READ COMMITTED(不可重复读)> REPEATABLE READ(MySQL 默认,靠 GAP/Next-Key 锁防幻读)> SERIALIZABLE(串行化,最安全但性能最差)。绝大多数业务用默认的 RR 就够了。

4. MVCC 让你"读不加锁"

InnoDB 给每行维护隐藏的事务 id 和回滚指针,普通 SELECT 默认走"快照读"(ReadView),看到的是事务开始时已提交的数据,既不会被别人未提交的写阻塞,也不会阻塞别人的写。这正是高并发下读写互不影响的根本原因。只有 SELECT ... FOR UPDATE 这类"当前读"才会去抢锁。

三、实例参考(动手步骤)

下面用一张账户表演示"显式事务 + 回滚保障",并验证隔离级别。
  1. -- 1) 确认当前引擎与隔离级别
  2. SHOW VARIABLES LIKE 'default_storage_engine';
  3. SELECT @@transaction_isolation;
  4. -- 2) 显式建 InnoDB 表
  5. CREATE TABLE account (
  6.   id      INT PRIMARY KEY,
  7.   name    VARCHAR(20),
  8.   balance INT
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  10. INSERT INTO account VALUES (1,'A',1000),(2,'B',1000);
  11. -- 3) 转账:要么都成功,要么都回滚
  12. START TRANSACTION;
  13. UPDATE account SET balance = balance - 100 WHERE id = 1;
  14. UPDATE account SET balance = balance + 100 WHERE id = 2;
  15. COMMIT;   -- 若中途报错,执行 ROLLBACK; 而非 COMMIT
  16. -- 4) 在另一个会话用默认 RR 读取,看不到未提交改动
  17. --    (SESSION B)SELECT * FROM account;  -- 仍显示 1000/1000,直到 SESSION A COMMIT
复制代码

关键提醒:START TRANSACTION 之后必须显式 COMMITROLLBACK;当 autocommit=1 时单条 SQL 自带隐式提交,容易让人误以为"加了事务就生效",其实没包住多句。

四、一个常被忽略的坑:长事务拖垮整个库

曾有一次线上告警"锁等待超时",排查发现是一条报表查询忘了提交,事务开着跑了 40 分钟。它不但一直占着行锁,还让 undo 日志无法 purge,版本链越积越长,所有依赖老版本的快照读都跟着变慢。教训很直接:事务里绝不放 SELECT 之后 sleep、绝不做外部 HTTP 调用;查完立刻 COMMIT。用 SELECT * FROM information_schema.innodb_trx 能实时看到哪些事务跑了太久,是排查锁问题的第一抓手。

五、实操检查清单


  • 建表显式写 ENGINE=InnoDB,别依赖默认(老库可能还是 MyISAM)。
  • 涉及金额、库存、状态流转的操作,必须 START TRANSACTION ... COMMIT/ROLLBACK,异常分支要有 ROLLBACK
  • 长事务会拖住 undo 日志和行锁,尽量短小;绝不在事务里做外部 HTTP 调用或 sleep。
  • 需要"可重复读"的业务用默认 RR;若出现幻读,再考虑 GAP/Next-Key 锁或升到 SERIALIZABLE。
  • 普通查询用快照读即可,只有真正要锁住待改的行才用 SELECT ... FOR UPDATE
  • 定期 SHOW ENGINE INNODB STATUS 看锁等待与长事务,并用 innodb_trx 及时 kill 异常长事务。
  • 断电恢复后靠 redo 保证已提交不丢、靠 undo 回滚未提交,无需手工修数据。
免责申明1、欢迎访问本站,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@dbabbs.com
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

QQ|Archiver|小黑屋|DBA论坛中国 ( 鲁ICP备20017503号-2 )

GMT+8, 2026-8-15 03:19 , Processed in 0.012949 second(s), 10 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表