MVCC其实就是一个多版本并发控制,即多个不同版本的数据实现并发控制的技术,其基本思想是为每次事务生成一个新版本的数据,在读数据时选择不同版本的数据即可以实现对事务结果的完整性读取。
提高并发读写性能,操作时会生成事务 ID。
每条记录都会保存两个隐藏列:【DB_TRX_ID】(事务ID)和【DB_ROLL_PT】(回滚指针或删除版本号)
--假设系统的全局事务 ID 从 1 开始
start transaction;--拿到事务ID=1
insert into user (name,age) value ('张三',18);
insert into user (name,age) value ('李四',19);
commit;
id | name | age | db_trx_id | db_roll_pt |
---|---|---|---|---|
1 | 张三 | 18 | 1 | NULL |
2 | 李四 | 19 | 1 | NULL |
执行插入的时候,先拿到事务id,然后把记录写入到表中,并更新事务ID 为 1。
--假设系统的全局事务ID 号目前到了 22
start transaction;--拿到当前事务ID=22
delete user where id=2;
commit;
id | name | age | db_trx_id | db_roll_pt |
---|---|---|---|---|
1 | 张三 | 18 | 1 | NULL |
2 | 李四 | 19 | 1 | 22 |
直接更新 id=2
的删除版本号为 22。
--假设系统的全局事务ID 号目前到了 33
start transaction;--拿到当前事务ID=33
update user set age=19 where id=1;
commit;
id | name | age | db_trx_id | db_roll_pt |
---|---|---|---|---|
1 | 张三 | 18 | 1 | 33 |
2 | 李四 | 19 | 1 | 22 |
1 | 张三 | 19 | 33 | NULL |
修改操作时先将数据复制一行,然后将原来那一行的数据的删除版本号设置为 33。复制的这一行的数据 age 变为 19。并且事务版本号设置为 33。
--假设系统的全局事务ID 号目前到了 44
start transaction;--拿到当前事务ID=44
select * from user;
commit;
id | name | age | db_trx_id | db_roll_pt |
---|---|---|---|---|
1 | 张三 | 18 | 1 | 33 |
2 | 李四 | 19 | 1 | 22 |
1 | 张三 | 19 | 33 | NULL |
查询规则:
查询数据版本号小于等于当前事务ID的数据行。
这样确保事务读取的行,要么在事务开启之前已经存在的,要么是事务自身插入或者修改过的。
查询删除版本号为 null或 删除版本号大于当前事务 ID的数据。
确保取出来的行记录在事务开启之前没有被删除。
翻译成 sql 就是:
select * from user where db_trx_id<=44 and (db_roll_pt is null or db_roll_pt > 44);
undo log 是为了实现事务的原子性而出现的产物。
undo log 实现事务原子性:
事务处理过程中如果出现错误或者用户执行了回滚。mysql 可以利用 undo log 将数据恢复到事务之前的状态。
undo log 在 mysq innodb 存储引擎中用来实现多版本并发控制。
undo log 实现多版本并发控制:
事务未提交之前, undo 保存了未提交之前的版本数据。undo 中的数据可作为数据旧版本快照。提供给其他并发事务进行快照读。
快照读:
sql 读取的数据时快照版本,也就是历史版本,普通的 select 查询就是快照读。
innodb 快照读:数据的读取将由 cache(原本数据) +undo(事务修改前的数据) 两部分组成。
当前读
sql 读取的数据是最新版本。通过锁机制来保证读取的数据无法通过其他事务进行修改。
update、delete、insert、select… lock in share mode、select… for update
都是当前读。
redo log 的写入:
不是随着事务的提交才写入的,而是事务的执行过程中就开始写入redo log中。具体的落盘策略可以进行配置。
redo log 是为了实现事务的持久性而出现的产物
redo log 实现事务持久性:
为防止发生故障的时候还有脏页数据未写入磁盘。在重启 mysql 服务的时候根据 redo log 进行回放重做,从而达到事务的未写入磁盘数据进行持久化的特性。
redo log 配置路径:
innodb_log_group_home_dir
:指定 redo log 的目录。默认在 {datadir}/ib_log_file1&{datadir}/ib_log_file2
一旦事务提交成功并且数据持久化落盘之后,此时 redo log 中对应的事务记录就失去意义,所以 redo log 的写入时日志文件循环写入的:
innodb_log_files_in_group
:默认为 2,指定 redo log 日志文件组中的数量。innodb_log_file_size
:默认 48M,指定 redo log 文件的最大存储。innodb_log_buffer_size
:默认 16M,指定redo log 在 cache/buffer (操作系统缓存)中 缓冲池大小。redo log 的持久化策略:
0
:每秒提交一次 redo buffer --> redo log OS cache --> flush cache to disk,可能丢失一秒内的事务数据。
1
:默认值,每次事务提交执行 redo buffer --> redo log OS cache --> flush cache to disk,最安全,性能最差的方式。
2
:每次事务提交执行 redo buffer --> redo log OS cache, 再每秒执行 --> flush cache to disk 操作。断电会丢失 1 秒数据