侧边栏壁纸
博主头像
BinArTale's Blog 博主等级

行动起来,活在当下

  • 累计撰写 6 篇文章
  • 累计创建 21 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

MySQL中的MVCC技术

binbin
2025-03-21 / 0 评论 / 0 点赞 / 15 阅读 / 0 字

1. 事务的隔离级别:

事务的隔离级别,总共有四种不同的隔离级别,每种解决不同的并发问题:

  1. READ-UNCOMMITTED(读未提交)

  • 事务可以读取其他事务未提交的数据(脏读),会有脏读、不可重复读和幻读的问题

  • 示例:

-- 事务 A
BEGIN;
UPDATE user set age = age -1 where id = 1; -- 未提交

-- 事务B 
BEGIN;
SELECT age from user where id = 1 ; -- 读到事务A未提交的age的数据(原始是18,现在读取到17)
COMMIT;

-- 事务A回滚
ROLLBACK;

结果就是事务B读取到了事务A未提交的脏数据,如果事务A回滚,那么事务B读取到的数据就是错的。

  1. READ-COMMITTED(读已提交)

  • 事务只能读取到其他事务已经提交的数据,这解决了脏读,但是还是会不可重复读和幻读

  • 示例:

-- 事务 A
BEGIN;
UPDATE user set age = age -1 where id = 1; 
COMMIT; -- 事务已提交,age 值变为 17

-- 事务B 
BEGIN;
SELECT age from user where id = 1 ; -- 第一次查询可能读取到18,如果A没提交,也就是不会读取到未提交的数据

-- 事务A提交后读取
SELECT age from user where id = 1 ; -- 第二次查询可能读取到17,A已提交
COMMIT

这就会导致两次读取到的数据不一致,也就是不可重复读, 但是不会读取到未提交的数据了,避免了脏读。

  1. REPEATABLE-READ(可重复读)

  • 事务执行期间多次读取同一数据,结果保持一致,这解决了脏读和可重复读,但是会有幻读问题

  • 示例

-- 事务A
BEGIN;
SELECT *  FROM user WHERE age = 18; -- 假设返回2条记录

-- 事务B 插入数据并提交
BEGIN;
INSERT INTO user(age) VALUES (18)
COMMIT;

-- 事务A 再次查询
BEGIN;
SELECT *  FROM user WHERE age = 18; -- 返回3条记录

事务A在第一次查询的时候有2条记录,等事务B提交以后事务A再查询有3条记录,也就是发生了幻读。

解决幻读的方法有多种

  1. 将事务隔离级别调整为SERIALIZABLE

  2. 在可重复读的事务级别下,给事务操作的这张表加锁。

  3. 在可重复读的事务级别下,给我事务操作的这张表加 Next-key LockRecord Lock行锁 + Gap Lock间隙锁),也就是不只是锁住每条记录,也锁住每条记录的间隙,让记录之间无法新增记录。

‼️: next-key lock的确是解决了幻读问题,但是next-key lock在并发情况下也经常会造成死锁。死锁检测和处理也会花费时间,一定程度上影响到并发量。

  1. SERIALIZABLE(可串行化):最高的隔离级别,所有事务依次逐个执行,这样事务之间就完全不会产生干扰。

2. 多版本并发控制(Multi-Version Concurrency Control)

MVCC 是一种用于在多个并发事务同时读写数据库时,保持数据一致性和隔离性的技术。他是通过在每一行上维护多个版本的数据来实现的,当一个事务要对数据进行修改时,MVCC 会为改事务创建一个快照,而不是直接修改实际的数据行。

  1. 读操作(SELECT)

当一个事务进行读操作的时候,他会使用快照读取,他会选择一个不晚于事务创建时间的最新版本的数据作为快照数据,后续读取的都是这个快照数据,其他事务对数据行的修改不会影响当前事务的读操作。

  1. 写操作(UPDATE,DELETE,INSERT)

对于写操作,事务会为要修改的数据创建一个新版本,将修改后的数据写入新版本,新版本数据会携带当前事务的版本号,以便其他事务能够正确读取相应版本的数据。原始版本的数据仍然存在。

  1. 事务的提交

  • 当一个事务提交时,他所作的修改就会成为数据库最新版本的数据,并且其他事务可见

  • 当一个事务回滚时,所做的修改将会被撤销,其他事务不可见。

  1. 版本回收

为了防止数据库中的版本无限增长,MVCC会定期进行版本回收。回收机制会删除已经不再需要的旧版本数据,从而释放空间。

一致性非锁定读和锁定读

  1. 非锁定读:添加一个版本号或者时间戳,在更新数据的时候同时版本号+1或者更新时间戳。查询的时候,将当前可见的版本号和对应记录的版本号进行比较,如果记录的版本号小,表示该记录可见InnoDB 存储引擎中MVCC就是对一致性非锁定读的实现,读取一个正在执行更新操作的行时,不会等待行上的锁释放,而是去读取行的一个快照数据。

  2. 锁定读:执行 select ... lock in share mode select ... for updateinsert updatedelete操作时就是锁定读。在锁定读下,读取的数据是最新版本,所以也被称为当前读(current read)。锁定读会对读取到的记录加锁, select ... lock in share mode 的话加S锁,其他事务也可以加 Sselect ... for updateinsert updatedelete 是对记录加 X 锁,且其他事务不能加任何锁。InnoDB 在实现 Repeatable Read 时,如果执行的是当前读,则会对读取的记录使用 Next-key Lock ,来防止其它事务在间隙间插入数据。

0

评论区