redis学习小结(面试常见知识点)

是什么

redis是一个key-value的nosql数据库 常用作持久化数据库的缓存 数据都在内存(也可持久化)

数据结构

以下的key都以name作为例子

string

常用命令

set name a

set name a ex 60

set name a px 60000

setnx name a  分布式锁

get name 

实现底层结构

简单动态字符串

list

常用命令

lpush name a b c

lpop name

rpush name a b c

rpop name 

lrange name 1 3

实现底层结构

quicklist(代替了双向链表和压缩列表)

hash

常用命令

hset name a 1

hgetall name

hget name a 

实现底层结构

哈希表和listpack(代替压缩列表)

set

常用命令

sadd name a b c d 

srem name a b c 

smembers name

实现底层结构

哈希表和整数集合

zset

常用命令

zadd name 500 a 1000 b

zrem name  a b

zrange name 1 100

底层实现结构

listpack和跳表

bitmap

常用命令

setbit name 1 0

getbit name 1

bitcount name 1 100

底层实现结构

二进制数组

stream常用命令

xadd name * group 11

xread streams name msgid

底层实现结构

消息队列

底层实现结构

简单动态字符串

  1. len 保存了SDS保存字符串的长度
  2. alloc 分配给字符数组的空间长度
  3. flags 用来表示不同类型的sds
  4. buf[] 数组用来保存字符串的每个元素

双向链表

quicklist

双向链表+压缩列表的组合

listpack

压缩列表

压缩列表(ziplist)是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

压缩列表的原理:压缩列表并不是对数据利用某种算法进行压缩,而是将数据按照一定规则编码在一块连续的内存区域,目的是节省内存

哈希表

解决哈希冲突 链地址法

 扩容和收缩  过程是渐进式rehash

什么叫渐进式 rehash?也就是说扩容和收缩操作不是一次性、集中式完成的,而是分多次、渐进式完成的。如果保存在Redis中的键值对只有几个几十个,那么 rehash 操作可以瞬间完成,但是如果键值对有几百万,几千万甚至几亿,那么要一次性的进行 rehash,势必会造成Redis一段时间内不能进行别的操作。所以Redis采用渐进式 rehash,这样在进行渐进式rehash期间,字典的删除查找更新等操作可能会在两个哈希表上进行,第一个哈希表没有找到,就会去第二个哈希表上进行查找。但是进行 增加操作,一定是在新的哈希表上进行的

整数数组

跳表

底层还是链表但是每个结点有多层

 链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点

1. 搜索:

从最高层的链表节点开始,如果比当前节点要大和比当前层的下一个节点要小,那么则往下找,也就是和当前层的下一层的节点的下一个节点进行比较,以此类推,一直找到最底层的最后一个节点,如果找到则返回,反之则返回空。

2. 插入:

首先确定插入的层数,有一种方法是假设抛一枚硬币,如果是正面就累加,直到遇见反面为止,最后记录正面的次数作为插入的层数。当确定插入的层数k后,则需要将新元素插入到从底层到k层。

3. 删除:

在各个层中找到包含指定值的节点,然后将节点从链表中删除即可,如果删除以后只剩下头尾两个节点,则删除这一层。

比平衡树的优势

redis架构

redis不支持事务回滚 

网络模型使用Io复用

 线程模型命令的执行单线程无锁

加上都是在内存操作 这也是 redis很快的原因

redis的事务不太好用加上也是单线程 建议都有lua脚本

网络io模型(指用户进程读取socket数据时的交互模型)

同步阻塞模型(单个socket)

 检测socket 如果没有数据则阻塞 直到当前的socket有数据才返回

同步非阻塞(单个socket)

检测socket 如果当前sokcet无数据就返回error 有数据则返回数据 

io多用复用(同步 可阻塞和非阻塞)(多个socket)(redis使用这个)

select poll epoll

select/poll

轮询访问多个socket

内核空间如果有数据了 则把所有的socket拷贝到用户空间 然后轮询一遍所有socket 找到数据

如果内核空间没有数据 则根据是否阻塞返回空或者阻塞

select受最大文件描述符影响1024 poll则没有

都是将fd(文件描述符)列表从用户空间拷贝到内核空间 内核空间检测到有socket有数据则给对应的socket标记 再从内核空间拷贝到用户空间 然后再遍历一遍检测有数据的sokcet  

epoll 

内核空间把有数据的socket放入到就绪队列 然后用户态下获取队列数据 

