You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.
写入时复制( Copy-on-write, 简称COW) 是一种计算机程序设计领域的优化策略。其核心思想是, 如果有多个调用者( callers) 同时要求相同资源( 如内存或磁盘上的数据存储) , 他们会共同获取相同的指针指向相同的资源, 直到某个调用者试图修改资源的内容时, 系统才会真正复制一份专用副本( private copy) 给该调用者, 而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的( transparently) 。此作法主要的优点是如果调用者没有修改该资源, 就不会有副本( private copy) 被创建, 因此多个调用者只是读取操作时可以共享同一份资源。
# 1.使用场景
## 1.1 在文件系统中的应用
数据的备份或者复制至少处于硬件(硬盘)和操作系统两个层级。硬件备份一般使用RAID0-10的方案, 保护硬盘中的数据安全; 操作系统中则使用Snapshot(快照)技术来维持数据的安全和有效。人们一直采用数据复制、备份、恢复等技术来保护重要的数据信 息,定期对数据进行备份或复制。由于数据备份过程会影响应用性能,并 且非常耗时,因此数据备份通常被安排在系统负载较轻时进行(如夜 间)。另外, 为了节省存储空间, 通常结合全量和增量备份技术。为了解决性能和持续运行的问题引入了Snapshot, Snapshot的实现又主要分为split mirror、changed block和current三大类, 具体实现方式中会有部分依赖COW技术。
## 1.2 软件和web系统中的应用
传统方式下,`fork()函数在创建子进程时直接把所有资源复制给子进程`, 即: 正文段块, 数据段块, 堆块, 栈块。这种实现方式简单, 但是效率低下, 而且复制的资源可能对子进程毫无用处。linux为了降低创建子进程的成本, 改进fork()实现方式使用COW技术创建子进程。当父进程创建子进程时, 内核只为子进程创建虚拟空间, 父子两个进程使用的是相同的物理空间。`只有父子进程发生更改时才会为子进程分配独立的物理空间`。
![[3669053-3b340bc054bdb1e0.png]]
上图展示从P1进程创建子进程P2时的物理空间使用状况。通过COW技术, fork()延迟了数据拷贝,根据子进程的实际操作最终可能完全避免数据复制,如:子进程创建后运行一个与当前数据无关的可执行文件。
### Redis中的COW
Redis的快照就是使用fork()函数创建子进程,由子进程读取数据并持久化到磁盘完成。
### JDK中的COW
`CopyOnWriteArrayList` 和`CopyOnWriteArraySet`都是使用在读多写少且数据总量不大的场景下, 在保证多线程写的原子性的同时又避免了读的冲突和竞争, 使用迭代器的时候也绝对不会抛出ConcurrentModificationException。