Redis拾遗
-
本文整理自剑指Java面试-Offer直通车
-
主流应用架构
-
-
Memcache和Redis的区别
-
Memcache
-
代码层次类似Hash
-
支持简单数据类型
-
不支持数据持久化存储
-
不支持主从
-
不支持分片
-
-
Redis
-
数据类型丰富
-
支持数据磁盘持久化存储
-
支持主从
-
支持分片(Redis3.0+)
-
-
-
为什么Redis能这么快
-
完全基于内存,绝大部分请求是纯粹的内存啊哦做,执行效率高
-
数据结构简单,对数据操作也简单
-
采用单线程,单线程也能处理高并发请求,想多核也可启动多实例
-
使用多路I/O复用模型,非阻塞IO
-
-
多路I/O复用模型
-
FD
-
File Descriptor,文件描述符
-
一个打开的文件通过为疑似的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射
-
-
传统的阻塞I/O模型
-
-
epoll/kqueue/evport/select
-
Redis根据不同的操作系统,选择不同的多路复用函数
-
优先选择时间复杂度为O(1)的I/O多路复用函数作为底层实现
-
以时间复杂度为O(n)的select作为保底
-
基于react设计模式监听I/O事件
-
Select系统调用
-
-
-
-
说说你用过的Redis的数据类型
-
String
-
最基本的数据类型
-
二进制安全
-
可以包含任何数据,比如JPG图片或者序列化的对象
-
-
最大512M
-

-
-
Hash
-
String元素组成的字典,适合用于存储对象
-
-
List
-
列表,按照String元素插入顺序排序
-
大约能存储40亿个成员
-
-
Set
-
String元素组成的无需集合,通过哈希表实现,不允许重复
-
-
Sorted Set
-
通过分数来为集合中的成员进行从小到大的排序
-
-
HyperLogLog
-
用于计数
-
-
Geo
-
支持存储地理位置信息
-
-
底层数据类型基础
-
简单动态字符串
-
链表
-
字典
-
跳跃表
-
整数集合
-
压缩列表
-
对象
-
-
-
从海量Key里查询出某一固定前缀的Key
-
KEYS pattern
-
查找所有复核给定模式pattern的key
-
一次性返回左右匹配的key
-
键的数量过大会导致服务器卡顿
-
-
SCAN cursor [MATCH pattern] [COUNT count]
-
基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程
-
以0作为游标开始一次新的迭代,知道命令返回游标0完成一次遍历
-
不保证每次执行都返回某个给定数量的元素(甚至会返回0个,只要游标不为0,就不应该结束迭代),支持模糊查询
-
一次返回的数量不可控,只能是大概率复核count参数
-
有可能会返回重复Key
-
-
-
如何通过Redis实现分布式锁
-
分布式锁需要解决的问题
-
互斥性
-
只能有一个客户端获得锁
-
-
安全性
-
锁只能被持有锁的客户端删除
-
-
死锁
-
获取锁的客户端未能释放锁,其他客户端再也无法获取该锁
-
-
容错
-
当部分Redis节点宕机时,客户端应依然能够获取锁
-
-
-
SETNX key value
-
如果key不存在,则创建并赋值
-
时间复杂度,O(1)
-
设置成功返回1,设置失败返回0
-
SETNX设置的key将长期存在,分开设置EXPIRE无法满足原子性
-
-
SET key value [EX seconds] [PX milliseconds] [NX|XX]
-
-
EX seconds,设置键的过期时间为seconds秒
-
PX millisecond,设置键的过期时间为milliseconds毫秒
-
NX,只在键不存在时,才对键进行设置操作
-
XX,只在键已存在时,才对键进行设置操作
-
SET操作完成时,返回OK,否则返回nil
-
-
-
大量的key同时过期的注意事项
-
集中过期,由于清除大量的key很耗时,会出现短暂的卡顿现象
-
解决方法,在key的过期时间上加上一个随机值
-
-
-
如何使用Redis做异步队列
-
使用List作为队列,RPUSH生产消息,LPOP消费消息
-
缺点,没有等待队列里有值就直接消费
-
弥补,可以通过在应用层引入Sleep机制去调用LPOP重试
-
-
BLPOP key [key...] timeout
-
阻塞直到队列有消息或者超时
-
缺点,只能供一个消费者消费
-
-
使用pub/sub,主题订阅者模式
-
发送者pub发送消息,订阅者sub接收消息
-
订阅者可以订阅任意数量的频道
-
subscribe 频道
-
publish 频道 消息
-

