近期复习redis,整理了redis 复习大纲,分享给大家,私信回复获取xmind文件。
1.基础数据结构
- string 字符串数组
动态字符串,类似于arrayList预分配空间,扩容时长度小于1M,每次扩容一倍,大于1M扩容1M,最大512M
字符串由多个字节组成,每个字节由8个bit组成,即字符串是多个bit的组合,这便是位图的数据结构
- list
相当于linkedList 双向链表,
底层结构:quickList,包含多个zipList,双向指针
lindex 查询某位置的元素 O(n)操作,慎用
ltrim 链表截取,可以用来实现定长链表
阻塞读 blpop/brpop
- hash
hash结构的存储结构内存消耗高于string,根据实际情况选择
hincrby 类似于 incrby
- set
无序的,唯一,相当于hashset
内部实现相当于特殊的字典,value都是null
- zset
有序的set,类似于java的sortedset和hashmap结合体
数据结构跳跃列表
最后一个value被移除,数据结构被自动清除,内存被回收
2.高级数据结构
- 位图
byte数组,最小单位是比特bit,每个bit只能是0或1
getbit setbit
bitcount 统计指定范围内1的个数
bitpos 指定范围出现的第一个0或者1
- HyperLogLog
提供不精确的去重计数方案, 统计UV,
pfcount 计数
pfadd 增加计数
采用稀疏矩阵存储,占据空间小,内存占用12k
- 布隆过滤器
可以理解为一个不精确的set,使用contains判断是不是存在
过滤掉已经存在的内容
bf.add 添加元素bf.madd多个添加
bf.exists查询是否存在bf.mexists
对已经见过的元素不会误判,判断不存在那一定不存在,判断存在可能不存在
布隆过滤器的initial_size越大,会浪费空间,越准确
- GeoHash
附近的人,存地理位置,相当于一个普通的 zset
增加 geoadd company 116.2 39.9 juejin
key 经度 维度 value
距离geodist
获取元素位置 geoposcompanyjuejin
附近的人georadiusbymember
根据坐标查询 georadius
建议Geo的数据使用单独的Redis实例部署,如果数据量过亿,需要对Geo数据进行拆分,按照国家省市区
3.redis限流
- 滑动窗口限流
zset ,score填当前时间,
通过score排序,判断数量,达到限流的效果
- 漏斗限流
限流模块Redis-Cell, cl.throttle命令
- 令牌桶限流限流
通过lua脚本,实现令牌桶限流算法
4.redis大key
- 大key现象
如果发现redis内存大起大落,很有可能是大key
客户端超时阻塞
单线程处理,操作大 key 时会比较耗时,引发网络阻塞
网络流量较大,带宽高
内存分布不均
集群模型在 slot 分片均匀情况下
- Scan
redis是一个大的hash表,scan扫描数组的槽位 (slot),limit代表遍历的槽位,有些槽位可能是空的,有些可能是多个
scan的遍历顺序:高位进位加法遍历
- 大key扫描
扫描命令,scan
redis-cli -h 127.0.0.1 -p 6379 --bigkeys (可能会使ops升高)
redis中的OPS 即operation per second 每秒操作次数。意味着每秒对Redis的持久化操作。
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.1 (每隔100条can命令。休眠0.1s)
RdbTools 工具查找大 key
- 大key删除
unlink 命令代替 del 来删除
大对象删除 unlink,不是所有的unlink都会异步处理,如果key占用内存很小就会立即回收,跟del一样
5.redis序列化协议
- RESP
直观的文本协议,优势在于实现过程简单,解析性能极好
6.redis 持久化
- 快照RDB
二进制序列化,存储上非常紧凑
快照机制,操作系统的cow机制--copy on write
fork 产生一个子进程,来序列化写到磁盘,不是子线程
- AOF日志
内存数据修改的指令记录文本,时间长会很大,重放慢
日志刷盘机制每隔一秒进行一次fsync(强制刷盘,从内核缓存刷到磁盘)
通常redis主节点不会进行持久化操作,redis 4.0 开始混合持久化
7.Redis 事务
- redis管道 pipeline
多次write到操作系统内核缓冲区,一次read,从缓冲区读
- Redis 事务
multi事务开始
exec事务提交
discard事务丢弃
redis 事务不具备原子性,仅仅是满足了事务的隔离性中的串行,不被其他事务打断,单线程
redis事务通常会结合pipeline一起使用,减少io
8.redis作为锁
- watch:乐观锁
先读后写场景,,通过watch先盯住一个或者多个变量,事务执行时会检查变量是否被动过,动过会返回null,客户端再重试
watch-》multi-》command-》exec
- 分布式锁
set nx ex
释放锁时使用lua脚本,防止释放调别的线程的锁
在sentinel集群模式下,由于主从同步的延时,主节点挂掉后,从节点加的锁没有及时同步到新的主节点,产生问题,采用Redlock算法
9.redis消息队列
- 消息多播,广播消息 pubsub
list只能单播,redis提供pubSub模块
PubSub的生产者传递过来一个消息,Redis会找到对应的消费者传递过去。如果一个消费者都没有,那么消息直接丢弃。
如果有多个消费者,一个消费者突然断开连接,生产者会继续发送消息,其他正常连接的消费者可以持续收到消息。
断开的消费者重新连接上,在断联期间生产者发送的消息,对这个消费者来说是彻底丢失了。
- Stream,支持多播的可持久化消息队列
每个Stream可以挂多个消费组,,一个消费组挂多个消费者
消费者中Pending Entries List用来存放已经被客户端读取,但是没有ack的消息,确保每个消息被消费一次,不会丢失消息
xadd追加消息
xdel从stream删除消息
xrange获取消息列表,自动过滤删除的消息
xlen消息长度
del删除stream
xgroup create创建消费组
xinfo stream 获取stream信息
xinfo groups获取消费组信息
10.redis内存回收
- 内存回收机制
操作系统是以页为单位来回收内存的,这个页上只要有一个key还在使用,那么他就不能被回收
如果执行flushdb会立即刷新,
redis无法保证立即回收已经删掉的key,但会重新使用尚未回收的空闲内存
- 内存分配算法
默认内存分配算法:jemalloc
11.Redis集群
- Redis 主从同步
1.增量同步
修改的指令记录在本地内存buffer,异步将buffer中的指令同步到从节点
从节点执行同步过来的指令,并向主节点返回执行到哪(偏移量)
redis的复制内存buffer是一个定长的环形数组,满了后会覆盖前面内容
如果出现网络问题可能出现无法同步,丢失指令的问题。
2.快照同步(非常耗资源)
1,主节点bgsave,内存数据全部快找到磁盘文件
2,快照文件全部传送到从节点
3,从节点清空当前内存数据,一次全量加载
4,加载完成通知主节点进行增量同步、
快照同步过程中,主节点的复制buffer还在移动,如果同步时间长或buffer太小,存在覆盖的风险,导致快照同步后无法完成增量同步,再次发起快照同步,可能产生死循环。
务必配置合适的buffer大小,避免快照同步死循环
无盘复制
非ssd磁盘,主节点内存--》从节点内存
- Sentinel (redis分布式)
sentinel无法保证消息完全不丢失,尽量保证消息少丢失
min-slaves-to-write 1表示至少有一个从节点在进行正常赋值,否则停止对外写服务,丧失可用性
min-salves-max-lag 10表示如果10S内没有收到从节点反馈,就意味着从节点同步不正常(异常复制)
- Codis (开源redis集群)
edis集群代理中间件
分片原理
1024个槽位
crc32进行hash,对1024取模
缺点,不支持事务、单个key不宜过大、网络有一定开销、部署zookeeper代价
优点,使用简单、后台界面可视化
- Redis Cluster(官方集群化方案)
16384个槽位
定位算法,crc16算法进行hash,对16384取模
key定位到错误槽位后会跳转,返回-moved指令
迁移工具:redis-trib
12.key过期策略
redis会将设置了过期时间的key放在独立的字典中
- 定时扫描
默认每秒扫描10次,扫描不会遍历过期字典中的所有key,采用简单的贪心策略
1.从过期字典中随机取出20个key
2.删除20个中已经过期的key
3.如果过期的比例超过1/4,则重复第1步
为保证过期扫描不会出现循环过度,导致线程卡死,算法还增加了扫描时间上限,默认不会超过25ms
- 惰性删除
访问这个key时做过期时间校验,过期了就删除掉
key过期时间不能全部在同一时期,一方面是防止缓存雪崩,另一方面减少因为redis扫描过期key和内存页回收导致的卡顿
从节点不会进行过期扫描,,主节点的del指令,可能会出现主从不一致的问题
13.内存满后淘汰策略 LRU
为了限制使用最大内存,redis提供配置参数maxmemory来限制内存超出期望大小,当实际内存超出maxmemory时,提供下面的策略maxmemory_policy:
noeviction不接受写服务,接收del和读请求,默认策略
volatile-lru在设置了过期时间的key中,最少使用的被淘汰,没有过期时间的不处理、
volatile-lfu
volatile-ttl在设置了过期时间的key中,比较key的过期时间,ttl 越小的优先被淘汰
volatile-random在设置了过期时间的key中,随机淘汰
allkeys-lru所有key中,按照lru算法淘汰
allkeys-lfu
allkeys-random所有key中随机
如果用到持久化功能,不要用allkeys
Redis是一种近似LRU算法
Redis是一种近似LRU算法,因为LRU算法内存消耗较大,改动较大
redis为实现近似LRU算法,给每个key加了一个额外字段,长度24bit,即最后一次被访问的时间。
懒惰处理,当写操作,发现内存超出maxmemory,就执行一次LRU
随机采样出5个key,淘汰掉最旧的key,如果还是超出,就继续采样,淘汰
maxmemory_samples 默认为5
14.Redis源码
- 字符串
redis中的字符串叫SDS,simple dynamic string
T capacity; // 数组容量
T len; // 实际长度
bytes flags; // 特殊标志位 无需关注
byte[] content; // 数组内容 实际存放的值
- hash字典
字典内部有两个hashtable,通常情况下只有一个hashtable有值,在扩容时一个存旧的,一个存新的。渐进式rehash进行扩容
触发扩容时机 hset、hdel和定时任务
字典数据结构精华就是hashtable。与hashmap类似,通过分桶解决hash冲突
- hash函数
redis的hash算法是siphash
hash算法的好坏体现在是否把key打散的比较均匀
- set
set 底层实现也是字典,只不过所有的value都是null
- 压缩列表
redis为节省空间,zset和hash在元素个数较少时,采用ziplist进行存储。
压缩列表是一块连续的内存空间,元素紧挨着,没有任何冗余空隙
- intset
set集合中元素都是整数且数量较少时,redis用intset存储
- quikclist
quikclist是ziplist和linkedlist的混合体,他将linkedlist按段分每一段是ziplist
默认单个ziplist长度为8kb,超出这个字节数,会另起一个ziplist
- zset (跳跃链表)
zset内部实现是一个hash字典加一个跳跃列表(skiplist)
redis的跳跃链表共有64层
15.redis运维
- info指令
1:Server服务器运行参数
2:Clients客户端信息
3:Memory内存统计数据
4:Persistence持久化信息
5:Stats通用统计数据
6:Replication主从复制相关信息
7:CPUCPU使用情况
8:Cluster集群信息
9:Keyspace键值对统计数量
- Redis安全
- 危险指令
keys,flushdb,flushall
redis配置文件中提供rename-command指令,用于修改危险指令
rename-command keys abckeysabc,需要输入abckeysabc
rename-commandflushall“” 完全封杀该条指令
- redis不支持ssl链接
如果在公网,可使用SSL代理,官方推荐使用spiped
spiped原理
spiped会在客户端和服务端个启动一个spiped进程,达到代理的作用
16.面试题
- Redis 为何快的本质原因
纯内存操作,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在 IO 上,所以读取速度快。
整个 Redis 就是一个全局 哈希表,他的时间复杂度是 O(1),而且为了防止哈希冲突导致链表过长,Redis 会执行 rehash 操作,扩充 哈希桶数量,减少哈希冲突。并且防止一次性 重新映射数据过大导致线程阻塞,采用 渐进式 rehash。巧妙的将一次性拷贝分摊到多次请求过程后总,避免阻塞。
Redis 使用的是非阻塞 IO:IO 多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,Redis 采用自己实现的事件分离器,效率比较高。
采用单线程模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
Redis 全程使用 hash 结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
根据实际存储的数据类型选择不同编码。
声明:我要去上班所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,版权归原作者马老司所有,原文出处。若您的权利被侵害,请联系删除。
本文标题:(redis面试题)(redis面试题分布式锁)
本文链接:https://www.51qsb.cn/article/m794o.html