平衡二叉树
- 增删改查时间复杂度O(log2N)
- 平衡的目的是增删改后,保证下次搜索能稳定排除一半的数据
- 100万数据,最多只需比较 20 次 (为啥?朋友们?)
- 总结:通过比较保证有序,通过每次排除一半的元素达到快速索引的目的
散列表
根据key计算key在表中的问题的数据结构; 是key和其所在存储位置的映射关系
- 为了能够高效存取,散列表使用了顺序结构与链式结构混合的方式来实现 [4]。散列表的数据结构大致如图 1。其中黑色部分为链式结构,棕色部分为顺序结构。
之所以采用这样的结构是有几个原因,第一,元素的键通过散列函数所转化出的整数,经过计算后直接对应于顺序结构中的某个位置,可实现高效的随机存取;第二,如果不同的键经过散列函数计算后得到了相同的值,则需要使用链式结构来表示这些元素在逻辑上被放置于同一个位置。
一旦明白散列表的数据结构后,也就不难明白散列表访问元素时的流程了:
-
将指定的键通过散列函数转化为一个整数值 n。
-
使用这个得到的数值 n 计算出一个表中的合法位置 x ,访问这个 x 的位置。
-
如果表中的 x 位置没有任何对象,则说明散列表中目前并没有指定键所对应的元素。
-
如果表中的 x 位置有指针指向某个对象,那么这个对象一定是一个链式结构的起始。依次检查链式结构的每一个节点,如果有某一个节点的键与指定的键一致,则说明找到元素;如果没有找到,则说明散列表中目前并没有指定键所对应的元素。
hash函数
映射函数hash(key) = addr,hash函数可能会把两个或者以上的不同key映射到同一个地址,这种情况称为 hash冲突 (hash碰撞)
选择 hash
- 计算速度快
- 强随机分布 (等概率,均匀的分布在整个地址空间)
冲突解决
- 链表法
用链表来处理哈希冲突,如果冲突元素过多,链表会转化为红黑树 - 开放寻址法
问题:哈希聚集(近似key的对应hash值也近似,导致他的数组槽位也靠经,形成hash聚集)
方案:双重哈希(再哈希)
hash的原理与hash函数的实现
hash的应用场景
分布式hash的实现原理
海量数据去重布隆过滤器
- 布隆过滤器是一种概率型的数据结构,特点是搞笑的插入和查询,能确定某个字符串一定不存在或者可能存在
- 布隆过滤器不存储具体数据,所以占用空间小,查询结果存在误差,但是误差可控,但是不支持删除操作
- 构成: 位图(BIT数组)+ n个hash函数
原理:
为什么不支持删除操作呢?
在位图中,每个槽位只有两种状态(0 ,1),一个槽位被置成 1 状态,但是不确定它被设置了多少次,也就是不知道被多少个key哈希映射而来,也不知道被多少个hash函数映射而来,不可逆哦。
应用场景
布隆过滤器通常用于判断某个key 一定不存在的场景,同时允许判断存在时存在误差的情况(比如判断1000次存在,有1次判断时错误的,
具体就是假阳率,错误率多少)
- 缓存穿透
- 热 key 限流
布隆过滤器的数学推导与证明
分布式一致性 hash
分布式一致性 hash算法将哈希空间组织成一个虚拟的圆环,圆环的大小是 2*32
算法:hash(ip) % 2*32,最终会得到一个 [0, 2*32 - 1] 之间的一个无符号整型,这个整形代表服务器的编号,
多个服务器都通过这种方式在 hash 环上映射一个点来标识该服务器的位置;当用户操作某个key,通过同样的算法生成一个值,沿环顺时针定位某个服务器,那么该key就在该服务器中
应用场景
分布式缓存:将数据均衡的分散在不同的服务器中,用来分摊缓存服务器的压力
解决缓存服务器数量变化尽量不影响缓存失效
hash 偏移
hash 算法得到的结果是随机的,不能保证服务器节点均匀分布在哈希环上;
分布不均导致造成请求访问不均匀
虚拟节点
为了解决哈希偏移的问题,增加了虚拟节点的概念,理论上,哈希环上节点树越多,数据分布越合理
为每个服务节点计算多个 哈希节点(虚拟节点),通常做法 hash("IP:PORT:seqno")% 2*32
比如:一个实际节点对应多个 虚拟节点
数据迁移
增加,或者删除节点的时候,要考虑 进行 数据迁移
- 在新增一个实际节点后,会为它生成一些虚拟节点,每个虚拟节点有一个自己的哈希值,会对应到哈希环中的一个位置,插入新虚拟节点后可能会需要从后面位置的虚拟节点上“抢”一些数据。抢夺的数据可能与当前的虚拟节点是属于同一个实际节点的,例如:原本的虚拟节点列表为[1,100,500,1500],在新增实际节点A时,先生成了一个虚拟节点1000,那么它会从1500节点上“抢”走范围在501 ~ 1000的数据;然后节点A又生成了一个哈希值为800的虚拟节点,那么就会从节点1000上“抢”走范围在501 ~ 800的数据。
- 删除一个实际节点时,属于它的虚拟节点也要删除。如果在删除过程中,某个虚拟节点有存放数据,那么就要从当前虚拟节点的位置向后遍历,找到第一个不属于要被删除实际节点的虚拟节点。例如目前哈希值为1000、属于A实际节点的虚拟节点要被删掉了,1500节点也属于A,不能把数据迁移到这里;2000节点属于B,因此选择把1000节点上的数据迁移到2000节点。
100万数据
- 因为 ln100万 = ln 10*6 = 6,又因为 log2 100万 = 3 * ln 100万 = 18