-
缺点,消息发布是无状态的,无法保证可达,若发送消息时,没有消费者在线,消息就丢失了
-
-
-
Redis如何做持久化
-
RDB(快照)持久化,保存某个时间点的全量数据快照
-
优点,全量数据快照,文件小,恢复快
-
缺点,无法保存最近一次快照之后的数据
-
SAVE命令
-
在主线程中保存快照,阻塞Redis的服务器进程,知道RDB文件被创建完毕
-
-
BGSAVE命令
-
Fork一个子进程来创建RDB文件,不阻塞服务器进程
-

-
-
LASTSAVE
-
返回上次执行save的时间
-
BGSAVE保存成功后,才会更新此时间
-
-
自动化出发RDB持久化
-
redis.conf
-
save 900 1
-
900秒内有1条写入指令,则产生一次快照
-
-
save 300 10
-
300秒内有10条写入指令,则产生一次快照
-
-
save 60 10000
-
60秒内有10000条写入指令,则产生一次快照
-
-
stop-writes-on-bgsave-error yes
-
当备份进程出错时,主进程停止接受写入操作
-
-
rdbcompression yes
-
在备份时,将rdb压缩后再保存,建议设置为no
-
-
save ""
-
禁用rdb配置
-
-
-
主从复制时,主节点自动触发
-
执行Debug Reload时
-
执行Shutdown且没有开启AOF持久化
-
-
缺点
-
内存数据的全量同步,数据量大会由于I/O而严重影响性能
-
可能会因为Redis挂掉而丢失从当前至最近一次快照期间的数据
-
-
-
AOF(Append-Only-File)持久化,保存写状态
-
优点,可读性高,适合保存增量数据,数据不易丢失
-
缺点,文件体积大,恢复时间长
-
-
记录下除了查询以外的所有变更数据库状态的指令
-
以append的形式追加保存到AOF文件中(增量)
-
redis.conf
-
appendonly yes
-
开启AOF
-
-
appendfilename "appendonly.aof"
-
AOF文件的名字
-
-
appendfsync always/everysec/no
-
立即写入/每秒写入/交由操作系统决定(一般等到缓存区填满才同步数据到磁盘中)
-
推荐everysec
-
-
-
日志重写解决AOF文件大小不断增大的问题
-
调用fork(),创建一个子进程
-
子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件
-
主进程持续将新的变动同时写到内存和原来的AOF里
-
主进程获取子进程重写AOF的完成信号,往新AOF同步增量变动
-
使用新的AOF文件替换掉旧的AOF文件
-
-
-
RDB-AOF混合持久化方式,4.0后默认
-
BGSZVE做镜像全量持久化,AOF做增量持久化
-

-
-
Redis数据的恢复
-
RDB和AOF文件共存情况下的恢复流程,AOF优先
-

-
-
-
使用Pipeline的好处
-
Pipeline和Linux的管道类似
-
Redis基于请求/响应模型,单个请求处理需要一一应答
-
Pipeline批量执行指令,节省多次IO往返时间
-
有顺序依赖的指令建议分批发送
-
-
Redis的同步机制
-
全同步过程
-
Slave发送sync命令到Master
-
Master启动一个后台进程,将Redis中的数据快照保存到文件中
-
Master将保存数据快照期间接收到的写命令缓存起来
-
Master完成写文件操作后,将该文件发送给Slave
-
使用新的AOF文件替换掉旧的AOF文件
-
Master将这期间收集的增量写命令发送给Slave端进行回放
-
-
增量同步过程
-
Master接收到用户的操作指令,判断是否需要传播到Slave
-
将操作记录追加到AOF文件
-
将操作传播到其他Slave
-
对齐主从库,确保从数据库是该操作所对应的数据库
-
将命令和参数按照Redis的格式写入到响应Slave的缓存中
-
-
将缓存中的数据发送给Slave
-
-
-
Redis Sentinel(Redis哨兵)
-
解决主从同步Master宕机后的主从切换问题
-
监控,检查主从服务器是否运行正常
-
提醒,通过API向管理员或其他应用程序发送故障通知
-
自动故障迁移,主从切换
-
-
-
流言协议Gossip
-
每个节点都随机地与对方通信,最终所有节点的状态达成一致
-
种子节点定期随机向其他节点发送街店列表以及需要传播的消息
-
不保证信息一定会传递给所有的节点,但是最终会趋于一致
-


