一、事务概述

事务是一组数据库操作,要么全部成功,要么全部失败。

ACID特性

原子性(Atomicity):

  • 事务是不可分割的工作单位
  • 要么都做,要么都不做

一致性(Consistency):

  • 事务必须使数据库从一个一致性状态变换到另一个一致性状态

隔离性(Isolation):

  • 多个事务并发执行时,互不干扰

持久性(Durability):

  • 事务一旦提交,对数据库的改变是永久的

二、事务的使用

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 开启事务
START TRANSACTION;

BEGIN;

-- 提交事务
COMMIT;

-- 回滚事务
ROLLBACK;

-- 设置保存点
SAVEPOINT savepoint_name;
ROLLBACK TO savepoint_name;

示例

1
2
3
4
5
6
7
START TRANSACTION;

UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;

COMMIT;
-- 或 ROLLBACK;

三、事务隔离级别

并发问题

脏读:

  • 读到其他事务未提交的数据

不可重复读:

  • 同一事务中,两次读取同一数据结果不同
  • 重点是其他事务修改了数据

幻读:

  • 同一事务中,两次查询结果数量不同
  • 重点是其他事务新增或删除了数据

四种隔离级别

读未提交(READ UNCOMMITTED):

  • 允许读取未提交的数据
  • 可能脏读、不可重复读、幻读

读已提交(READ COMMITTED):

  • 只能读取已提交的数据
  • 可能不可重复读、幻读
  • Oracle默认级别

可重复读(REPEATABLE READ):

  • 同一事务中读取同一数据结果相同
  • 可能幻读
  • MySQL默认级别

串行化(SERIALIZABLE):

  • 最高隔离级别
  • 解决所有问题
  • 性能最低

查看和设置

1
2
3
4
5
-- 查看隔离级别
SELECT @@transaction_isolation;

-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

四、事务实现原理

原子性实现

使用undo log(回滚日志):

  • 记录数据修改前的值
  • 回滚时恢复数据

undo log类型:

  • insert undo log:插入操作,回滚直接删除
  • update undo log:更新操作,回滚恢复旧值

持久性实现

使用redo log(重做日志):

  • 记录数据修改后的值
  • 崩溃恢复时重放操作

WAL机制:

  • 先写日志,再写磁盘
  • 减少随机I/O

隔离性实现

锁机制:

  • 读写锁
  • 行锁、表锁
  • 共享锁、排他锁

MVCC(多版本并发控制):

  • 读操作不加锁
  • 写操作加锁
  • 通过版本号实现

一致性实现

  • 原子性保证操作完整
  • 隔离性保证并发正确
  • 持久性保证结果永久
  • 数据库约束保证数据正确

五、MVCC详解

版本链

每条记录包含隐藏列:

  • DB_TRX_ID:最后修改的事务ID
  • DB_ROLL_PTR:回滚指针,指向undo log

通过回滚指针形成版本链。

Read View

读操作时生成一致性视图:

  • m_ids:活跃事务ID列表
  • min_trx_id:最小活跃事务ID
  • max_trx_id:下一个将分配的事务ID
  • creator_trx_id:创建者事务ID

可见性判断

记录的DB_TRX_ID:

  • 小于min_trx_id:已提交,可见
  • 大于等于max_trx_id:未开始,不可见
  • 在m_ids中:未提交,不可见
  • 不在m_ids中:已提交,可见
  • 等于creator_trx_id:自己修改,可见

RC和RR区别

READ COMMITTED:

  • 每次SELECT生成新的Read View

REPEATABLE READ:

  • 第一次SELECT生成Read View,后续复用

六、事务最佳实践

事务控制

  • 事务尽量简短,减少锁持有时间
  • 避免在事务中进行耗时操作
  • 避免事务嵌套

隔离级别选择

  • 默认使用REPEATABLE READ
  • 需要实时数据用READ COMMITTED
  • 数据一致性要求极高用SERIALIZABLE

死锁避免

  • 按相同顺序访问资源
  • 避免长事务
  • 设置合理的锁等待超时
1
2
3
4
5
-- 查看锁等待超时时间
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';

-- 查看死锁检测
SHOW VARIABLES LIKE 'innodb_deadlock_detect';

七、总结

事务核心要点:

  • ACID特性保证数据完整性
  • 四种隔离级别解决并发问题
  • undo log保证原子性
  • redo log保证持久性
  • MVCC实现非阻塞读

理解事务机制是数据库开发的基础。