(redis面试题)(redis面试题分布式锁)

近期复习redis,整理了redis 复习大纲,分享给大家,私信回复获取xmind文件。

(redis面试题)(redis面试题分布式锁)
(redis面试题)(redis面试题分布式锁)
(redis面试题)(redis面试题分布式锁)

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

(0)
打赏微信扫一扫微信扫一扫QQ扫一扫QQ扫一扫
上一篇2022-12-22
下一篇2022-12-22

你可能还想知道

发表回复

登录后才能评论