概述
Redis是速度非常快的非关系型内存数据库,Key-value型,key只能为String,value可以为五种不同类型的值
为什么redis速度非常快
- redis是内存操作
- 单线程单进程模型,IO多路复用(基于reactor模式)
数据类型
- String : 字符串、整数、浮点,可以自增
- List:列表,从两端压入或者弹出、对单个或多个元素进行操作,只保留一个范围的元素
- set:无序集合 添加、获取、移除单个元素,检查元素是否存在,交集、并集、差集,随机获取元素
- hash:包含键值对的无序散列表
- Zset:有序集合 根据分值范围获取元素,计算一个键的排名
- bitmap:位图
- hyperloglog:不精确的去重计数
- geospatial:地理位置计算
- pub/sub 发布订阅
- lua脚本
为什么选用跳跃表作为有序集合的实现
跳跃表为有序集合底层实现之一,跳跃表是基于多指针的有序连表实现的
与平衡二叉树相比,跳跃表具有插入速度快,因为不需要进行旋转来维护平衡性,更容易实现,支持无所操作
使用场景
- 计数器,String进行自增或自减运算
- 缓存,热点数据放到内存中,设置内存最大使用了及淘汰策略保证缓存的命中率
- 查找表,DNS记录
- 消息队列,List双向连表,Lpush写入和rpop读取
- 会话缓存,分布式session
- 分布式锁,SETNX命令,具有原子操作
- set,集合有交集并集差集等操作,共同好友等操作
- zset ,有序集合 排行榜
- bitmap,统计功能
持久化
因为是内存数据库,保证服务重启后数据不丢失,将数据持久化到硬盘
RDB持久化
将某个时间点的所有数据都放在硬盘上
可以将快照复制到其他的服务器从而创建具有相同数据的服务器副本
如果发生系统故障,会丢失最后一次创建快照之后的数据
如果数据量很大,保存快照的时间会很长
AOF持久化
将写命令添加到AOF文件的末尾
使用AOF持久化需要设置同步选项,从而确保写命令同步到磁盘的时机,先储存到缓冲区
- always 每个写命令都同步(严重降低服务器性能)
- everysec 每秒同步一次(即使系统崩溃也只会丢失一秒的数据)
- no 让操作系统决定何时同步(不会给性能带来提升,也会增加数据丢失)
AOF重写,去除AOF文件中冗余写命令
高可用
- 单副本
-
- 单机应用
- 多副本
-
- 主从结构
- Sentinel(哨兵)
-
- 主要解决主从模式下高可用自动切换问题
- slave作为备份节点不提供服务
- 不能解决读写分离问题
- Cluster(集群)
-
- 一致性hash算法,hash槽(分片),虚拟节点
- 由多个节点组成,每个节点中只有一个master和最少一个salve,只有master提供写操作,salve/master都可以提供读服务,数据通过异步的主备复制机制保证。每个节点中的数据是分片的 只有一组master/savle中的数据是相同的
- 保证集群中有2*n+1个master节点即可实现故障转移高可用,常见为3主3从。
- 客户端向集群任意节点发送命令,接收到命令的节点会根据CRC16进行hash然后与16383取余计算出这个key对应的solt和节点,如果这个槽在当前节点则继续执行命令,并返回结果,如果不在当前节点,则发送moved重定向异常(包含目标节点信息),客户端从moved异常中获取目标节点信息,向其发送命令,并获取结果
- ASK重定向与MOVED重定向的区别,moved:槽不在当前节点中,ASK槽还在迁移中
- jedis等都提供了smart客户端,即将槽与节点的映射关系缓存到本地,即可避免每次的重定向请求。
常见问题
缓存穿透
缓存中大量不存在的数据进入db请求导致db压力剧增
解决:布隆过滤器:如果过滤器中不存在,那么数据一定不存在,如果过滤器中存在,实际数据也可能不存在,bitmap实现
缓存击穿
某个热点数据失效,大量针对这个数据的请求会穿透到数据库
解决:针对多个热点数据在过期时间上加上随机数,避免同一时刻失效
缓存雪崩
缓存挂掉,所有请求都会穿透到数据库
解决:快速失败的熔断,减少db压力,保证缓存服务高可用
缓存删除策略
noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错。(没人用)
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key(这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。)
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。
LRU策略,回收最近最少使用的key,java linkedhashMap可以简单实现LRU算法。