0. 基础知识
哈希函数:输入无限,输出有限,一个输入对应一个输出,但一个输出可能对应多个输入(哈希碰撞),且输出域具有离散性,均匀分布
MD5:长度为16位,采用16进制(0~f),范围为[ 0, 2^64-1 ]
SHA1:长度为32位,采用16进制,范围为[ 0, 2^128-1]
字节换算
1 T = 2^10 G = 2^10 * 2^10 MB = 2^10 * 2^10 *2^10 KB = 2^40 B = 2^40 * 8 b
1 B = 8 b
1 kB = 2^10 B
1 MB = 2^10 KB
1 G = 2^10 MB
1 T = 2^10 G
1 G = 2^30 B = 1073,741,824 B ~= 10亿B
// 一个int整型长度为 4B = 32b,范围为[-2^31,2^31-1]
1.设计一个RandomPool结构
有三个功能,时间复杂度为 O(1)
insert(key): 将key插入结构中,且不重复
delele(key): 将结构中的key删除
getRandom(): 等概率随机返回结构中的任意一个key
class RandomPool {
constructor() {
// 维护两个哈希表,记录key-index和index-key,且结构中有size属性
this.keyIndexMap = new Map()
this.indexKeyMap = new Map()
this.size = 0
}
insert(key) {
if (!this.keyIndexMap.has(key)) {
this.keyIndexMap.set(key, this.size)
this.indexKeyMap.set(this.size, key)
this.size++
}
}
delete(key) {
// 要实现等概率随机返回key,那么key索引应保持连续,
// 删除操作时采用最后一个元素替换当前元素的方式实现
if (this.keyIndexMap.has(key)) {
this.size--
let curIndex = this.keyIndexMap.get(key)
let lastKey = this.indexKeyMap.get(this.size)
this.keyIndexMap.set(lastKey, curIndex)
this.indexKeyMap.set(curIndex, lastKey)
this.keyIndexMap.delete(key)
this.indexKeyMap.delete(this.size)
}
}
getRandom() {
if (this.size == 0) return null
return this.indexKeyMap.get(Math.floor(Math.random() * this.size))
}
}
2.布隆过滤器(大厂常考)
用于设计黑名单系统、解决爬虫去重问题,采用hash存储,极大的减少存储空间
考虑set的add和check
黑名单不会判断为白,但白名单有极小几率判断为黑
// 位图存储
function example() {
let arr = new Array(10) // int[] 32bit * 10 -> 320 bits
// arr[0] int 0 ~ 31
// arr[1] int 32 ~ 63
// arr[2] int 64 ~ 95
let i = 178 // 取178位
let numIndex = i / 32 // 数索引
let bitIndex = i % 32 // 位索引
// 1.查i位状态
let status = (arr[numIndex] >> bitIndex) & 1 // (11101 >> 2) & 0001 == 1,2位状态为1
// 2.改i位状态为1
arr[numIndex] = arr[numIndex] | (1 << bitIndex)
// 3.改i位状态为0
arr[numIndex] = arr[numIndex] & (~(1 << bitIndex))
}
// 假如使用m bit的长度的数组来存储黑名单Un,fk为哈希函数
U1 --f1--> out1 --%m--> 0到m-1的随机数
U1 --f2--> out1 --%m--> 0到m-1的随机数
U1 --fk--> out1 --%m--> 0到m-1的随机数
U2 --f1--> out2 --%m--> 0到m-1的随机数
U2 --f2--> out2 --%m--> 0到m-1的随机数
U2 --fk--> out2 --%m--> 0到m-1的随机数
...
Un --f1--> outn --%m--> 0到m-1的随机数
Un --f2--> outn --%m--> 0到m-1的随机数
Un --fk--> outn --%m--> 0到m-1的随机数
// 将数组上随机数索引处置1表示黑名单
// 这样在调用k个哈希函数查某个黑名单时返回k个值都为1,查白名单时返回的K个值中只要有0那么必然是白名单
// 存储数组位长度m及调用的哈希函数数目k根据Un长度以及可容错率p设定,尽可能减少hash碰撞
布隆过滤器考虑点:
样本量n、失误率p、存储长度m、哈希调用次数k
m = (-n*lnP) / (ln2)^2 bit
k = Math.ceil(ln2 * m / n) ~= 0.7*m/n 个
3.一致性哈希
存在问题:
- 服务器负载不均衡
- 即使负载均衡了,在添加或移除某个服务器时,又不均衡了
一致性哈希:
- 在服务器数量少时,如何把环均分?
- 如何做到负载均衡?
// 机器生成1000个哈希值(虚拟节点),最后再去抢环
// 由于哈希输出具有离散性,固只要将一台机器切分成足够多的虚拟节点,在环上分布就足够均匀!
m1(a1, a2, ... , a1000)
m2(b1, b2, ... , b1000)
m3(c1, c2, ... , c1000)
// 如此占环之后,环上任意一段分布的不同机器虚拟节点数一定相同!
// 若m1是强负载,m2是中负载,m3是低负载,那么m1切分出1500个,m2切分出1000个,m3切分出500个即可做到负载均衡