在内核空间申请内存 存入sokcet用红黑树保存 当有数据时会放入记录就绪事件队列 返回给用户空间

整个过程没有数据的时候根据是否阻塞 返回空或者阻塞 好处是少了数据的拷贝和sokcet的遍历

水平触发:在水平触发模式下,只要文件描述符对应的内核缓冲区中有数据可读,epoll_wait 就会持续将该文件描述符作为有事件发生的文件描述符返回。也就是说,即使应用程序在一次 epoll_wait 返回后只读取了部分数据,下次调用 epoll_wait 时,如果缓冲区中还有剩余数据,它依然会将该文件描述符标记为可读事件发生

边缘触发:在边缘触发模式下,只有当文件描述符的可读状态发生变化(例如从无数据可读变为有数据可读)时,epoll_wait 才会将该文件描述符作为有事件发生的文件描述符返回。如果应用程序在一次 epoll_wait 返回后没有一次性将缓冲区中的数据全部读取完,后续即使缓冲区中还有数据,epoll_wait 也不会再将该文件描述符标记为可读事件发生,直到有新的数据到达,缓冲区状态再次发生变化。

信号驱动io(同步)(单个socket)

内核空间有数据后 内核向用户进程发信号 用户进程再执行io的read取socket的数据

异步io

socket有数据后 直接拷贝数据到用户空间 再通知用户去读取数据 进程不需要操作io的read

持久化

redis持久化有两种 rdb和aof

RDB

rdb就是以生成快照的形式保存到硬盘

RDB的优点:

1、存储的快照是压缩格式,占用内存极小。

2、数据恢复的速度很快。

RDB的缺点:

1、不能实时

2、每次调用bgsave命令都会调用fork子进程,这是重量级操作,执行成本高。

AOF

每执行一次修改数据命令,都会将该命令追加到appendonly.aof文件中(先写入os cache,然后通过fsync刷盘)

redis的集群

主从复制 指将一台Redis服务器的数据,复制到其他的Redis服务器

全量复制、增量复制

全量复制 第一次同步的时候,复制全部的数据

在这里插入图片描述

增量复制

把主从库 网络断链 的时候,从库未收到的数据 同步给从库

在这里插入图片描述

1主从模式

可以一主多从

缺点是容灾不好 主机宕机了

脑裂问题

2哨兵模式

 Redis哨兵机制

在主从复制实现之后,如果想对master进行监控,Redis提供了一种哨兵机制,哨兵的含义就是监控Redis系统的运行状态,并做相应的响应。

 哨兵的功能

其主要的功能有以下两点:
(1)监控所有Redis节点是否正常运行;
(2)master故障后可以通过投票机制,从slave中选举出新的master,保证集群正常运行。
在一个一主多从的集群中,可以启用多个哨兵进行监控以保证集群足够稳健,这种情况下,哨兵不仅监控主从服务,哨兵之间也会相互监控,建议哨兵至少3个并且是奇数。

哨兵的任务

哨兵主要用于管理多个Redis服务器,主要有以下三个任务:
(1)监控:哨兵会不断的检测master和slave之间是否运行正常;
(2)提醒:当监控的某个Redis出现问题,哨兵可以通过API向管理员或其他应用程序发送通知;
(3)故障迁移:当一个master不能正常工作时,哨兵会开始一次自动故障迁移操作,它会将失效master的其中一个slave提升为master,并让失效master和其他slave该为复制新的master,当客户端试图连接失效的master时,集群也会向客户端返回新的master地址,使得集群可以使用新的master代替失效的master。

 Redis哨兵的工作原理

哨兵是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议来接收关于Master是否下线的信息,并使用投票协议来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。
每个哨兵会向其它哨兵、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂。若“哨兵群”中的多数sentinel都报告某一master没响应,系统才认为该master"彻底死亡",通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。
虽然哨兵释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵

缺点不好扩容

raft分布式一致性算法

Raft 算法的领导者选举机制具有以下过程:

