驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
一致性hash和常规取模的对比
/  

一致性hash和常规取模的对比

正文

从分库分表的业务来讲,需要根据某个key将数据分散到N个数据表中,该做法有2种类型。

  • hash(key) % N
  • 一致性hash

传统取模

第一个方法:很显然当N变动的时候,原始取模的做法存在数据抖动很大,但是如果采用一致性hash,当数据表有变更的时候,抖动会很小。

一致性Hash

第二个是一致性Hash,那么他的原理是什么?简单来说:

  • 2^32次方想象为组成圆环上的刻度
  • 目标key取hash值后同 2^32次方取模
  • 将资源分布在上述刻度中,按照一个方向,模离资源所在的刻度最近的就分布在那个资源节点上
    • 资源节点的分布如果少,那么也会造成资源倾斜的问题
    • 可以考虑将资源节点多一些复制节点,然后分布到圆环刻度上,可以减轻资源变动的风险

代码测试

代码参考如下,假如有2W条数据插入5个、6个 数据表,可以很明显的对比出,传统的取模抖动是一致性hash的6倍。

fun main() {
    consistentHash1()
    normalHash()
}

/**
 * 28103
 */
private fun consistentHash1() {

    val hash1 = ConsistentHash(20, listOf("1", "2", "3", "4", "5"))
    val map1 = (1..200000).associateWith { hash1.get(it) }


    val hash2 = ConsistentHash(20, listOf<String>("1", "2", "3", "4", "5", "6"))
    val map2 = (1..200000).associateWith { hash2.get(it) }

    val size = (1..200000).count { map1[it] != map2[it] }
    println("一致性Hash 不同的size为:${size}")
}

/**
 * 166666
 */
private fun normalHash() {
    val map1 = (1..200000).associateWith { it.hashCode() % 5 }
    val map2 = (1..200000).associateWith { it.hashCode() % 6 }
    val size = (1..200000).count { map1[it] != map2[it] }
    println("取模 不同的size为:${size}")
}
骐骥一跃,不能十步。驽马十驾,功在不舍。