当前位置: 首页 > article >正文

四种隔离级别是如何逐步加强事务隔离的,以及在底层如何使用锁机制和多版本控制(MVCC)来实现

隔离级别越高,事务之间的隔离性就越强,但并发性能通常会下降。为了更好地理解隔离级别的差异,下面通过一个简单的场景来说明四种隔离级别是如何逐步加强事务隔离的,以及在底层如何使用锁机制和多版本控制(MVCC)来实现。

场景描述

假设有一个银行系统,用户A和用户B都在账户表 accounts 中有一条记录,账户余额字段为 balance。用户A的账户当前余额为100元。

  • 事务1:用户A从账户中取出50元(UPDATE accounts SET balance = balance - 50 WHERE user_id = 1)。
  • 事务2:系统读取用户A的账户余额,进行财务报表统计(SELECT balance FROM accounts WHERE user_id = 1)。

1. 读未提交(Read Uncommitted)

  • 事务1开始:用户A取钱,账户余额变为50元,但事务1还未提交。
  • 事务2开始:系统读取用户A的余额,由于读未提交隔离级别允许读取未提交的数据,事务2读到的是事务1尚未提交的50元。
  • 问题:如果事务1最终回滚,用户A的余额应恢复为100元,但事务2已经在报表中记录了错误的余额(50元),这就是“脏读”。
底层实现:
  • 锁机制:基本不使用锁,因此读可以直接读取到其他事务未提交的写操作。
  • 代码实现:读取时不加锁,类似于下面的伪代码。
    -- 事务1:
    START TRANSACTION;
    UPDATE accounts SET balance = balance - 50 WHERE user_id = 1;
    
    -- 事务2:
    SELECT balance FROM accounts WHERE user_id = 1;  -- 读取到50元,虽然事务1未提交
    

2. 读已提交(Read Committed)

  • 事务1开始:用户A取钱,账户余额变为50元,但事务1还未提交。
  • 事务2开始:系统读取用户A的余额,读已提交隔离级别不允许读取未提交的数据,因此事务2读取到的是事务1之前的余额(100元)。
  • 事务1提交:用户A的余额最终更新为50元。
  • 问题:虽然避免了脏读,但如果事务2再次查询余额,可能会看到50元,这就是“不可重复读”,即同一事务内多次读取同一数据可能得到不同结果。
底层实现:
  • 锁机制:读操作会读取已经提交的数据,写操作会加排他锁(X锁),避免其他事务读取未提交的数据。
  • 代码实现:通过锁定写操作确保读操作只能看到已提交的数据。
    -- 事务1:
    START TRANSACTION;
    UPDATE accounts SET balance = balance - 50 WHERE user_id = 1;  -- 加排他锁
    
    -- 事务2:
    SELECT balance FROM accounts WHERE user_id = 1;  -- 读取到100元,等待事务1提交
    
    COMMIT;  -- 事务1提交,之后查询才能看到50元
    

3. 可重复读(Repeatable Read)

  • 事务1开始:用户A取钱,账户余额变为50元,但事务1还未提交。
  • 事务2开始:系统读取用户A的余额,可重复读确保事务2在整个事务期间看到的数据是一致的,事务2读取到100元。
  • 事务1提交:用户A的余额更新为50元,但事务2再次读取时依然看到100元,保持一致性。
  • 问题:虽然解决了不可重复读,但可能出现“幻读”,即如果事务1在插入新数据时,事务2可能看到不同的数据集。
底层实现:
  • MVCC:通过多版本控制,每次事务开始时创建一致性视图,保证读操作始终看到事务开始时的数据版本。

  • Next-Key Lock:为了防止幻读,MySQL会对读取的记录和记录间的间隙加锁,阻止其他事务插入新记录。

  • 代码实现:通过一致性视图保证同一事务内多次读取数据结果相同。

    -- 事务1:
    START TRANSACTION;
    UPDATE accounts SET balance = balance - 50 WHERE user_id = 1;
    
    -- 事务2:
    SELECT balance FROM accounts WHERE user_id = 1;  -- 读取到100元
    SELECT balance FROM accounts WHERE user_id = 1;  -- 读取到100元,即使事务1提交
    
    COMMIT;  -- 事务1提交后,事务2仍然看到的是事务开始时的数据
    

