• 简单聊聊copy on write(写时复制)技术


    目录

    基本介绍

    概述 

    关键点 

    应用实现

    数据库中的MVCC

     Java中的CopyOnWriteArrayList

    Nacos避免并发读写冲突问题


    基本介绍

    概述 

    写时复制(英语:Copy-on-write,简称COW)是一种计算机领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变

    这过程对其他的调用者都是 [透明]的。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy) 被创建,因此多个调用者只是读取操作时可以共享同一份资源。

    当需要修改某个共享数据时,先将原始数据复制一份,并在副本上进行修改,修改完成后再将副本的引用赋值给原始数据的引用 ,读写分离,空间换时间,避免为保证并发安全导致的激烈的锁竞争。

    关键点 

    • Copy-on-write适用于读多写少的情况,最大程度的提高读的效率;
    • Copy-on-write是最终一致性,在写的过程中,原有的读的数据是不会发生更新的,只有新的读才能读到最新数据;
    • 在java中,为了能使其他线程能够及时读到新的数据,需要使用volatile变量;
    • 写的时候不能并发写,需要对写操作进行加锁;

    应用实现

    数据库中的MVCC

    多版本并发控制(MVCC) 在一定程度上实现了读写并发,它只在 可重复读(REPEATABLE READ) 和 提交读(READ COMMITTED) 两个隔离级别下工作。其他两个隔离级别都和 MVCC 不兼容,因为 未提交读(READ UNCOMMITTED),总是读取最新的数据行,而不是符合当前事务版本的数据行。而 可串行化(SERIALIZABLE) 则会对所有读取的行都加锁。

    MVCC除了支持读和读并行,还支持读和写并行、写和读并行,但为了保持数据一致性,写和写是无法并行的。

    行锁,并发,事务回滚等多种特性都和 MVCC 相关。MVCC 实现的核心思路就是 Copy On Write

    在一个事务写的时候会copy一个记录的副本,其他事务的读操作会读取这个记录的副本,因此不影响其他事务对此记录的写入,实现写和读并行。 

     Java中的CopyOnWriteArrayList

     CopyOnWriteArrayList 是jdk1.5以后并发包中提供的一种并发容器,写操作通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也成为“写时复制容器”,类似的容器还有 CopyOnWriteArraySet。

    1. public boolean add(E e) {
    2. //加锁,对写操作保证线程安全
    3. final ReentrantLock lock = this.lock;
    4. lock.lock();
    5. try {
    6. Object[] elements = getArray();
    7. int len = elements.length;
    8. //拷贝原容器,长度为原容器+1
    9. Object[] newElements = Arrays.copyOf(elements, len + 1);
    10. //在新副本执行添加操作
    11. newElements[len] = e;
    12. //底层数组指向新的数组
    13. setArray(newElements);
    14. return true;
    15. } finally {
    16. lock.unlock();
    17. }
    18. }

    CopyOnWriteArrayList底层实现添加的原理是先copy出一个容器(可以简称副本),再往新的容器里添加这个新的数据,最后把新的容器的引用地址赋值给了之前那个旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。 

     读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。Java 的 list 在遍历时,若中途有其他线程对容器进行修改,则会抛出ConcurrentModificationException 异常。而CopyOnWriteArrayList由于其“读写分离”的思想,遍历和修改操作分别作用在不同的 list容器,所以迭代的时候不会抛出 ConcurrentModificationExecption 异常了。

     其存在数据一致性问题:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。如果希望写入的的数据,马上能读到,不要使用CopyOnWrite容器

    Nacos避免并发读写冲突问题

    Nacos在更新实例列表时,会采用CopyOnWrite技术,首先将旧的实例列表拷贝一份,然后更新拷贝的实例列表,再用更新后的实例列表来覆盖旧的实例列表。

    这样在更新的过程中,就不会对读实例列表的请求产生影响,也不会出现脏读问题了。

  • 相关阅读:
    程序环境和预处理
    Java语言框架
    深聊MySQL,从入门到入坟之:定位慢SQL的方法还可以这样,小菜鸡都能看懂。
    c++模板
    使用 PHP 和 MySQL 的安全登录系统
    【LeetCode周赛】LeetCode第362场周赛
    985大学新增专业,考数据结构+自然语言处理!中央民族大学新增语言信息安全...
    Ab3d.DXEngine 6.0 Crack 2023
    动手实践丨使用华为云IoT边缘体验“边云协同”
    C练习题_15
  • 原文地址:https://blog.csdn.net/m0_62436868/article/details/134367575