目录
redis是一个高性能的基于key-value结构存储的nosql开源数据库,目前大部分公司都采用redis来实现分布式缓存,从而提高数据的检索效率。
redis几个特点
- 它是基于内存存储的,在进行数据IO操作时能达到非常高的QPS,,官方提供的指标是10W;
- 它提供了非常丰富的数据存储结构,如string、list、hash、set、zset等;
- redis底层采用单线程实现数据IO,所以在数据算法层面并不需要考虑并发安全性,从而导致它的底层算法时间复杂度基本上都是常量复杂度;
- 虽然redis是一个内存存储,但是它支持持久化,避免因为服务器故障导致数据丢失的问题;
基于这些特点 redis一把用来实现分布式缓存从而降低应用程序对关系型数据库检索带来的性能影响,除此之外,redis还可以实现分布式锁、分布式队列、排行榜、查找附近的人等功能,为复杂应用提供非常方便和成熟的解决方案。
持久化的两种方式
- AOF。以日志的形式记录操作语句,所以可能会有很多并不需要执行的语句存在,所以文件会比较大。在恢复时逐条执行。它有一个rewrite机制,就是当达到阈值时,会把日志中的过程化数据干掉,只保留最终的状态数据
- RDB。快照的方式,只在当前时间之前做备份,之后的数据会丢失,文件相对较小。
在4.X版本时提供混合持久化方式,就是在打开开关后,在某个时间点使用RDB方式进行备份,然后之后的数据按照AOF方式执行
缓存穿透
简单点说就是大量请求的key不存在于redis中,导致请求直接到了数据库,数据库短时间内无法承受压力。
解决方式:
- 做好参数校验。一些不合法的参数直接返回异常;
- 缓存无效key。如果缓存和数据库都没有找到这个key,则构造一个到redis中并设置一个比较短的过期时间。这个方式可能会使redis中存储大量无效的数据;
- 布隆过滤器。特点就是不存在的一定不存在,但是存在的不一定存在,所以可能会误判。把所有请求的key都放入布隆过滤器中,当用户请求过来,先判断请求的值是否在布隆过滤器中,若不存在则直接返回异常,存在则继续;
缓存击穿
缓存中没有,但是数据库有,导致数据库短时间承压。例如第一次访问。
解决方式:
- 设置长一点的过期时间,或者永不失效;
缓存雪崩
缓存在同一时间大面积失效,导致数据库短时间承压。
解决方式:
- 设置永不失效。或者手动清除;
- 设置随机失效时间;
- 若redis是集群的,则可将数据分布到多个节点上;
- 定时任务刷新缓存;
保持数据一致
- 先更新数据库,再更新redis。如果redis更新失败,可能不一致;
- 先删除redis缓存,再更新数据库,再次查询时再添加到缓存中。但是多线程情况下可能还是存在不一致的情况,例如线程一删除了缓存,但是还没有更新数据库,这个时候线程二又查询了数据,把旧数据添加到了redis中;
- 延时双删。先删除数据库缓存,再更新redis,然后延时一点时间再次删除缓存;
集群模式
主从模式
主库进行读写,并且会和从库进行数据同步。这种模式下客户端直接连接主库或者从库,但是当主库或者从库宕机后客户端需要自己手动修改ip,另外这种模式比较难扩容,整个集群中它能够存储的数据受到机器内存容量的限制,所以不适合特大数据量。
哨兵模式
在主动模式的基础上新增哨兵节点,当哨兵发现主库宕机后,自动选举出另一个库作为新的主库,哨兵也可以做集群,从而保证哨兵节点宕机后还有其他哨兵可以工作,所以这种模式比较能保证高可用,但是仍然无法解决redis容量上限的问题。
Cluster模式
支持多主多从,这种模式会按照key进行槽位的分配,可以使得不同的key分散到不同的主节点上,利用这种模式可以使得整个集群支持更大的数据容量,同时每个主节点也可以拥有自己的多个从节点,如果这个主节点宕机,会从其他的从节点选举出新的主节点
主从同步原理
同步过程:
- 从节点执行slaveof master IP PORT,保存主节点信息
- 从节点中的定时任务发现主节点信息,建立和主节点的socket连接
- 从节点发送信号,主节点返回,两边能相互通信
- 建立连接后,主节点将所有数据发送到从节点
- 主节点把当前数据同步给从节点后,完成复制过程。接下来,主节点会持续把写命令发送给从节点,保证数据一致性
几个概念:
- runid:唯一标识。每个redis启动后会生成一个uuid,每次重启都会发生变化
- offset:偏移量。主从节点各自维护自己的偏移量,当主节点有写入命令时,offset=offset+命令的字节长度。从节点收到主节点发送的命令,也会增加自己的offset,并把自己的offset发送给主节点,主节点同时保存自己的offset和从节点的offset,通过对比offset来判断主从节点的数据是否一致。
- repl_backlog_size:缓冲区。保存在主节点的一个固定长度的先进先出队列,默认1M。
原理:
从节点发送psync命令,psync {runid} {offset},因为时第一次发送,所以此时runid=?,offset=-1。
主节点收到后就知道了该节点是第一次同步,就会执行bgsave命令进行RDB的持久化,同时会返回 fullresync {runid} {offset}给从节点,其中runid是主节点的唯一标识,offset为目前主节点的偏移量,从节点收到后保存信息。主节点会将持久化文件发送给从节点,一直到从节点加载数据完成之前,所有主节点的写入命令会写入到缓冲区,同时更新主节点的offset。从节点收到文件后会清理掉本地数据然后加载主节点发送过来的RDB文件,如果开启了AOF则会重写AOF。之后就会从节点会进行增量同步。
增量同步时,从节点同样发送psync {runid} {offset}到主节点,主节点收到后会对比两个offset,如果offset在缓冲区之间,执行增量同步,如果主节点写入了太多数据而导致offset超出缓冲区范围则会再次进行全量同步,同样如果runid不同的话也会进行全量同步
过期策略
- 定时删除策略:为每个键设置一个定时任务,一旦到了过期时间则将键删除。这种方式对内存友好,但对CPU不够友好。
- 惰性删除策略。不管键有没有过期都不主动删除,等到每次去获取键的时候再判断是否过期,如果过期则删除,否则返回对应的值。这种方式对内存不够友好,但对CPU友好。
- 定期删除策略。系统每隔一段时间定期扫描一次已经设置了过期时间的键,发现过期的键则删除。