事务和锁是 MySQL 中保证数据正确性与并发安全的核心机制。查询数据更多关注“如何把数据取出来”,而事务与锁关注的是“多个人同时操作数据时,如何保证结果仍然正确”。
事务(Transaction)是一组不可再分的 SQL 操作,这组操作要么全部执行成功,要么全部执行失败,不会只完成一部分。
典型场景是转账:
- A 账户扣款
- B 账户加款
这两个步骤必须作为一个整体执行。如果只扣款成功、加款失败,数据就会出现错误,因此需要事务保证一致性。
事务通常用 ACID 来描述:
- 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败。
- 一致性(Consistency):事务执行前后,数据都必须处于合法状态。
- 隔离性(Isolation):多个事务并发执行时,彼此之间尽量不互相干扰。
- 持久性(Durability):事务一旦提交,结果就会被持久保存。
MySQL 中常见的事务控制语句如下:
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
COMMIT;如果执行过程中发现异常,可以回滚:
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
ROLLBACK;其中:
START TRANSACTION表示开启事务COMMIT表示提交事务ROLLBACK表示回滚事务
锁(Lock)是数据库在并发场景下保护数据的一种机制。当多个会话同时读写同一批数据时,MySQL 通过加锁来避免脏数据、覆盖更新和不一致问题。
可以把锁理解为“先占用资源,再进行操作”的规则。谁先拿到锁,谁就先操作;其他事务需要等待或按隔离规则读取。
- 共享锁(Shared Lock,S 锁):允许多个事务同时读取同一条数据,但不允许修改。
- 排他锁(Exclusive Lock,X 锁):拿到锁的事务可以读写该数据,其他事务通常不能再加共享锁或排他锁。
- 行锁:只锁定某几行记录,并发能力更强,影响范围更小。
- 表锁:锁定整张表,实现简单,但并发性能较低。
InnoDB 一般以行锁为主,因此更适合高并发业务场景。
如果没有锁,在并发环境下容易出现以下问题:
- 脏读:读取到另一个事务尚未提交的数据。
- 不可重复读:同一个事务中两次读取同一条记录,结果不同。
- 幻读:同一个事务中两次按条件查询,第二次多出或少了记录。
- 丢失更新:多个事务同时修改同一条记录,后一次覆盖前一次结果。
MySQL 常见事务隔离级别包括:
- READ UNCOMMITTED:隔离级别最低,可能出现脏读。
- READ COMMITTED:只能读到已提交数据,能避免脏读。
- REPEATABLE READ:同一事务中多次读取结果保持一致,是 InnoDB 默认级别。
- SERIALIZABLE:隔离级别最高,并发能力最低,安全性最强。
隔离级别越高,数据越安全,但并发性能通常越低,因此实际使用时要在一致性与性能之间平衡。
可以用下面的语句查看当前会话的隔离级别:
SELECT @@transaction_isolation;也可以设置当前会话的隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;- 事务尽量短小,避免长事务占用锁太久。
- 尽量通过索引命中目标行,减少锁范围。
- 先查询再更新的业务,要注意并发竞争问题。
- 批量更新和大事务要谨慎,避免阻塞其他会话。
- 在高并发系统中,要结合事务隔离级别一起分析锁问题。
事务解决的是“操作要不要作为一个整体成功”的问题,锁解决的是“并发操作时谁可以先访问数据”的问题。掌握这两个基础概念后,再学习 MySQL 的更新、删除、索引优化和性能调优会更容易建立完整认识。