事务与锁机制是数据库系统中非常重要的概念,尤其在 MySQL 这样的关系型数据库中,它们决定了数据的 一致性、完整性 和 并发控制。下面我将详细讲解事务和锁机制,分步骤深入分析。
一、事务(Transaction)
1. 什么是事务?
事务是一组操作的集合,这些操作要么全部执行成功,要么全部失败回滚,保证数据库的 一致性 和 完整性。
2. 事务的四大特性(ACID)
-
A - 原子性(Atomicity):
事务中的所有操作是一个不可分割的整体,要么全部执行,要么全部不执行。例子:银行转账,A账户转100元给B账户,要么两步都完成(A扣款,B加款),要么都不完成(回滚)。
-
C - 一致性(Consistency):
事务开始前和结束后,数据库保持一致的状态。例子:无论事务成功与否,两个账户的总金额保持不变。
-
I - 隔离性(Isolation):
并发执行的事务互不影响,每个事务的操作在完成前对其他事务是不可见的。例子:当两个事务同时更新同一条数据时,事务 A 的操作不会影响事务 B,反之亦然。
-
D - 持久性(Durability):
一旦事务提交,数据会永久保存,即使系统崩溃也不会丢失。例子:事务提交后,数据写入磁盘,保证数据不丢失。
3. MySQL 事务的实现
MySQL 支持事务的存储引擎是 InnoDB。其他存储引擎(如 MyISAM)不支持事务。
事务的基本语法
sql
-- 开始事务 START TRANSACTION; -- SQL 操作 UPDATE account SET balance = balance - 100 WHERE name = 'A'; UPDATE account SET balance = balance + 100 WHERE name = 'B'; -- 提交事务 COMMIT; -- 回滚事务 ROLLBACK;
事务的两种结束方式
- 提交(COMMIT):事务中的所有操作成功完成,并永久保存。
- 回滚(ROLLBACK):撤销事务中的所有操作,数据库回到事务开始时的状态。
自动提交模式
- 在 MySQL 中,默认开启 自动提交模式。每执行一条 SQL 语句,MySQL 会自动提交。
- 关闭自动提交:
sql
复制代码
SET autocommit = 0;
二、事务的隔离级别
1. 为什么需要隔离级别?
在并发环境下,多个事务同时操作数据库,会引发 并发问题,如脏读、不可重复读和幻读。
2. 并发问题
- 脏读(Dirty Read):
一个事务读到了另一个未提交事务修改的数据。 - 不可重复读(Non-Repeatable Read):
在同一事务中,前后两次查询的数据不一致,因为其他事务修改了数据。 - 幻读(Phantom Read):
在同一事务中,前后两次查询结果的行数不一致,因为其他事务插入/删除了数据。
3. MySQL 的四种隔离级别
MySQL 支持 四种事务隔离级别,从低到高依次是:
隔离级别 | 脏读 | 不可重复读 | 幻读 | 特点 |
---|---|---|---|---|
Read Uncommitted | 允许 | 允许 | 允许 | 最低级别,读到未提交数据 |
Read Committed | 禁止 | 允许 | 允许 | 只读取已提交数据(Oracle默认) |
Repeatable Read | 禁止 | 禁止 | 允许 | 可重复读(MySQL 默认级别) |
Serializable | 禁止 | 禁止 | 禁止 | 最高级别,完全串行化操作 |
设置事务隔离级别
sql
复制代码
-- 查看当前隔离级别 SELECT @@TRANSACTION_ISOLATION; -- 设置隔离级别为 Repeatable Read SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
三、锁机制
1. 为什么需要锁?
在并发操作中,多个事务可能对同一资源进行读写操作,为了防止数据不一致或损坏,MySQL 采用锁机制来控制并发访问。
2. MySQL 锁的分类
MySQL 的锁主要分为以下几类:
1)按照锁的粒度分类
-
表锁(Table Lock):
- 锁定整个数据表。
- 粒度大,开销小,适用于读多写少的场景。
- MyISAM 存储引擎使用表锁。
-
行锁(Row Lock):
- 锁定某一行记录。
- 粒度小,开销大,适用于高并发场景。
- InnoDB 支持行锁。
2)按照锁的类型分类
-
共享锁(S锁):
允许多个事务同时读数据,互不影响。sql
复制代码
SELECT * FROM table_name LOCK IN SHARE MODE;
-
排他锁(X锁):
排他性地锁定数据,其他事务无法读写被锁定的数据。sql
复制代码
SELECT * FROM table_name FOR UPDATE;
3)间隙锁(Gap Lock)
- 间隙锁 用于防止幻读,锁定一个范围内的数据,甚至包括不存在的数据。
- 发生在 Repeatable Read 隔离级别 下。
3. 锁的实现方式
在 InnoDB 中,锁是通过 索引 实现的。如果查询未使用索引,InnoDB 会退化为表锁。
行锁与索引
sql
复制代码
-- 通过主键索引加行锁 SELECT * FROM table_name WHERE id = 1 FOR UPDATE; -- 未使用索引,退化为表锁 SELECT * FROM table_name WHERE name = '西瓜' FOR UPDATE;
四、事务与锁的结合使用
在实际开发中,事务和锁通常结合使用。例如:
sql
复制代码
START TRANSACTION; -- 通过行锁确保事务的隔离性 SELECT * FROM account WHERE name = 'A' FOR UPDATE; UPDATE account SET balance = balance - 100 WHERE name = 'A'; SELECT * FROM account WHERE name = 'B' FOR UPDATE; UPDATE account SET balance = balance + 100 WHERE name = 'B'; COMMIT;
- 使用
FOR UPDATE
加排他锁,确保数据安全。 - 在高并发下避免 死锁 和 锁等待。
总结
- 事务 通过 ACID 保证数据一致性。
- 隔离级别 决定事务之间的并发控制程度。
- 锁机制 控制并发访问,InnoDB 支持行锁、表锁和间隙锁。
- 结合事务和锁,可以实现高并发下的数据安全操作。