初始状态:当一个 Raft 集群启动时,所有节点都处于 Follower 状态。每个节点都等待接收来自 Leader 的心跳消息。
Follower 成为 Candidate:如果一个 Follower 在一段时间内未收到来自 Leader 的心跳消息,它将成为 Candidate,并开始发起选举。
发起选举:一旦成为 Candidate,节点将增加自己的当前任期号(term),然后发送包含该任期号和候选人 ID 的请求给其他节点。每个节点只能投一次票,因此在一次选举中只会有一个 Candidate 获胜。
投票过程:每个节点在收到来自 Candidate 的选举请求后,会根据以下条件进行投票决策:
如果该节点已经投过票给了其他 Candidate,并且该 Candidate 的任期号更大,则拒绝投票。
如果该节点的日志更新较新,没有落后于 Candidate 的日志,则支持该 Candidate 并重置自己的选举超时计时器。
选举胜出条件:一个 Candidate 可以成为新的 Leader,需要满足以下条件之一:
收到了大多数节点的选票,并且得票数超过了一半。
在选举过程中,其他节点成为了 Leader(新的 Leader 后来者居上)。
成为 Leader:当一个 Candidate 赢得选举后,它将成为新的 Leader。它会发送心跳消息给所有节点来维持自己的领导地位,并准备接收客户端的操作请求。
通过这样的领导者选举机制,Raft 算法保证了集群中只存在一个 Leader,使得整个系统能够稳定运行。而选举过程中使用的随机化和选举超时机制,确保了快速选出新的 Leader,从而减少系统的不可用时间。

3cluster模式

主要是解决扩容的文件 

对redis进行分片

   Redis3.0加入了Redis的集群模式,实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的master节点上面,从而解决了海量数据的存储问题。

        Redis集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点进行操作,就像操作单一Redis实例一样,不需要任何代理中间件,当客户端操作的key没有分配到该node上时,Redis会返回转向指令,指向正确的node。

        Redis也内置了高可用机制,支持N个master节点,每个master节点都可以挂载多个slave节点,当master节点挂掉时,集群会提升它的某个slave节点作为新的master节点。

 如上图所示,Redis集群可以看成多个主从架构组合起来的,每一个主从架构可以看成一个节点(其中,只有master节点具有处理请求的能力,slave节点主要是用于节点的高可用)

Redis集群通过分布式存储的方式解决了单节点的海量数据存储的问题,对于分布式存储,需要考虑的重点就是如何将数据进行拆分到不同的Redis服务器上。常见的分区算法有hash算法、一致性hash算法

一致性hash算法:为每一个节点分配一个token,构成一个哈希环;查找时先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点。优点是在加入和删除节点时只影响相邻的两个节点,缺点是加减节点会造成部分数据无法命中,所以一般用于缓存,而且用于节点量大的情况下,扩容一般增加一倍节点保障数据负载均衡
 

 Redis集群采用的算法是哈希槽分区算法。Redis集群中有16384个哈希槽(槽的范围是 0 -16383,哈希槽),将不同的哈希槽分布在不同的Redis节点上面进行管理,也就是说每个Redis节点只负责一部分的哈希槽。在对数据进行操作的时候,集群会对使用CRC16算法对key进行计算并对16384取模(slot = CRC16(key)%16383),得到的结果就是 Key-Value 所放入的槽,通过这个值,去找到对应的槽所对应的Redis节点,然后直接到这个对应的节点上进行存取操作
 

redis与mysql一致性问题

redis和mysql数据保持一致的做法

1延迟双删   先删除缓存再把数据写入数据库过了一段时间(500ms)再删缓存

2使用canal开源 调用binlog来更新缓存

3使用消息队列kafka等

4直接修改缓存 缓存不过期 然后redis更新到数据库

5先更新数据库再删redis

但是删除redis可能失败 需要这样做

缓存穿透

不存在的数据 redis中找不到去找数据库

解决方法

1保存null值

2布隆过滤器 

缓存击穿

一个记录过期了 大量访问这个数据 直接访问数据库

解决方法

1设置缓存不过期

2加锁 只有一个线程可以访问数据库 当数据回写到缓存后 其他线程访问缓存

缓存雪崩

同时大量的缓存过期 导致大量请求访问数据库

解决方法

1随机设置缓存过期的时间

2降级或者熔断

redis过期策略

惰性过期  访问这个key再判断是否过期

定期过期 过一段时间扫描一部分Key 判断是否过期

定时过期

redis使用的是惰性过期+定期过去

redis内存淘汰策略

1.noeviction(默认策略):对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)

2.allkeys-lru:从所有key中使用LRU算法进行淘汰(LRU算法:即最近最少使用算法)

3.volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰

4.allkeys-random:从所有key中随机淘汰数据

5.volatile-random:从设置了过期时间的key中随机淘汰

6.volatile-ttl:在设置了过期时间的key中,淘汰过期时间剩余最短的

当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误

常见问题

大key

拆key

分片 

热数据和冷数据分开

热key

多级缓存

主从复制

分片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值