极客兔兔Gee-Cache Day4

  • 缓存雪崩:缓存在同一时刻全部失效(例如设置相同过期时间,服务器宕机),均需要去访问DB,造成DB瞬间访问量过大

    • 解决1:设置不同的过期时间
    • 解决2:保证热点数据永不过期
    • 解决3:缓存降级,降级是指返回一些默认值或者部分数据
    • 解决4:熔断,请求量过高时,自动拒绝请求
  • 一致性哈希

    • 过程
      • 1.开辟一个大小为 2 32 2^{32} 232的空间,将数字首尾相连形成环
      • 2.计算节点的Hash值,并映射到环的对应位置
      • 3.计算keyHash值,找到距离key最近的节点,发送请求
    • 数据倾斜
      • 节点数量过小,会导致key都被分配给某个节点
      • 解决:为真实节点引入虚拟节点,同时维护真实节点和虚拟节点的映射关系
  • day4需要实现一致性哈希的过程,一致性哈希的主要结构由Map进行维护

    • Map中有四个熟悉:hashreplicaskeyshashMap
    • hash:哈希函数类型,这样定义允许在一致性哈希中使用不同的哈希函数
    • replicas:表示一个真实节点对应的虚拟节点的数量
    • keys:整形切片,存储某个真实节点对应的虚拟节点的哈希值,方便二分快速定位虚拟节点的位置(实际是找大于等于key哈希值的最小节点)
    • hashMap:虚拟节点->真实节点的映射
  • 一致性哈希代码

    • package consistenthash
      
      import (
      	"hash/crc32"
      	"sort"
      	"strconv"
      )
      
      type hash func(data []byte) uint32
      
      type Map struct {
      	hash     hash           // 哈希函数
      	replicas int            // 对应虚拟节点数量
      	keys     []int          // 虚拟节点列表
      	hashMap  map[int]string // 虚拟节点->真实节点映射
      }
      
      func New(replicas int, fn hash) *Map {
      	m := &Map{
      		hash:     fn,
      		replicas: replicas,
      		hashMap:  make(map[int]string),
      	}
      	if m.hash == nil {
      		m.hash = crc32.ChecksumIEEE
      	}
      	return m
      }
      
      func (m *Map) Add(keys ...string) {
      	for _, key := range keys {
      		for i := 0; i < m.replicas; i++ {
      			hash := m.hash([]byte(strconv.Itoa(i) + key)) // 通过在前缀添加数字作为虚拟节点
      			m.keys = append(m.keys, int(hash))
      			m.hashMap[int(hash)] = key
      		}
      	}
      	sort.Ints(m.keys) // 排序方便二分
      }
      
      func (m *Map) Get(key string) string {
      	if len(m.keys) == 0 {
      		return ""
      	}
      	hash := m.hash([]byte(key))
      	idx := sort.Search(len(m.keys), func(i int) bool { // 二分查找最近的节点
      		return m.keys[i] >= int(hash)
      	})
      	return m.hashMap[m.keys[idx%len(m.keys)]]
      }
      
  • 测试代码

    • package consistenthash
      
      import (
      	"strconv"
      	"testing"
      )
      
      func TestHashing(t *testing.T) {
      	hash := New(3, func(key []byte) uint32 { // 假设key全为数字字符串,因此自定义hash值为对应数字
      		i, _ := strconv.Atoi(string(key))
      		return uint32(i)
      	})
      
      	hash.Add("6", "4", "2")
      	testDates := map[string]string{ // key应该对应的节点
      		"2":  "2",
      		"11": "2",
      		"23": "4",
      		"27": "2",
      	}
      
      	for k, v := range testDates {
      		if hash.Get(k) != v {
      			t.Errorf("%s获取节点错误~", k)
      		}
      	}
      
      	hash.Add("8")
      	testDates["27"] = "8"
      	for k, v := range testDates {
      		if hash.Get(k) != v {
      			t.Errorf("%s获取节点错误~", k)
      		}
      	}
      
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值