0%

Copy On Write

Copy On Write

“在写的时候复制”

概念

“写入时复制(英语:Copy-on-write,简称COW)是一种计算机 [程序设计]领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是 [透明]的。此做法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy) 被创建,因此多个调用者只是读取操作时可以共享同一份资源。”

当多个调用者需要使用同一份资源时,最简单的方法是为新来的调用者创建一份资源副本,但是创建副本会带来额外开销。当调用者过多时,创建副本的开销也会变大,为了减少这部分系统开销,对调用者做了标记,判断调用者究竟是要读还是要写,如果是读就和之前的调用者共享资源,如果是写才创建资源副本。COW翻译过来可以是“在写的时候复制”。

下面是整理的几种COW的应用场景

Fork系统调用

fork方法最初是Unix系统中创建子进程的系统调用方法,在创建子进程时会使用Fork方法。假如进程只有读的事件,那么父子进程使用相同的代码段数据段,只有发生写事件时,系统才会拷贝给子进程一份资源。

写时复制的主要优势在于它可以显著减少内存使用,特别是在拷贝大规模数据时。它避免了不必要的数据复制,从而提高了系统性能和效率。此外,写时复制还提供了更好的并发性,因为多个进程或线程可以同时读取相同的数据副本,而无需担心冲突。

关于fork系统调用可以阅读我的另一篇笔记

Redis持久化

Redis的RDB持久化:bgsave命令执行后,会在服务端目录下生成一个dump.rdb文件,而这个文件中就保存了内存中存放的数据,当服务器重启后,会自动加载里面的内容到对应数据库中。

Redis是如何在持久化期间还能保证正常使用呢?它就使用了fork函数创建了子进程,在子进程中备份数据库

Untitled

当 fork 操作执行后,父进程中的内存页都会被设置为只读,然后子进程的地址空间指向父进程。

当父子进程都只读内存时,共享资源,但是其中某个进程写内存时,CPU 硬件检测到内存页是只读的,于是触发页异常中断(page-fault)。中断例程中,系统就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。这确保了子进程中的数据是隔离的,不受父进程中数据更改的影响。

MySQL多版本并发控制(MVCC)

在MySQL的InnoDB引擎中多版本控制MVCC(Multiversion Concurrency Control)就用了CopyOnWrite原理。

COW策略在MVCC中的应用主要体现在版本链的维护中。当事务对某个数据项进行修改时,会创建该数据的新版本,并在新版本上进行修改操作。这样,旧版本的数据仍然可用于其他事务的读取,从而实现了多版本的并发控制。

Java - COWArrayList

支持写时复制的集合:CopyOnWriteArrayListCopyOnWriteArraySetCOW 容器固然安全了很多,但是由于每次写都要复制整个数组,时间和空间的开销都更高,因此只适合读多写少的情景。在写入时,为了保证效率,也应尽量做批量插入或删除,而不是单条操作。并且它的正本和副本有可能不同步,因此无法保证读取的是最新数据,只能保证最终一致性。

有关 COW (CopyOnWrite) 的一切-阿里云开发者社区 (aliyun.com)

如果觉得我的文章对您有用,赏我一包辣条吧!您的支持将鼓励我继续创作!也可以加我微信一起交流学习,折腾点有意思事情。