MVCC初学demo(一

先谈谈 MVCC

两阶段锁(2PL)作为一种经典的并发控制机制,确实有效地解决了数据库中的读写冲突问题,保障了事务的隔离性。然而,锁机制本身在高并发场景下会带来一定的性能开销,例如阻塞、死锁以及上下文切换等问题。这类似于 Java 并发编程的发展历程:从早期的 synchronized 关键字,逐步演进到更灵活的 Lock 接口和读写锁(ReentrantReadWriteLock)等机制。数据库领域也面临着类似的挑战与演进需求,于是便催生了另一种解决并发问题的思路------多版本并发控制(MVCC, Multi-Version Concurrency Control)。MVCC 通过维护数据的多个版本来实现非阻塞的读操作,在提升并发性能的同时,有效减少了锁的使用,成为现代数据库系统中实现高并发访问的重要技术之一。

几个日志的作用

  • binlog:记录"做了什么",用于复制和恢复。
  • redo log:记录"怎么改的页",用于崩溃后重做,保证持久性。
  • undo log:记录"改之前什么样",用于回滚和 MVCC,保证原子性与隔离性。

MVCC 模拟实现代码总结

该代码实现了一个简化的 多版本并发控制(MVCC) 存储系统,用于演示数据库中如何通过版本管理实现高并发下的读写隔离,避免锁竞争,提升系统吞吐。

1. 核心设计思想

MVCC 机制:通过为每个数据项维护多个历史版本,使得读操作无需加锁,可以读取事务开始时的"快照",而写操作则创建新版本,从而实现读写不阻塞。 版本控制:使用全局递增的 globalVersion 来标识每个写操作的时序,事务以开始时的版本号作为其"视图",读取可见的最新版本。

2. 关键组件说明

arduino 复制代码
class VersionedValue<T> {
    private final T value;
    private final long version;
}

// 每个 key 对应多个版本的值
public final Map<String, List<VersionedValue<Integer>>> store = new ConcurrentHashMap<>();
//全局版本号
private final AtomicLong globalVersion = new AtomicLong(1);

 public long beginTransaction() {
        return globalVersion.get(); // 事务开始时的全局版本号
    }
 
   public VersionedValue<Integer> read(String key, long transactionVersion) {
        List<VersionedValue<Integer>> versions = store.get(key);
        if (versions == null) return new VersionedValue<>(0, 0);

        // 选择小于等于事务版本的最新版本
        for (int i = versions.size() - 1; i >= 0; i--) {
            VersionedValue<Integer> v = versions.get(i);
            if (v.getVersion() <= transactionVersion) {
                return v;
            }
        }
        return new VersionedValue<>(0, 0);
    }

    // 冲突检测写入
// 推荐的修改:使用 synchronized 块
    public boolean writeWithConflictDetection(String key, int value, long txVersion) {
        List<VersionedValue<Integer>> versions = store.computeIfAbsent(key, k -> new ArrayList<>());
        // 对特定key的版本列表进行同步
        synchronized (versions) {
            if (!versions.isEmpty()) {
                VersionedValue<Integer> latestVersion = versions.get(versions.size() - 1);
                if (latestVersion.getVersion() > txVersion) {
                    return false; // 冲突
                }
            }
            // 没有冲突,执行写入(注意:write方法也需要被同步,或者将其逻辑合并到这里)
            // 为了安全,将 write 方法的逻辑也放入同步块
            // 假设 globalVersion 已改为 AtomicLong
            VersionedValue<Integer> newVersion = new VersionedValue<>(value, globalVersion.incrementAndGet());
            versions.add(newVersion);
            return true;
        }
    }

3. 核心操作逻辑

  • beginTransaction(): 返回当前全局版本号作为事务的"快照版本",后续读操作基于此版本可见性规则进行。
  • read(String key, long transactionVersion): 从指定 key 的版本列表中,查找 版本号 ≤ 事务版本 的最新版本。 若无数据,返回默认值 (0, 0),模拟"读取未定义值"。
  • writeWithConflictDetection(String key, int value, long txVersion): 写前检查(Write-Time Validation):在写入前检查该 key 的最新版本是否已超过当前事务的快照版本。 若存在更新的版本(latestVersion > txVersion),说明有并发写冲突,写入失败。 否则,生成新版本并追加到列表。 使用 synchronized(versions) 对单个 key 的版本列表加锁,粒度小,避免全局锁,提升并发性能。
相关推荐
csdn_aspnet2 小时前
在 MacOS 中安装 MySQL 8
mysql·macos
清风6666663 小时前
基于单片机的水塔液位检测与智能调节报警系统设计
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
gplitems1233 小时前
Technox – IT Solutions & Services WordPress Theme: A Practical
linux·服务器·数据库
thinktik3 小时前
AWS EKS安装S3 CSI插件[AWS 海外区]
后端·kubernetes·aws
不剪发的Tony老师4 小时前
MySQL 9.5创新版发布,有哪些新功能?
数据库·mysql
布朗克1684 小时前
MySQL 及 SQL 注入详细说明
数据库·sql·mysql·1024程序员节
武子康5 小时前
Java-154 深入浅出 MongoDB 用Java访问 MongoDB 数据库 从环境搭建到CRUD完整示例
java·数据库·分布式·sql·mongodb·性能优化·nosql
我科绝伦(Huanhuan Zhou)5 小时前
MySQL一键升级脚本(5.7-8.0)
android·mysql·adb
Austindatabases5 小时前
DBA 从“修电脑的” 到 上演一套 “数据治理” 大戏 --- 维护DBA生存空间,体现个体价值
数据库·dba
LB21125 小时前
Redis黑马点评 day01
数据库·redis·缓存