您的位置:首页 > 健康 > 养生 > android开发app的详细过程_网络营销的理论和特点有哪些_如何搭建一个网站_sem竞价推广托管代运营公司

android开发app的详细过程_网络营销的理论和特点有哪些_如何搭建一个网站_sem竞价推广托管代运营公司

2025/1/4 17:27:17 来源:https://blog.csdn.net/weixin_51147313/article/details/143634489  浏览:    关键词:android开发app的详细过程_网络营销的理论和特点有哪些_如何搭建一个网站_sem竞价推广托管代运营公司
android开发app的详细过程_网络营销的理论和特点有哪些_如何搭建一个网站_sem竞价推广托管代运营公司

场景设定

  • 真实节点(服务器)NodeANodeB
  • 虚拟节点副本数m.replicas = 3
  • 数据键"Data1""Data2""Data3"

我们将:

  1. 初始化一致性哈希环
  2. 添加真实节点,并生成虚拟节点。
  3. 存储数据,将数据键映射到真实节点。
  4. 查找数据,根据数据键找到对应的真实节点。
  5. 模拟节点的增减,观察数据映射的变化。

步骤 1:初始化一致性哈希环

首先,创建一个一致性哈希环的结构体 Map,包含以下属性:

  • hash:哈希函数。
  • replicas:虚拟节点的数量(每个真实节点的副本数)。
  • keys:存储虚拟节点的哈希值,有序排列。
  • hashMap:映射虚拟节点哈希值到真实节点。
type Map struct {hash     HashFunc           // 哈希函数replicas int                // 虚拟节点副本数keys     []int              // 排序后的虚拟节点哈希值hashMap  map[int]string     // 虚拟节点哈希值到真实节点的映射
}

假设我们使用 Go 语言的 crc32.ChecksumIEEE 作为哈希函数。

func New(replicas int, fn HashFunc) *Map {return &Map{hash:     fn,replicas: replicas,hashMap:  make(map[int]string),}
}

初始化哈希环

hashRing := New(3, crc32.ChecksumIEEE)

步骤 2:添加真实节点并生成虚拟节点

调用 Add 方法,将真实节点 NodeANodeB 添加到哈希环中。

hashRing.Add("NodeA", "NodeB")

Add 方法的实现

func (m *Map) Add(keys ...string) {for _, key := range keys {for i := 0; i < m.replicas; i++ {virtualNodeID := strconv.Itoa(i) + keyhash := int(m.hash([]byte(virtualNodeID)))m.keys = append(m.keys, hash)m.hashMap[hash] = key}}sort.Ints(m.keys)
}

执行过程

  • 为每个真实节点生成 3 个虚拟节点
  • 计算每个虚拟节点的哈希值,并建立映射关系。

示例

  • 对于 NodeA

    • 虚拟节点 "0NodeA",哈希值 hash("0NodeA")
    • 虚拟节点 "1NodeA",哈希值 hash("1NodeA")
    • 虚拟节点 "2NodeA",哈希值 hash("2NodeA")
  • 对于 NodeB

    • 虚拟节点 "0NodeB",哈希值 hash("0NodeB")
    • 虚拟节点 "1NodeB",哈希值 hash("1NodeB")
    • 虚拟节点 "2NodeB",哈希值 hash("2NodeB")

假设计算得到以下哈希值

虚拟节点标识符哈希值(假设)真实节点
"0NodeA"105NodeA
"1NodeA"215NodeA
"2NodeA"325NodeA
"0NodeB"125NodeB
"1NodeB"235NodeB
"2NodeB"345NodeB
  • 将哈希值添加到 m.keys 列表中
  • 建立哈希值到真实节点的映射 m.hashMap

排序后的 m.keys 列表

m.keys = [105, 125, 215, 235, 325, 345]

步骤 3:存储数据

数据键

  • "Data1"
  • "Data2"
  • "Data3"

存储过程

对于每个数据键,执行以下步骤:

  1. 计算数据键的哈希值
  2. m.keys 中查找第一个大于等于该哈希值的虚拟节点
  3. 通过 m.hashMap 获取对应的真实节点

实现代码

func (m *Map) Get(key string) string {if len(m.keys) == 0 {return ""}hash := int(m.hash([]byte(key)))idx := sort.Search(len(m.keys), func(i int) bool {return m.keys[i] >= hash})return m.hashMap[m.keys[idx%len(m.keys)]]
}

