c/c++后台开发学习笔记 1.1.3 海量数据去重的Hash,BloomFilter,bitmap

背景

  • 判断单词拼写是否正确
  • 判断垃圾邮件
  • url去重
  • 缓存穿透问题(访问不存在的元素会穿透redis缓存而始终访问mysql,造成mysql压力过大)

需求

海量数据中查询某个字符串是否存在

平衡二叉树

红黑树、AVL,b/b+树, 跳表
时间复杂度是O(log2(n))
10亿节点最多比较30次

散列表(哈希表)

根据key计算在表中存储的位置

hash函数

Hash(key)–>addr; 两个key被隐射到一个地址称为冲突
负载因子:数据储存元素个数/数据长度 (越小冲突越小)

冲突处理

  • 链表法:java/c++ 标准库都是链表法
    • 头插法:redis等都是这样,因为对于数据库,最新的数据是最常访问的
    • 尾插法:算法有可能退化成o(n)
    • 可以将过长的链表(经验值:256)转化成红黑树(java)
  • 开放寻址法
    • 搜索addr,addr+a(i,1) , addr+a(i,2)…
    • 根据a,如果a是线性,容易哈希聚集,效率不行,如果a是二次,则会讲聚集延后 (都不怎么用)
    • 另外还可以使用双重哈希来解决哈希聚集的现象(原理略)

BloomFilter

概率性数据结构
高效地插入和查询
能确定某个字符串一定不存在或可能存在
不存储具体数据–>占用空间小,查询结果存在误差但是误差可控
不支持删除操作

构成

bit数据+n个hash函数
每个hash把key映射到一个bit,多个hash映射到多个bit,可以告诉我们字符串是否存在
加入一共64bit
hash(val)=173

  1. 174%64 = 45
  2. j=45%8
  3. i=45/8
    插入时把每个hash对应的bit设为1
    查询时,检测每个hash对应的bit是否都为1,如果有0则一定不存在,否则可能存在
    为什么不能删除?因为如果删除,则可能把其他key的bit也置为0

应用场景

用于判断某个key一定不存在,同时允许判断有误差的情况

  1. 解决缓存穿透
  2. 热key限流

应用分析

多少个hash?多少bit?预期储存多少元素?如何控制误差?
公式:

n -- 预期放多少个元素
p -- false positive possibility (判断为存在但实际不存在的概率)
m -- 位图所占空间
k -- hash个数
n = ceil(m / (-k / log(1 - exp(log(p) / k)))) 
p = pow(1 - exp(-k / (m / n)), k) 
m = ceil((n * log(p)) / log(1 / pow(2, log(2)))); 
k = round((m / n) * log(2));

https://hur.st/bloomfilter
先确定n和p,再计算m和k

选择hash

  • 计算速度快
  • 强随即缝补(等概率、均匀地分布在整个空间)
  • murmurhash1, murmurhash2, murmurhash3, siphash (redis6.0使用,rust使用),cityhash
  • siphash主要解决字符串接近的强随机分布性
  • 为什么hash实现过程当中经常出现i*31?
    • i*31 == i << 5 - i 计算快
    • 31是质数,hash随机分布性好(好于17和101)

构造k个hash函数

取两个hash函数,hash1(key), hash2(key)

hash[i] = (hash1 + hash2*i) % m

如果一定要支持删除操作怎么办

添加第二个布隆过滤器,用于记录已删除,先走之前那个,再查第二个,如果查到则表示这个元素已经被删除(也有误差)

解决缓存穿透

场景

  1. 访问redis,如果redis不存在走2
  2. 访问mysql,如果存在返回数据

缓存穿透:当数据不存在时一定会走mysql,数据库压力很大
发生原因:黑客利用伪造数据共计,或者业务bug造成大量重复请求不存在的数据
解决方法:在server增加一个bloomfilter,把mysql中存在的可以都存进去,这样就能在server端判断不存在的数据了

分布式一致性hash

把hash空间组织成一个虚拟的圆环,圆环大小是2^32

分布式缓存

比如memcached集群
一开始有3台机器,我们用hash(key)%3分区
后来又加了一台机器,变成hash(key)%4分区,可是原来的映射被改变了(没有一致性)

一致性hash

一个圆环
每个服务器落在hash(ip:port)%2^32
每个key计算hash(key)%2^32,然后顺时针找到第一台服务器

增加节点

增加节点可以把原来之后那个节点的一部分数据分给新节点,其他节点不影响
怎么消除影响:数据迁移

如何保证每个服务器均匀分布?

不均匀的原因是什么:节点太少
解决方法: 虚拟节点
每个服务器都有多个虚拟节点hash(ip:port:1), hash(ip:port:2) … hash(ip:port:30)
把虚拟节点映射到真实节点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值