写时复制

写时复制(Copy-on-Write, CoW)最核心思路:​​只有当数据被修改时,才真正复制一份新的数据副本。在修改之前,所有使用者共享同一份数据。​

假设我们有一个共享数据对象 data = "A"

  1. ​初始状态​​:

    • 有两个变量 ab 都指向这个数据

    a → ["A"] ↑ b ─────┘

  2. ​当 a 要修改数据​​(比如改成"B"):

    • 系统发现 ab 共享同一份数据

    • ​先复制​​:创建数据的新副本 "B"

    • ​再修改​​:让 a 指向新副本

    • ​原数据保留​​:b 仍然指向旧数据 "A"

    a → ["B"] (新副本)

    b → ["A"] (原数据)

  3. ​核心原则​​:

    • ​读操作​​:不复制,多个读者共享同一份数据

    • ​写操作​​:先复制再修改,确保修改不会影响其他使用者

package main  
  
import "fmt"  
  
type database struct {  
    data int // 原始数据  
}  
  
type snapshot struct {  
    data *int // 指向原始数据指针  
}  
  
var sna = &snapshot{}  
  
func (d *database) createShot() *snapshot {  
    return &snapshot{data: &d.data} // 创建快照,只共享指针  
}  
  
func (d *database) clone() *snapshot {  
    s := d.data  
    return &snapshot{data: &s} // 复制新快照  
}  
  
func (d *database) set() {  
    sna = d.clone() // 触发写时复制,复制一份给快照  
    d.data = 1  // 修改原数据  
}  
  
func main() {  
    d := database{data: 2}  
    sna = d.createShot()  
    fmt.Println(*sna.data) // 2  
  
    d.set()             // 写时复制:源数据修改,快照复制数据  
    fmt.Println(d.data) // 1  
  
    fmt.Println(*sna.data) // 2  
  
}

写时复制的三个黄金法则:

  1. ​读共享​​:多个读者共享同一份数据

  2. ​写分离​​:写操作时自动创建新副本

  3. ​零复制​​:没有修改时不做任何数据拷贝

共享指针直到修改时刻,修改前才真正复制数据​

最后更新于