1.为什么Redis能这么快
完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高
数据结构简单,对数据操作也简单
采用单线程,单线程也能处理高并发请求,想多核也可启动多实例
使用I/O复用模型,非阻塞IO
1.1 多路I/O复用模型
FD:文件描述符(一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射)
select可以同时监控多个文件描述符的可读可写情况。其他I/O多路复用函数:epoll(Linux)/kqueue(Mac)/evport/select
2.数据结构
String:最基本的数据类型,二进制安全
Hash:String元素组成的字典,适合用于存储对象
List:列表,按照String元素插入顺序排序(栈)
Set:String元素组成的无序集合,通过哈希表实现,不允许重复
Sorted Set:通过分数来为集合中的成员进行从小到大的排序
HyperLogLog:计数
Geo:存储地理位置信息
3.从海量key里查询出某一固定前缀的key
如果用keys pattern(keys k1*):keys指令一次性返回所有匹配的key;键的数量过大会使服务卡顿
SCAN cursor [MATCH pattern] [COUNT pattern]:
基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程
以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历
不保证每次执行都返回某个给定数量的元素,支持模糊查询
一次返回的数量不可控,只能是大概率符合count参数
>>> scan 0 match k1* count 10 (开始迭代,希望返回以k1为前缀的key,希望一次返回10个)
1)游标 xxxx
2)结果集
>>> scan xxxx match k1* count 10 (从xxxx开始继续迭代)
1)游标 xxxx
2)结果集
4.如何通过Redis实现分布式锁
set key value [EX seconds] [PX milliseconds] [NX|XX]
set设置超时时间、判断是否存在才操作->如果返回OK:执行业务,删除key,释放锁,结束。如果返回nil:结束。
注:如果大量的key同时过期,由于清除大量的key很耗时,会出现短暂的卡顿现象。
解决方法:在设置key的过期时间的时候,给每个key加上随机值
5.如何使用Redis做异步队列
使用List作为队列,RPUSH生产消息,LPOP消费消息;但是没有消息的时候,可能也会进行消费->应用层sleep进行LPOP重试
BLPOP key [key...] timeout 阻塞直到队列有消息或超时
缺点:只能提供一个消费者消费
pub/sub:主题订阅者模式
发送者(pub)发送消息,订阅者(sub)接收消息
订阅者可以订阅任意数量的频道(topic)
缺点:消息的发布是无状态的,无法保证可达
SUBSCRIBE myTopic:订阅myTopic的频道
PUBLISH myTopic aaa:向订阅了myTopic的频道发送'aaa'
6.Redis如何做持久化
6.1 RDB(快照)持久化:保存某个时间点的全量数据快照
配置:
redis.windows.conf中 save 900 1:在900秒内如果有一次写入操作,就做快照持久化
redis.windows.conf中 stop-writes-on-bgsave-error yes:当备份进程出错的时候,主进程就停止接受新的写入操作了。
redis.windows.conf中 rdbcompression yes:在备份的时候,需要将rdb文件压缩后保存
命令:
SAVE:阻塞Redis的服务器进程,直到RDB文件被创建完毕(可能会阻塞所有client请求,较少使用)
BGSAVE:Fork出一个子进程来创建RDB文件,不阻塞服务器进程
自动化触发RDB持久化的方式:
根据redis.windows.conf配置里的save m n定时触发(用的是BGSAVE)
主从复制时,主节点自动触发
执行Debug Reload
执行Shutdown且没有开启AOF持久化
BGSAVE原理:
执行BGSAVE命令,会先检查是否存在AOF/RDB的子进程正在执行,存在则返回错误。如果不存在会触发持久化,调用rdbSaveBackground方法,执行fork()系统调用(创建进程)
当进行写入操作,fork的时候,主进程会拿到资源的快照文件(副本),子进程把内存数据写入到rdb临时文件中,写入完成后,替换快照文件。
当redis启动时,如果检测到了rdb文件的存在,会自动载入这个文件
缺点:
内存数据的全量同步,数据量大会由于I/O而严重影响性能
可能会因为Redis挂掉而丢失从当前至最近一次快照期间的数据
6.2 AOF(Append-Only-File)持久化:保存写状态
记录下除了查询以外的所有变更数据库状态的指令
以append的形式追加保存到AOF文件中(增量)
配置:
redis.windows.conf中 appendonly no:不开启AOF
redis.windows.conf中 appendfilename "appendonly.aof":这就是aof文件
redis.windows.conf中 appendfsync everysec:只要缓存区发生变化,就会每隔一秒,将指令写入到aof文件中
为了解决AOF文件大小不断增大的问题,利用日志重写,原理如下:
调用fork(),创建一个子进程
子进程产生一条能生成所有原数据的新AOF指令,把新的AOF写到一个临时文件里,不依赖原来的AOF文件
主进程持续将新的变动同时写到内存和原来的AOF里
主进程获取子进程重写AOF的完成信号,往新的AOF同步增量变动
使用新的AOF文件替换掉旧的AOF文件
6.3 区别
RDB和AOF文件共存情况下的数据恢复流程:
redis启动时,会先判断是否存在AOF文件,如果存在就只加载AOF文件;如果不存在则判断是否存在RDB文件,如果存在就加载RDB文件。
RDB和AOF的优缺点:
RDB优点:全量数据快照,文件小,恢复快。
RDB缺点:无法保存最近一次快照之后的数据。
AOF优点:可读性高,适合保存增量数据,数据不易丢失。
AOF缺点:文件体积大,恢复时间长。
RDB-AOF混合持久化方式:
BGSAVE做镜像全量持久化,AOF做增量持久化
7.Pipeline的好处
Redis基于请求/相应模型,单个请求出来需要一一应答。(如果有大量指令,那么可能会堵塞)
Pipeline批量执行指令,节省多次IO往返时间
有顺序依赖的指令建议分批发送
8.Redis的同步机制
8.1 全量同步过程
Slave发送sync命令到Master
Master启动一个后台进程,将Redis中的数据快照保存到文件中(BGSAVE)
Master将保存数据快照期间接收到的写命令缓存起来(将增量数据缓存起来)
Master完成写文件操作后,将该文件发送给Slave
使用新的AOF文件替换掉旧的AOF文件
Master将这期间收集的增量写命令发送给Slave端
8.2 增量同步过程
Master接收到用户的操作指令,判断是否需要传播给Slave
将操作记录追加到AOF文件中
将操作传播到其他Slave:1、对齐主从库;2、往响应缓存写入指令
将缓存中的数据发送给Slave
9.Redis Sentinel(哨兵)
9.1 解决主从同步Master宕机后的主从切换问题
监控:检查主从服务器是否运行正常
提醒:通过API向管理员或者其他应用程序发送故障通知
自动故障迁移:主从切换(将一个Slave变成一个Master)
9.2 流言协议Gossip
每个节点都随机地与对方通信,最终所有节点的状态达成一致
种子节点定期随机向其他节点发送节点列表以及需要传播的消息
不保证信息一定会传递给所有节点,但是最终会趋于一致
10.Redis集群
如何从海量数据里快速找到所需?
分片:按照某种规则去划分数据,分散存储在多个服务器节点上(节点之间通过Gossip协议传播信息)
常规的按照哈希划分无法实现节点的动态增减
一致性哈希算法:对2^32取模,将哈希值空间组织成虚拟的圆环
写入数据的时候,会将数据进行Hash,然后选择顺时针离他最近的服务器存储。如果这个服务器宕机了,则继续顺时针选取服务器。
问题:如果节点较少时,可能会出现数据倾斜,即被缓存的对象大部分集中存储在某一台服务器上。
解决:引入虚拟节点均匀分布到环上(存储过程多了一步:虚拟节点到实际节点的映射)