前言
以下是针对redis的知识点整理,用于复习,主要以罗列为主,详细具体讲解可以参考书《Redis设计与实现》,你可以过一遍看看有无知识点遗漏。个人能力有限,如果你还有补充可以再下方评论指出,万分感谢。
基础知识点
数据类型
string (字符串)、list (列表)、set (集合)、hash (哈希) 和 zset (有序集合)
底层实现包括:SDS动态字符串,双向链表,数组加链表,渐进式hash,跳表redis是单线程
redis默认有16个数据库,默认先用0,可以使用select命令切换
过期策略
惰性删除:当这个key被访问到,但是已经过期,那就删除
定期删除:过一定时间,拿出一定的key判断,进行删除,如果超过一定数量,继续拿出一定的key进行判断删除,时间存在上限内存超限
当redis使用超过内存限制会根据策略来执行:
noeviction:不能put,但是可以del和get,默认是这个策略
volatile-lru:在有过期时间的key中,淘汰最少用的(redis是近似lru算法,会随机取几个淘汰最少用的)
volatile-ttl:在有过期时间的key中,淘汰过期时间最短的(剩下寿命最短的)
volatile-random:在有过期时间的key中,随机淘汰
allkeys-lru:所有key中,淘汰最少用的
allkeys-random:所有key中,随机淘汰持久化方式
RDB、AOF、混合
RDB:类似快照,将当前数据拍个照片保存,利用操作系统的 COW 机制来进行数据段页面的分离,进行对数据的复制,同时,新数据会在新的page上面
AOF:利用日志来完成记录,当逻辑处理完成记录日志,利用日志重放来恢复,会对aof进行重写瘦身,通过fsync来保证数据刷到磁盘上面,1s一次
混合:rdb存储,增量用AOF,重启后先读取rdb再重放aof通信协议
RESP直观的文本协议,利用一些特殊字符来确定当前的是什么语句redis事务
redis可以使用multi/exec/discard。multi 指示事务的开始,exec 指示事务的执行,discard 指示事务的丢弃。redis是不支持回滚的,只能把当前所有的执行丢弃,事务性能不高,使用管道优化,提供watch机制监听改变,但是针对改变只是知道,但是不予处理。keys和scan
通过这两个命令可以找到对应的键,keys会卡,scan不会但是慢一点,可能会重复。redis对内存优化
在存放大key或者数量量大列表时,会对存储结构进行压缩,ziplist
当删除多个键的时候内存不会马上被回收,因为操作系统的内存是按页来的,只有这个页上面的key全部删除才能回收,同时,新的key会利用删除后的空间Pipeline
网络交互中利用了内核的特性,客户发送消息时,只需要将消息写入本地缓存,就马上返回,不需要等待,后续操作由内核网关去异步发送,而读取返回消息时也是读取的本地相对应的读缓存,如果没有数据就需要等待网络返回数据从而从缓存中读取数据,这个时候是真正耗时的时候。
常用架构
单机
单个节点使用主从
一方面是从做备份,一方面从可以提供get服务
Redis同步的是指令流,增量同步,利用buffer进行缓冲,可能出现buffer充满的时候就需要进行快照复制,将主节点进行快照,然后直接发送给从节点,然后从节点删除所有数据,然后从节点进行加载,然后同步新的指令,速度慢。哨兵
利用哨兵去自动选举出主节点,同时出现异常自动重新选出主节点集群
codis:利用中间代理人去访问,将不同的槽位分配到相应的redis节点上面,client通过codis进行访问,codis可以配置多个,通过zk来同步槽位。
cluster:官方给出,去中心化,自动维护槽位分布。
应用场景
分布式锁
setnx(set if not exists)
一开始不支持设置过期时间,后来2.8更新后,命令为:set lock true ex 5 nx
但是超时是存在问题的,如果再时间限制过程中,没有执行完,锁自动释放了,但是实际还在执行,但是别的线程可以抢到锁去执行了。MQ
可以利用list来实现消息队列的操作,rpop lpush,redis的list是一个双端队列,同时也支持阻塞拿出消息,来支持队列为空的时候的问题
同时有PubSub支持订阅,但是少了很多功能如ack,不能保证数据一定成功发出,后面引入StreamHyperLogLog
用于模糊统计,可以用于统计网站的uv(单个用户访问只能算一次),利用矩阵。GeoHash
redis支持存储地理位置,并且进行附近统计和距离计算布隆过滤
rebloom用于最大化效率去重,会有误判,利用hash原理。实际适用于:爬虫系统过滤已经查过的url、垃圾邮件过滤、缓存击穿防御等,总之用于数据量大、要求速度快、可以忍受误判的情况。
问题解决
缓存击穿
多次查询那些一定不存在的数据,或者当前数据不在缓存的高并发查询,导致巨大流量涌入数据库。
解决方式:
- 缓存空对象,如果可以缓存空对象,将空对象作为null缓存起来,让缓存强制命中(提前缓存或者惰性缓存均可)。存在问题:当空对象多时,浪费了缓存的空间。
- 利用布隆过滤器缓存出现过的key,保证不在过滤器里面的key一定不存在,布隆过滤器节省很多空间
缓存雪崩
情况一:多数类似缓存同时过期,导致对这些key的查询同时落到数据库。
情况二:缓存服务器直接挂掉,导致所有请求全部落到数据库,导致后续雪崩。
这里的解决方式就需要视情况而定:
情况一的话,可以尝试设置缓存过期时间为随机值,不让同类型缓存同时过期。
情况二的话,首先优先保证架构上面能压住,尽可能保证有redis的备份节点可以恢复,当然也要做planB,万一全部缓存节点全部挂,最前面网关层面要要做到限流,后续服务需要做降级或熔断,这个时候就不是缓存的问题了,就是架构的问题了。