2.8 KiB
写入时复制(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技术创建子进程。当父进程创建子进程时,内核只为子进程创建虚拟空间,父子两个进程使用的是相同的物理空间。只有父子进程发生更改时才会为子进程分配独立的物理空间
。
!
上图展示从P1进程创建子进程P2时的物理空间使用状况。通过COW技术,fork()延迟了数据拷贝,根据子进程的实际操作最终可能完全避免数据复制,如:子进程创建后运行一个与当前数据无关的可执行文件。
Redis中的COW
Redis的快照就是使用fork()函数创建子进程,由子进程读取数据并持久化到磁盘完成。
JDK中的COW
CopyOnWriteArrayList
和CopyOnWriteArraySet
都是使用在读多写少且数据总量不大的场景下,在保证多线程写的原子性的同时又避免了读的冲突和竞争,使用迭代器的时候也绝对不会抛出ConcurrentModificationException。