具体执行

  • 数据键 "Data1"

    • 计算哈希值

      hash("Data1") = 220  // 假设哈希值为 220
      
    • 查找虚拟节点

      • m.keys 列表:[105, 125, 215, 235, 325, 345]
      • 使用 sort.Search,找到索引 idx = 3(因为 m.keys[3] = 235 >= 220
    • 获取真实节点

      node = m.hashMap[m.keys[3]]  // m.keys[3] = 235
      node = "NodeB"
      
    • 存储位置"Data1" 存储在 NodeB

  • 数据键 "Data2"

    • 计算哈希值

      hash("Data2") = 330  // 假设哈希值为 330
      
    • 查找虚拟节点

      • 找到索引 idx = 5(因为 m.keys[5] = 345 >= 330
    • 获取真实节点

      node = m.hashMap[m.keys[5]]  // m.keys[5] = 345
      node = "NodeB"
      
    • 存储位置"Data2" 存储在 NodeB

  • 数据键 "Data3"

    • 计算哈希值

      hash("Data3") = 90  // 假设哈希值为 90
      
    • 查找虚拟节点

      • 找到索引 idx = 0(因为 m.keys[0] = 105 >= 90
    • 获取真实节点

      node = m.hashMap[m.keys[0]]  // m.keys[0] = 105
      node = "NodeA"
      
    • 存储位置"Data3" 存储在 NodeA


步骤 4:查找数据

当我们需要读取数据时,使用相同的 Get 方法,根据数据键找到对应的真实节点,然后从该节点获取数据。

示例

  • 查找 "Data1"

    • 计算哈希值 hash("Data1") = 220
    • 查找到虚拟节点哈希值 235
    • 映射到真实节点 NodeB
    • NodeB 获取 "Data1" 数据

步骤 5:模拟节点的增减

情况一:新增节点 NodeC

  • 添加 NodeC

    hashRing.Add("NodeC")
    
  • NodeC 创建虚拟节点

    虚拟节点标识符哈希值(假设)真实节点
    "0NodeC"160NodeC
    "1NodeC"270NodeC
    "2NodeC"380NodeC
  • 更新 m.keys 列表并排序

    m.keys = [105, 125, 160, 215, 235, 270, 325, 345, 380]
    
  • 重新映射数据

    • "Data1"(哈希值 220)

      • 新的索引 idx = 4m.keys[4] = 235 >= 220
      • 真实节点仍为 NodeB
    • "Data2"(哈希值 330)

      • 新的索引 idx = 6m.keys[6] = 325 < 330,但 m.keys[7] = 345 >= 330
      • 真实节点为 NodeB(无变化)
    • "Data3"(哈希值 90)

      • 索引 idx = 0m.keys[0] = 105 >= 90
      • 真实节点为 NodeA(无变化)
  • 添加新数据 "Data4"(哈希值 280)

    • 计算哈希值 hash("Data4") = 280
    • 索引 idx = 6m.keys[6] = 325 >= 280
    • 真实节点为 NodeA
    • "Data4" 存储在 NodeA

情况二:移除节点 NodeB

  • 从哈希环中移除 NodeB

    hashRing.Remove("NodeB")
    
  • Remove 方法的实现(简化版):

    func (m *Map) Remove(key string) {for i := 0; i < m.replicas; i++ {virtualNodeID := strconv.Itoa(i) + keyhash := int(m.hash([]byte(virtualNodeID)))idx := sort.Search(len(m.keys), func(i int) bool {return m.keys[i] == hash})if idx < len(m.keys) && m.keys[idx] == hash {m.keys = append(m.keys[:idx], m.keys[idx+1:]...)}delete(m.hashMap, hash)}
    }
    
  • 更新后的 m.keys 列表

    m.keys = [105, 160, 215, 270, 325, 380]
    
  • 重新映射数据

    • "Data1"(哈希值 220)

      • 新的索引 idx = 3m.keys[3] = 270 >= 220
      • 真实节点为 NodeC(原来是 NodeB
    • "Data2"(哈希值 330)

      • 新的索引 idx = 5m.keys[5] = 380 >= 330
      • 真实节点为 NodeC(原来是 NodeB
    • "Data3"(哈希值 90)

      • 索引 idx = 0m.keys[0] = 105 >= 90
      • 真实节点为 NodeA(无变化)
  • 结果

    • "Data1""Data2" 重新映射到 NodeC
    • "Data3" 仍然在 NodeA

总结

通过以上模拟,我们可以看到:

  • 一致性哈希算法在节点增减时,只有部分数据需要重新映射,保证了数据迁移的最小化
  • 虚拟节点的引入,使得数据在真实节点间分布更加均衡,避免了数据倾斜
  • 通过哈希环的顺时针查找,简单高效地实现了数据键到节点的映射

注意事项

  • 哈希函数的选择:应使用分布性良好的哈希函数,避免哈希冲突。
  • 虚拟节点数量的设定:虚拟节点越多,数据分布越均衡,但也增加了哈希环的维护开销。
  • 一致性:在分布式环境中,各节点应共享相同的哈希环视图,保证一致性。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com