Redis是一个高性能,单线程(新版本是多线程)的kv数据库。
具有速度快,可持久化,多种数据结构,支持主从复制和分布式。
Redis速度快的主要原因是纯内存,非阻塞IO,使用单线程避免线程切换和数据竞争
五种数据结构
String,hash,set,list,zset (zset是基于跳表的有序集合)
zset与set一样,string类型元素的集合,且不允许重复的成员。是由一个 hash 和 skiplist 组成的,其中 hash 用来保存 value 和 score 对应关系.通过score来为集合中的成员进行从小到大的排序。
skiplist 跳表:链表加多级索引的结构。
Redis 的跳跃表共有 64 级,kv之间使用指针形成有序的双向链表.同一层的 kv 会使用指针串起来.每层元素的遍历都是从跳跃表的头指针 kv header 出发.
每次查找一个结点时,需要遍历的结点数为 3*跳表高度 ,所以忽略低阶项和系数后的时间复杂度就是 ○(㏒n)
跳表不仅支持查找操作,还支持动态的插入、删除操作,而且插入、删除操作的时间复杂度也是 ○(㏒n)。
对于单纯的单链表,需要遍历每个结点来找到插入的位置。但是对于跳表来说,因为其查找某个结点的时间复杂度是 ○(㏒n),所以这里查找某个数据应该插入的位置,时间复杂度也是 ○(㏒n)
和红黑树优点:复杂度一样,跳表实现简单、维护方便
Redis的持久化有三种方式
RDB:在指定的时间间隔内将内存中的数据集快照写入磁盘
AOF:以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录
一致性hash
一致性hash是为了避免hash因子增加之后的hash不一致问题。
假设有一个redis集群作为缓存,一致性hash就是先假设一个0-2^32的圆环,这个redis集群先hash到圆环.上面,然后再将key值hash到圆环上面,此时, key值的hash通过顺时针或者逆时针的顺序去放到最近的圆环的redis单机上面。这样当redis单机数量增加或者减少的时候都是影响的一小部分缓存key值
缓存穿透,缓存击穿,缓存雪崩
缓存穿透:key对应的数据在数据源根本不存在,每次从缓存都获取不到,请求都会到数据源
采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力
若为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短
bitmap:https://www.cnblogs.com/chjxbt/p/10615304.html
缓存击穿:key对应的数据存在,但在redis中过期
使用mutex:在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX(只有不存在的时候才设置,可以利用它来实现锁的效果)或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法
缓存雪崩:缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力
将系统中key的缓存失效时间均匀地错开,防止统一时间点有大量的key对应的缓存失效。
比如我们可以在原有的失效时间基础上增加一个随机值
采用LRU策略处理溢出
过期策略
Redis 的过期策略就是指当 Redis 中缓存的 Key 过期了,Redis 如何处理
定时过期:每个设置过期时间的 Key 创建定时器,到过期时间立即清除。内存友好,CPU 不友好
惰性过期:访问 Key 时判断是否过期,过期则清除。CPU 友好,内存不友好
定期过期:隔一定时间,expires 字典中扫描一定数量的 Key,清除其中已过期的 Key。内存和 CPU 资源达到最优的平衡效果