软件架构与思考

👉 所有文章
高性能 高性能设计模式
数据库 数据库分库分表指南 MySQL 建表参考 MySQL 初始化数据的一些方案 数据版本号
分布式 ID 分布式 ID 生成方案探讨
缓存 缓存 使用数据版本号保证缓存是最新数据 基于redis的二级缓存
微服务 如何实现远程调用 RPC 协议中的数据签名验签和加解密方案探讨 关于服务间调用循环依赖的一些思考 我所理解的负载均衡 一致性哈希 基于Redis的分布式会话管理系统 如何部署服务 灰度发布 如何区分上游和下游 日志级别
算法与协议 一个可扩展的 MQ 消息设计 Dynamo涉及的算法和协议 写时复制
任务分发 Gearman入门 如何使用redis构建异步任务处理程序
安全 关于对账的一些理解 一个简单可靠的 Dubbo 请求/响应数据签名方案
其他 使用卫语句减少 if else 嵌套

写时复制


2014-06-12

写时复制,Copy-On-Write,简写为COW。

本文以python中的字符串为例。在python中,字符串是无法修改的。我们看下面的python代码:

s1 = 'abc'
s2 = s1
print s1, id(s1)
print s2, id(s2)

s2 = 'bcd'
print s1, id(s1)
print s2, id(s2)

id()函数用来返回对象的标识符,也就是对象在内存中的地址。下面是一次运行结果:

abc 5155392
abc 5155392
abc 5155392
bcd 34597896

上面的过程,可以用下面的图来表达:

开始时,变量s1和s2同时指向同一个字符串对象'abc',而该对象的引用计数为2。当对s2重新赋值时,发生了写时复制。这时,会在内存中新创建一个内存对象'bcd',s2指向这个新的对象,s1的指向不变。而此时,两个在内存中的字符串对象的引用计数都是1。

如果不使用写时复制的思路,那么在开始时候,在内存中就会有两个内容为'abc'的字符串对象。如果在后期并不会修改s1或者s2,那么会造成资源的浪费。所以可以认为写时复制是一个优化策略。

写时复制的例子是很常见的。

例如,你在某网站上注册一个账号,该网站会给你分配一个默认的图片作为头像,所有的新注册用户都拥有相同的默认头像,这可以类比成很多变量名指向了同一个对象。但是,用户也可以自定义头像(例如通过上传图片等方式),当某个用户自定义头像后,新的头像文件不应该去替换默认的头像文件,不过该网站会确保该用户的头像指向的是新图片,而非默认图片,而这一步就可以类比成写时复制

快照也使用了写时复制的技术。所谓快照,就是获取的一个文件的瞬间状态。我们要获取的瞬时状态可以类比成上面例子中s1变量指向的'abc',在快照期间肯定不能对'abc'进行写操作,所以首先创建s2指向'abc',新写入的数据则保存在s2中。当快照结束后,将s2合并到s1。

Linux写时拷贝技术(copy-on-write)也是一个例子。


( 本文完 )

文章目录