目录
什么是事务?
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要不同时失败。
用一个形象的比喻:事务就像银行转账操作,从A账户扣款和向B账户加款这两个操作必须作为一个整体执行——要么都成功完成,要么都不执行。如果只执行了其中一个操作,就会导致数据不一致。
事务的四大特性
- 原子性:事务是不可分割的最小操作单元,要么同时成功,要么同时失败。
- 一致性:事务完成时,必须使所有的数据保持一致状态。
- 隔离性:数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
- 持久性:事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。
事务的隔离级别
读未提交(Read Uncommitted):事务可以读取其他未提交事务的修改。会导致脏读(Dirty Read)、不可重复读和幻读。
读已提交(Read Committed):事务只能读取其他已提交事务的修改。解决了脏读,但仍可能存在不可重复读(Non-repeatable Read) 和幻读。这是Oracle等数据库的默认级别。
可重复读(Repeatable Read):确保在同一事务中多次读取同一数据的结果是一致的。解决了脏读和不可重复读,但仍可能存在幻读(Phantom Read)。这是MySQL InnoDB的默认级别。
串行化(Serializable):最高隔离级别,强制事务串行执行,完全避免脏读、不可重复读和幻读,但性能开销最大。
事务的原理
redo log
重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者是在内存中,后者在磁盘中,当事务提交之后会把所有的修改信息都存到该日志文件中,用于在刷新脏页到磁盘,发生错误时,进行数据恢复使用。
当对缓冲区的数据进行增删改之后,首先会把增删改的数据记录在redo log buffer中,redo log buffer就会去记录数据页的物理变化。当事务提交的时候就会把redo log buffer中数据页变化刷新到磁盘中。当进行脏页刷新时出错了,就能通过redo log进行恢复。
undo log
回滚日志,用于记录数据被修改前的信息,作用包含:提供回滚、MVCC(多版本并发控制)。
redo log是记录物理日志,而undo log是逻辑日志。当执行delete或update时,它都会记录一条相反的记录。当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容
Undo log销毁:undo log在事务执行时产生,事务提交时,并不会立刻删除undo log,这些日志还可能用于MVCC。
Undo log存储:undo log采用段的方式进行管理和记录,存放在rollback segment回滚段中。
MVCC实现原理
概念
- 当前读:读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
- 快照读:简单的select(不加锁)就是快照读,读取的是可见版本,有可能是历史数据,不加锁,是非阻塞读。RC:每次select都会生成一个快照读;RR:开启事务后的第一个select语句就是快照读的地方;S:快照读变成当前读
- MVCC:Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读的功能。具体实现还依赖数据库中的三个隐藏字段、undo log、readView。
隐藏字段
- DB_TRX_ID:最近修改事务id,记录插入这条记录或最后一次修改该记录的事务id。
- DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。
- DB_ROW_ID:隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段
undo log版本链
用 Undo Log 将一条记录的多个历史版本串联起来,形成一个基于
ROLL_PTR
(回滚指针)的链表结构。这个链表使得数据库能够“回到过去”,找到记录在某个特定时间点的状态。
readview
Readview是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃(未提交)事务的id。包含四个核心字段:
- m_ids:当前活跃事务的id集合
- min_trx_id:最小活跃事务id
- max_trx_id:预分配事务id,当前最大事务id+1
- creator_trx_id:Readview创建者的事务id