事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体一起向系统提交或者撤销操作请求,即这些操作要么同时成功,要么同时失败。
事务的原子性、一致性和持久性是由两类日志,redo log和undo log保证的;事务的隔离性则由锁和mvcc保证。
重做日志,记录的是事务提交时数据页的物理修改,用来实现事务的持久性。
该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log file),前者在内存中,后者在磁盘中。当事务提交之后会把所有修改信息保存到该日志文件中,用于在刷新脏页到磁盘发生错误时,进行数据恢复使用。
数据修改,事务提交后,缓冲区数据直接刷新到磁盘,已经通知客户端事务提交成功。但是刷新脏页数据出现错误,导致数据的不一致。
事务提交后,首先redo log buffer保存数据页的变化,更新到磁盘redo log 文件。然后缓冲区数据变更刷新到磁盘,如果执行正常,那么磁盘中redo log 文件就不需要关注;如果刷新脏页发生错误,通过redo log来恢复数据。这种机制称为WAL(write-ahead logging),优先记录日志。日志文件iblogfile0与iblogfile1循环使用,定期清理。
MySQL的redo log(重做日志)是用于保证事务的持久性的一种机制。它记录了所有对数据库所做的修改操作,以便在系统故障或重启后进行恢复。下面是MySQL redo log的原理:
重做日志的存在使得MySQL能够在系统故障或重启后保持事务的持久性。通过将事务的修改操作先记录到重做日志中,而不是立即写入磁盘,MySQL实现了较高的写入性能。同时,通过顺序写和写入磁盘前的分组操作,也提高了重做日志的写入效率。这种机制保证了MySQL的数据一致性和可靠性。
回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚和MVCC(多版本并发控制)。
undo log 和redo log记录物理日志不一样,它是逻辑日志。可以理解为当delete一条记录时,undo log中会记录一条对应的insert记录,反正易然。当执行一条update语句时,它会记录一条相反的update记录。当执行rollback时,就可以从undo log日志记录中读取相应的内容并回滚。
undo log 销毁:undo log在事务执行时产生,事务提交时,并不会立即删除undo log,因为这些日志还可能用于MVCC。
undo log存储:undo log 采用段的方式进行管理和记录,存放在rollback segment回滚段中,内部包含1024个undo log segment。
MySQL的undo log(撤销日志)是用于实现事务的回滚和MVCC(多版本并发控制)的一种机制。它记录了对数据库进行修改的旧值,以便在事务回滚或读取旧版本数据时使用。下面是MySQL undo log的原理:
撤销日志在MySQL中起到了重要的作用,它保证了事务的回滚能力和MVCC的支持。通过记录旧值和撤销操作,撤销日志使得MySQL能够回滚事务的修改,恢复到事务开始前的状态。同时,通过撤销日志的利用,MySQL实现了MVCC,提供了高并发的数据读取和隔离性。
当前读:读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录加锁。比如
select ... lock in share mode ,select ... for update ,update、insert、delete
都是当前读。
当前读(Current Read)是数据库事务隔离级别中的一种读取数据的方式。在当前读中,事务读取的是最新提交的数据版本,而不考虑正在执行的事务对数据所做的修改。
在当前读中,如果某个事务在读取某个数据行时,发现该数据行已经被其他事务修改但尚未提交,当前读会等待该事务完成并获取最新的数据版本后再读取。这种读取方式保证了事务读取到的数据是最新提交的数据。
当前读的特点如下:
当前读适用于一些对数据的实时性要求较高的场景,如查询实时统计数据、显示最新的库存信息等。但是需要注意,当前读可能会导致更多的阻塞等待,对并发性能产生影响。因此,在选择隔离级别和读取方式时,需要综合考虑应用的实际需求和并发性能的平衡。
快照读:简单的select(不加锁)就是快照读,读取的是数据记录的可见版本,有可能是历史版本,不加锁,是非阻塞读取。
快照读(Snapshot Read)是数据库事务隔离级别中的一种读取数据的方式。在快照读中,事务读取的是一个一致性的数据快照,即读取事务开始时数据库中的数据版本,并在整个事务过程中保持不变,不受其他事务的修改影响。
快照读的特点如下:
快照读适用于一些不要求读取最新数据的场景,如生成报表、数据分析等。由于快照读不会阻塞等待其他事务,因此可以提高并发性能。然而,需要注意的是,由于快照读读取的是一致性数据快照,可能会导致读取到已经过时的数据。因此,在选择隔离级别和读取方式时,需要根据应用的实际需求来确定是否使用快照读。
演示:
开启2个客户端,2个客户端都开启事务。客户端1简单读,客户端2修改一条记录,客户端再次读取数据。
不管客户端2事务修改记录未提交还是提交事务,客户端1都是快照读,读取的数据一致。如果客户端想要读取数据的最新记录,需要执行当前读。
MVCC(Multi-Version Concurrency Control)是一种用于实现并发控制的数据库技术。它在多用户并发访问数据库时,通过创建和管理多个数据版本,实现了高并发性和数据一致性的平衡。MVCC常用于支持事务隔离级别为"可重复读"的数据库系统,如MySQL的InnoDB存储引擎。
MVCC的具体实现,还需要依赖于数据库记录中的三个隐藏字段、undo log日志和readView。
如何查看表中隐藏字段呢?通过ibd2sdi
命令查看表空间文件.ibd,命令使用可以参考msyql官方文档<https://dev.mysql.com/doc/refman/8.0/en/ibd2sdi.html>或者参考下面链接5.
ibd2sdi stu.ibd
// 部分字段相关内容如下所示
{
"name": "DB_TRX_ID",
"type": 10,
"is_nullable": false,
"is_zerofill": false,
"is_unsigned": false,
"is_auto_increment": false,
"is_virtual": false,
"hidden": 2,
"ordinal_position": 4,
"char_length": 6,
"numeric_precision": 0,
"numeric_scale": 0,
"numeric_scale_null": true,
"datetime_precision": 0,
"datetime_precision_null": 1,
"has_no_default": false,
"default_value_null": true,
"srs_id_null": true,
"srs_id": 0,
"default_value": "",
"default_value_utf8_null": true,
"default_value_utf8": "",
"default_option": "",
"update_option": "",
"comment": "",
"generation_expression": "",
"generation_expression_utf8": "",
"options": "",
"se_private_data": "table_id=1169;",
"engine_attribute": "",
"secondary_engine_attribute": "",
"column_key": 1,
"column_type_utf8": "",
"elements": [],
"collation_id": 63,
"is_explicit_collation": false
},
{
"name": "DB_ROLL_PTR",
"type": 9,
...
}
db_trx_id
是 MySQL InnoDB 存储引擎中的一个系统变量,用于表示当前事务的唯一标识符(Transaction ID)。每个事务在 InnoDB 存储引擎中都会被分配一个唯一的 db_trx_id
值。
db_trx_id
的作用是用于事务管理和并发控制。它主要用于以下方面:
db_trx_id
作为事务的标识符,用于唯一标识每个正在执行的事务。通过 db_trx_id
,可以识别和区分不同的事务。db_trx_id
在并发控制机制中起着重要的作用。通过比较不同事务的 db_trx_id
值,可以确定事务的先后顺序,并根据事务的隔离级别进行相应的读取操作。db_trx_id
与 MVCC 搭配使用。每个数据行都会记录其被修改的事务ID范围(最小和最大的 db_trx_id
值)。通过比较事务的 db_trx_id
值和数据行的事务ID范围,可以确定事务是否可以读取该数据行,以实现事务的隔离性和一致性。总之,db_trx_id
是 InnoDB 存储引擎中用于标识事务和实现并发控制的重要变量。它在事务管理、并发控制和MVCC机制中起着关键的作用。
db_roll_ptr
是 MySQL InnoDB 存储引擎中的一个系统变量,表示回滚段指针(Rollback Segment Pointer)。它用于管理事务的回滚段。
回滚段是 InnoDB 存储引擎用于实现事务回滚操作的数据结构。当事务执行修改操作时,InnoDB会将旧数据的备份存储在回滚段中,以便在事务回滚时恢复数据。db_roll_ptr
存储了当前回滚段的指针位置,用于标识回滚段中的数据。
db_roll_ptr
的作用如下:
db_roll_ptr
用于管理回滚段的状态和位置。它指向当前正在使用的回滚段,以便在事务回滚时可以快速定位回滚段并进行数据恢复。db_roll_ptr
会指向回滚段中存储的旧数据,用于将数据恢复到事务开始之前的状态。db_roll_ptr
在并发控制中起着重要的作用。通过比较不同事务的 db_roll_ptr
值,可以确定事务的先后顺序,并根据事务的隔离级别进行相应的读取操作。总之,db_roll_ptr
是 InnoDB 存储引擎中用于管理回滚段和实现事务回滚的系统变量。它在事务回滚、并发控制和MVCC机制中扮演着重要的角色。
隐藏字段 | 描述 |
---|---|
DB_TRX_ID | 最近修改事务ID,记录插入这条记录或者最后一次修改该记录的事务ID |
DB_ROLL_PTR | 回滚指针,执行该记录的上一个版本,用于配合undo log |
DB_ROW_ID | 隐藏主键,当表没有指定主键的时候,系统会自动为每个表记录生成一个隐藏字段db_row_id,用来唯一标志一行 |
回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。
当insert的时候,产生的undo log只在回滚的时候需要,在事务提交后,可被立即删除;而update、delete的到时候,产生的undo log日志,不仅在回滚的时候需要,在快照读的时候也需要,不会立即删除。
不同事物或者相同事务对同一条记录进行修改,undo log会生成该记录的版本链表,链表表头是最新的旧记录,链表尾部是最早的旧记录。
Readview(读视图)是快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交)id。ReadView中包含4个核心字段:
字段 | 含义 |
---|---|
m_ids | 当前活跃事务ID集合 |
min_trx_id | 最小活跃事务id |
max_trx_id | 预分配事务ID,当前活跃事务最大id+1(事务ID自增) |
creator_trx_id | ReadView创建者事务ID |
不同的隔离级别,生成ReadView时机不同:
图片直接截图自视频,其中在事务5子在RC级别下第一次执行快照读的时候,左侧最新的记录即事务4修改那条记录应该还没有的。
在RR隔离级别下,第二次读取会复用第一次快照读生成的ReadView,即读取数据一致的。
如果小伙伴什么问题或者指教,欢迎交流。
❓QQ:806797785
参考链接:
[1]MySQL数据库视频[CP/OL].2020-04-16.p138-146.
[2]Mysql事务的实现原理之Redo Log的分析
[3]mysql 物理日志之redo log(重做日志)原理和介绍
[4]mysql事务实现的原理(redo log,undo log详解)
[5]ibd2sdi — InnoDB表空间SDI提取实用程序