4. 串行化(Serializable)

  • 事务1开始:用户A取钱,账户余额变为50元。
  • 事务2开始:系统想要读取用户A的余额,但串行化隔离级别要求所有事务串行执行,因此事务2必须等待事务1提交后才能读取数据。
  • 事务1提交:事务2读取到最新的余额(50元)。
  • 问题:串行化的性能非常低,因为所有并发事务都必须一个接一个地执行,虽然解决了所有并发问题,但严重影响系统性能。
底层实现:
  • 锁机制:事务之间完全隔离,通过锁定整个数据表或行,确保事务顺序执行。
  • 代码实现:所有事务串行执行,避免任何并发问题。
    -- 事务1:
    START TRANSACTION;
    UPDATE accounts SET balance = balance - 50 WHERE user_id = 1;  -- 锁定数据
    
    -- 事务2:
    SELECT balance FROM accounts WHERE user_id = 1;  -- 必须等待事务1提交后才能执行
    
    COMMIT;  -- 事务1提交后,事务2才开始执行
    

锁机制和MVCC的底层实现

1. 锁机制实现(伪代码)

在数据库引擎(如InnoDB)中,锁机制是通过管理锁表或内存结构来实现的。当事务试图对某个数据进行操作时,数据库会根据隔离级别判断是加共享锁还是排他锁。

// 事务1请求修改某行数据
if (requesting WRITE lock) {
    if (no other transactions holding lock) {
        // 加排他锁
        grant EXCLUSIVE lock;
    } else {
        // 等待其他事务释放锁
        wait for lock release;
    }
}

// 事务2请求读取同一行数据
if (requesting READ lock) {
    if (no exclusive lock) {
        // 加共享锁
        grant SHARED lock;
    } else {
        // 等待排他锁释放
        wait for lock release;
    }
}
2. MVCC的实现(伪代码)

MVCC 通过为每个事务创建数据的多个版本来实现。在InnoDB中,每行数据都有两个隐含的列:创建版本号删除版本号,它们标识了事务对数据的修改时间。

-- 在读操作时,判断版本号
SELECT * FROM accounts
WHERE user_id = 1
  AND create_version <= current_transaction_version
  AND (delete_version IS NULL OR delete_version > current_transaction_version);

每次事务读写时,都会根据事务的版本号判断能看到哪些数据版本。


总结

  • 隔离级别越高,事务之间的相互隔离就越彻底,数据的一致性越强,但并发性能也会下降。
  • 锁机制通过加锁和排它控制,确保事务之间的隔离性;MVCC通过多版本控制,使得读取和写入操作可以同时进行,提高并发性能。
  • 隔离级别的选择取决于业务场景对性能和一致性的需求。在高并发系统中,通常选择较低的隔离级别以提高性能,而在数据一致性要求较高的场景,则可能选择更高的隔离级别。

http://www.kler.cn/a/354191.html

相关文章:

  • 由于这些关键原因,我总是手边有一台虚拟机
  • R语言数据分析案例46-不同区域教育情况回归分析和探索
  • 【087】基于51单片机智能宠物喂食器【Proteus仿真+Keil程序+报告+原理图】
  • Java复习|图形用户界面AWT、Swing----银行客户管理系统【校课版】【1】
  • 算法day_3数组中的单一元素和二进制位颠倒
  • sqlite3,一个轻量级的 C++ 数据库库!
  • HCIP--1
  • 新媒体优势
  • Spring Boot驱动的在线考试系统:JavaWeb技术实战
  • Scala入门基础(12)抽象类
  • 梯度下降算法优化—随机梯度下降、小批次、动量、Adagrad等方法pytorch实现
  • pico+Unity交互开发教程——手指触控交互(Poke Interaction)
  • 如何利用OpenCV和yolo实现人脸检测
  • 如何利用边缘计算网关进行工厂设备数据采集?天拓四方
  • Linux创建sh脚本,实现全局调用
  • 可编辑73页PPT | 企业智慧能源管控平台建设方案
  • 机器学习【教育系统改善及其应用】
  • 线性代数基本知识
  • Web 搜索引擎优化
  • k8s部署Kafka集群超详细讲解
  • C#高级编程核心知识点
  • 智慧供排水管网在线监测为城市安全保驾护航
  • Mysql(4)—数据库索引
  • 数据结构实验十二 图的遍历及应用
  • 在FastAPI网站学python:虚拟环境创建和使用
  • 特斯拉智驾路线影响国内OEM组织架构变革,Robotaxi重塑汽车定位搅动风云