分布式一致性之NWR策略模型:
<1>nwr策略解释:
N:在分布式存储系统中,有多少份备份数据。
W:代表一次成功的更新操作要求至少有w份数据写入成功 。
R:代表一次成功的读数据操作要求至少有R份数据成功读取。
<2>策略原理:
NWR值的不同组合会产生不同的一致性效果,当W+R>N的时候,整个系统对于客户端来讲能保证
<3>不同组合产生的效果
w+r > n -> 对于客户端来说,强一致性保证。
w+r <= n -> 因客户端读or写的时候,(再多线程情况下会导致数据非强一致性)
模拟代码:
package main
import (
"fmt"
"math/rand"
"strconv"
"sync"
"time"
"github.com/bradfitz/gomemcache/memcache"
)
var mcDB []*memcache.Client
var (
server1 = "host1:11211"
server2 = "host2:11211"
server3 = "host3:11211"
)
var wirte, read []string
var lock sync.RWMutex
func main() {
// create a handle
mc1 := memcache.New(server1)
if mc1 == nil {
fmt.Println("memcache1 New failed")
}
mc2 := memcache.New(server2)
if mc2 == nil {
fmt.Println("memcache2 New failed")
}
mc3 := memcache.New(server3)
if mc3 == nil {
fmt.Println("memcache3 New failed")
}
mcDB = append(mcDB, mc1)
mcDB = append(mcDB, mc2)
mcDB = append(mcDB, mc3)
// 创建才是三个协程读取数据,三个协程序写数据
var wg sync.WaitGroup
go func() {
wg.Add(1)
defer wg.Done()
//
for {
n := rand.Int() % 10
key := "one_" + strconv.Itoa(n)
// v := rand.Int() % 3 // 随机选取写入那个mc
cnt := 0
err := mcDB[0].Set(&memcache.Item{Key: key, Value: []byte(key)})
if err == nil {
cnt++
}
err = mcDB[1].Set(&memcache.Item{Key: key, Value: []byte(key)})
if err == nil {
cnt++
}
err = mcDB[2].Set(&memcache.Item{Key: key, Value: []byte(key)})
if err == nil {
cnt++
}
if cnt < 2 {
// 少于2节点写入,认为写入失败,清理掉
mcDB[0].Delete(key)
mcDB[1].Delete(key)
mcDB[2].Delete(key)
} else {
// 写入成功,展示俩表中标记
lock.Lock()
wirte = append(wirte, "v")
lock.Unlock()
}
time.Sleep(time.Second * 1)
}
}()
// read
go func() {
wg.Add(1)
defer wg.Done()
for {
n := rand.Int() % 10
key := "one_" + strconv.Itoa(n)
cnt := 0
if _, err := mcDB[0].Get(key); err == nil {
cnt++
}
if _, err := mcDB[1].Get(key); err == nil {
cnt++
}
if _, err := mcDB[2].Get(key); err == nil {
cnt++
}
if cnt >= 2 {
lock.Lock()
read = append(read, "v")
lock.Unlock()
}
time.Sleep(time.Second * 1)
}
}()
// 独立一个协程输出当前list的内容
go func() {
for {
lock.Lock()
for _, v := range wirte {
fmt.Print(v, "<w>")
}
fmt.Println()
wirte = []string{}
for _, v := range read {
fmt.Print(v, "<r>")
}
fmt.Println()
read = []string{}
lock.Unlock()
time.Sleep(time.Second * 3)
}
}()
// 启动一个协程等待所有协程运行完成
go func() {
wg.Add(1)
defer wg.Wait()
}()
// 主协助空跑,让所有子协程序跑起来
fmt.Println("main is running ... ")
select {}
}
结果:
v<w>v<w>v<w>
v<r>v<r>v<r>
v<w>v<w>v<w>
v<r>v<r>v<r>
v<w>v<w>
v<r>v<r>
v<w>v<w>v<w>
v<r>v<r>v<r>
v<w>v<w>
v<r>v<r>
v<w>v<w>v<w>
v<r>v<r>v<r>
v<w>v<w>v<w>