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 的版本列表加锁,粒度小,避免全局锁,提升并发性能。
相关推荐
qq_356408662 小时前
es通过分片迁移迁移解决磁盘不均匀问题
java·数据库·elasticsearch
武子康3 小时前
Java-122 深入浅出 MySQL CAP理论详解与分布式事务实践:从2PC到3PC与XA模式
java·大数据·数据库·分布式·mysql·性能优化·系统架构
0wioiw03 小时前
Nodejs(④GraphQL)
后端·graphql
爱考证的小刘3 小时前
阿里云ACA认证[特殊字符]阿里云ACP认证
数据库·阿里云·云计算
Databend3 小时前
基于 Databend 的实时数据汇聚平台建设
数据库
王同学 学出来3 小时前
跟做springboot尚品甄选项目(二)
java·spring boot·后端
bobz9653 小时前
Calico 项目功能分析:聚焦转发面
后端
玄尺_0073 小时前
bug:uniCloud报Business Failed, 参数有误retry invoke error
数据库·bug
bobz9653 小时前
tcp 状态机
后端
@CLoudbays_Martin113 小时前
CDN是否能有效检测并且同时防御Ddos 和 CC 攻击?
java·服务器·网络·数据库·git·数据库开发·时序数据库