平衡二叉树
增删改查时间复杂度为O(log2N)
平衡的目的是增删改查后,保证下次搜索能稳定排除一半的数据
O(log2N)的直观理解:100万个节点,最多比较20次;10亿个节点,最多比较30次。
总结:通过比较保证有序,通过每次排除一半的元素达到快速索引的目的
hash函数
映射函数Hash(key) = addr;hash函数可能会把两个或两个以上不同的key映射到同一地址,这种情况称之为冲突或者hash碰撞。
处理碰撞的方式:
链地址法,里面分为两种,头插法和尾插法。头插法常用于数据库redis中,因为对于数据库而言,对于刚刚插入的数据未来也会最先访问。如果冲突太多链表过长,查找算法退化成O(logN),这时候可以把链表转换成红黑树或者AVL树(建议超过256个可以转换成红黑树结构)
开放寻址法,所有元素存放在哈希表数组中,不使用额外的数据结构,一般使用线性探测的思路解决。但容易导致hash聚集,也就是近似值它的hash值也近似,可以用双重hash解决。
计算速度快
强随机分布(等概率、均匀地分布在整个地址空间)
murmurhash1,murmurhash2,murmurhash3,siphash(redis6.0当中使用,rust等大多数语言选用的hash算法来实现hashmap),cityhash 等都具备强随机分布性;
siphash主要解决字符串接近的强随机分布性,key-value数据
布隆过滤器
背景
布隆过滤器是一种概率性数据结构,特点是高效插入和查询,能确定某个字符串是一定不存在和可能存在。
布隆过滤器不存储具体数据结构,所以占用空间很小,查询结果存在误差,但是误差是可控的,同时不支持删除操作,支持删除的话需要准备两个布隆过滤器,把要删除的数据放到另一个已删除的布隆过滤器,从第一个找到后去第二个判断是否被删除。
面试中常问hash函数实现过程当中为什么会出现 i*31?
- i * 31 = i * (32 - 1) = i * (1 << 5 - 1) = i << 5 - i
- 31是质数,hash随机分布性是最好的
构成
位图(BIT数组)+ n个hash函数
原理
当一个元素加入位图是,通过k个hash函数将这个元素映射到位图的k个点,并把它们置位1,当检索时再通过k个hash函数运算检测位图的k个点是否都为1;如果有不为1的点,那么认为该key不存在,如果全部为1,则可能存在。
应用场景
解决缓存穿透、热key限流
描述缓存场景,为了减轻数据库的访问压力,在server端与数据库(mysql)之间加入缓存用来存储热点数据。
描述缓存穿透的,server端请求数据时,缓存和数据库都不包含该数据,最终请求压力全部涌向数据库
发生原因:黑客利用漏洞伪造数据攻击或者内部业务bug造成大量重复请求不存在的数据
解决方案:在server端设置布隆过滤器预先存储一部分key,在访问数据库之前先通过布隆过滤器进行判断是否存在,可以知道是一定不存在还是可能存在,而这个存在的可能性p是可控的,可以排除大量的一定不存在数据请求,当然会出现误差,可控的误差。
分布式一致性hash
将哈希空间组织成一个虚拟的圆环,圆环的大小是2的32次方
应用场景:分布式缓存,将数据均衡的分散在不同的服务器中,用来分摊缓存服务器的压力;解决缓存服务器数量变化尽量不影响缓存失效。
分布式一致性hash只造成一部分影响
hash偏移有随机性,服务节点过于紧密造成存储不均衡,原因是节点太少,需要增加虚拟节点
增加或删除节点是需进行数据迁移。
缓存穿透 (不存在的key)
描述:访问一个缓存和数据库都不存在的 key,此时会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上。
解决:1、接口校验。2、缓存空值。3、布隆过滤器。
缓存击穿 (某些热key过期)
描述:某一个热点 key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。
解决: 1、加互斥锁(Redis 分布式锁) 2、热点数据不过期。
缓存雪崩 (大量key过期)
描述:大量的热点 key 设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库被打挂。
解决:1、过期时间打散。2、热点数据不过期。3、加互斥锁。