当前位置: 首页 > news >正文

Redis进阶知识

Redis

  • 1.事务
  • 2. 主从复制
    • 2.1 如何启动多个Redis服务器
    • 2.2 监控主从节点的状态
    • 2.3 断开主从复制关系
    • 2.4 额外注意
    • 2.5拓扑结构
    • 2.6 复制过程
      • 2.6.1 数据同步
  • 3.哨兵
    • 选举原理
    • 注意事项
  • 4.集群
    • 4.1 数据分片算法
    • 4.2 故障检测
  • 5. 缓存
    • 5.1 缓存问题
  • 6. 分布式锁

1.事务

Redis的事务只能保持命令是连续执行的,仅此而已。

  • MULTI:开启一个事务,执行成功返回OK
  • EXEC:真正执行事务
  • DISCARD:放弃当前事务。此时直接清空事务队列。之前的操作都不会真正执行到。
  • WATCH:开启事务时,如果对watch的事务进行修改,就会记录当前key的"版本号"。在真正提交事务的时候,如果发现当前服务器上的key版本号已经超过了事务开始时的版本号,就会让事务执行失败(事务中所有的操作都不执行)。
  • UNWATCH:取消对key的监控

客户端1事务先不执行呢:

127.0.0.1:6379> watch k1 # 开始监控 k1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 100 # 进⾏修改, 从服务器获取 k1 的版本号是 0. 记录 k1 的
版本号. (还没真修改呢, 版本号不变)
QUEUED
127.0.0.1:6379> set k2 1000
QUEUED

客户端2然后修改k1:

127.0.0.1:6379> set k1 200 # 修改成功, 使服务器端的 k1 的版本号 0 -> 1
OK

客户端1再执行,发现命令被撤回了。

127.0.0.1:6379> EXEC # 真正执⾏修改操作, 此时对⽐版本发现, 客⼾端的 k1 的版
本号是 0, 服务器上的版本号是 1, 版本不⼀致! 说明有其他客⼾端在事务中间修改了 k1 !!!
(nil)
127.0.0.1:6379> get k1
"200"
127.0.0.1:6379> get k2
(nil)

2. 主从复制

节点:每台redis-server主机就是一个节点
主节点:可读可写的节点。
从节点:只能读数据的节点。
主从复制:将主节点的数据复制到从节点,使从节点和主节点保持一致。

  • 可用性:一个节点挂了,其他节点可以顶上。
  • 性能提升:对于读操作可以平均分摊到多个机器上。

2.1 如何启动多个Redis服务器

正常来说,每个redis服务器程序,应该在一个单独的主机上。学习阶段可以部署到一台机器上,但是得要求端口不一样。

  • 配置文件在/etc/redis/redis.conf,复制出来修改port,和daemonize yes
  • 启动 redis-server [新配置文件路径] ,下图中就把另外两个redis启动起来了
    在这里插入图片描述
  • 设置主从结构,修改配置文件加上主从结构配置。在末尾加上 slave [主节点ip] [主节点port]
    在这里插入图片描述
  • 再次启动查看端口号。
    在这里插入图片描述
    最终达到的效果:主节点这边数据产生任何修改,从节点就能立即感知到。

2.2 监控主从节点的状态

info replication

主节点
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=100,lag=0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:100
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:100
从节点
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:170
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:170
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:170

2.3 断开主从复制关系

slaveof no one 来断开主节点复制关系
1)断开与主节点复制关系。
2)从节点晋升为主节点。

slaveof 新主机 新port 认另一个服务作为主节点
1)断开与旧主节点复制关系。
2)与新主节点建⽴复制关系。
3)删除从节点当前所有数据。
4)从新主节点进⾏复制操作。

以上方法仅仅是临时性的,但是当我们重启之后,还会以配置文件为准。

2.4 额外注意

安全性:主节点可以使用密码来保证自己服务器的安全性
只读:对于从节点进行修改,主节点是感知不到的。
传输延迟:
在这里插入图片描述

2.5拓扑结构

  • 一主一从
    写请求较多时,主节点负载较大。此时可以将主节点的AOF关闭,从节点开启AOF。当主机宕机的时候,需要先从从节点获取AOF文件再重启。
    在这里插入图片描述

  • 一主多从
    实际开发中,读请求够多,可以选择一主多从,但是同步时,由于从节点过多,增加带宽,同样对于主节点是一个压力。
    优点:同步速度快
    在这里插入图片描述

  • 树形主从
    解决了主节点同步时带宽过高的场景,因为从节点也可以同步它的子节点。
    缺点:同步的延时变长。
    在这里插入图片描述

2.6 复制过程

在这里插入图片描述

2.6.1 数据同步

Redis使用psync完成主从数据同步,同步过程分为:全量复制和部分复制。

  • 全量复制:一般用于初次复制场景,当数据量大时,可能会对主从节点和网络造成很大开销。
  • 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连接上主节点后,如果条件允许,主节点会补发数据给从节点。
主节点
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=100,lag=0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06      主节点ID
master_replid2:0000000000000000000000000000000000000000    备用ID 
master_repl_offset:100
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:100
从节点
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:170
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2fbd35a8b8401b22eb92ff49ad5e42250b3e7a06
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:170
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:170

PSYNC语法格式:

PSYNC replicationed offset
replicationed=?,offset=-1,表示全量复制
具体的值就是尝试进行部分复制

  1. replicationid/replid(复制ID)
    主节点的复制id,主节点重新启动,或者从节点晋升为主节点,都会生成一个replicationid。(同一个节点每次重启,replicationid都会变化)
    从节点和主节点连接后,就会获取到主节点的replicationid。

  2. offset(偏移量)
    主节点每进行一次写操作会记录一下偏移量,统计在master_repl_offset中。并且也会记录从节点的复制偏移量,保存在slave0中。
    从节也会记录自己的偏移量在slave_repl_offset
    因此可以通过对比主从节点的偏移量,来看数据是否一致。
    在这里插入图片描述

psync 运⾏流程
在这里插入图片描述
步骤:1.从节点发送psync命令给主节点,replied和offset的默认值是?和-1。
2.主节点根据psync参数和自身数据情况决定响应结果 +FULLRESYNC replid offset,从节点需要进行全量;+CONTINEU,部分复制;
-ERR,说明 Redis 主节点版本过低,不⽀持 psync 命令。

全量复制
在这里插入图片描述

1)从节点发送 psync 命令给主节点进⾏数据同步,由于是第⼀次进⾏复制,从节点没有主节点的运
⾏ ID 和复制偏移量,所以发送 psync ? -1。
2)主节点根据命令,解析出要进⾏全量复制,回复 +FULLRESYNC 响应。
3)从节点接收主节点的运⾏信息进⾏保存。
4)主节点执⾏ bgsave 进⾏ RDB ⽂件的持久化。
5)从节点发送 RDB ⽂件给从节点,从节点保存 RDB 数据到本地硬盘。
6)主节点将从⽣成 RDB 到接收完成期间执⾏的写命令,写⼊缓冲区中,等从节点保存完 RDB ⽂件
后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照 rdb 的⼆进制格式追加写⼊到收
到的 rdb ⽂件中. 保持主从⼀致性。
7)从节点清空⾃⾝原有旧数据。
8)从节点加载 RDB ⽂件得到与主节点⼀致的数据。
9)如果从节点加载 RDB 完成之后,并且开启了 AOF 持久化功能,它会进⾏ bgrewrite 操作,得到最
近的 AOF ⽂件。

生成RDB文件在发送比较麻烦:
在这里插入图片描述
即使是省去了读写硬盘的开销,但是此操作的大头是网络传输,这个没法省,所以开销仍然很大。

部分复制
在这里插入图片描述
1)当主从节点之间出现⽹络中断时,如果超过 repl-timeout 时间,主节点会认为从节点故障并终端
复制连接。
2)主从连接中断期间主节点依然响应命令,但这些复制命令都因⽹络中断⽆法及时发送给从节点,所
以暂时将这些命令滞留在复制积压缓冲区中。
3)当主从节点⽹络恢复后,从节点再次连上主节点。
4)从节点将之前保存的 replicationId 和 复制偏移量作为 psync 的参数发送给主节点,请求进⾏部分
复制。
5)主节点接到 psync 请求后,进⾏必要的验证。随后根据 offset 去复制积压缓冲区查找合适的数据,
并响应 +CONTINUE 给从节点。
6)主节点将需要从节点同步的数据发送给从节点,最终完成⼀致性。

复制积压缓冲区
复制积压缓冲区是保存在主节点上的⼀个固定⻓度的队列,默认⼤⼩为 1MB,当主节点有连接的从节
点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写⼊
复制积压缓冲区,如图所⽰。
在这里插入图片描述
由于缓冲区本质上是先进先出的定⻓队列,所以能实现保存最近已复制数据的功能,⽤于部分复制和复制命令丢失的数据补救。复制缓冲区相关统计信息可以通过主节点的 info replication 中:
在这里插入图片描述
在这里插入图片描述

实时复制:
主从节点在建⽴复制连接后,主节点会把⾃⼰收到的 修改操作 , 通过 tcp ⻓连接的⽅式, 源源不断的传输给从节点. 从节点就会根据这些请求来同时修改⾃⾝的数据. 从⽽保持和主节点数据的⼀致性.

另外, 这样的⻓连接, 需要通过⼼跳包的⽅式来维护连接状态. (这⾥的⼼跳是指应⽤层⾃⼰实现的⼼跳,
⽽不是 TCP ⾃带的⼼跳).
1)主从节点彼此都有⼼跳检测机制,各⾃模拟成对⽅的客⼾端进⾏通信。
2)主节点默认每隔 10 秒对从节点发送 ping 命令,判断从节点的存活性和连接状态。
3)从节点默认每隔 1 秒向主节点发送 replconf ack {offset} 命令,给主节点上报⾃⾝当前的复制偏移
量。
如果主节点发现从节点通信延迟超过 repl-timeout 配置的值(默认 60 秒),则判定从节点下线,断
开复制客⼾端连接。从节点恢复连接后,⼼跳机制继续进⾏。

3.哨兵

在这里插入图片描述
基本流程是:

  1. 哨兵监控
  2. 哨兵发现某个节点挂了,会协商选出一个leader
  3. leader去完成后续故障转移工作。从节点中选一个作为新的主节点;让其他从节点同步新主节点;通知应用层转移到新主节点上;
    在这里插入图片描述

选举原理

  1. 主观下线,这个哨兵通过心跳包,判定redis是否正常工作。
  2. 客观下线,多个哨兵都认为这个主节点挂了,(达到了法定票数)
  3. 让多个哨兵节点选一个leader
  4. leader选完后,leader就需要选一个从节点,作为新的主节点。
    优先级:每个redis数据节点,都会在配置文件中有一个优先级,slave-priority优先级大的胜出
    offset:offset越大说明同步数据的进度越大。
    run id:每个redis节点启动的时候随机生成一串数字。
  5. 选出之后,leader就会控制这个节点,执行slave no one,成为master。再控制其他节点,执行slave of,换主结点即可。

注意事项

  • 哨兵节点不能只有⼀个. 否则哨兵节点挂了也会影响系统可⽤性.
  • 哨兵节点最好是奇数个. ⽅便选举 leader, 得票更容易超过半数.
  • 哨兵节点不负责存储数据. 仍然是 redis 主从节点负责存储.
  • 哨兵 + 主从复制解决的问题是 “提⾼可⽤性”, 不能解决 “数据极端情况下写丢失” 的问题.
  • 哨兵 + 主从复制不能提⾼数据的存储容量. 当我们需要存的数据接近或者超过机器的物理内存, 这样的结构就难以胜任了.

4.集群

Redis 的集群就是引⼊多组 Master / Slave , 每⼀组 Master / Slave 存储数据全集的⼀部分, 从⽽构成⼀个更⼤的整体, 称为 Redis 集群 (Cluster).
在这里插入图片描述

4.1 数据分片算法

  • 哈希求余
    优点:简单高效、数据分配均匀
    缺点:一旦需要扩容,N改变了,原有的规则映射被破坏,就需要重新排列数据,开销大。

  • 一致性哈希算法
    第⼀步, 把 0 -> 2^32-1 这个数据空间, 映射到⼀个圆环上. 数据按照顺时针⽅向增⻓.
    第⼆步, 假设当前存在三个分⽚, 就把分⽚放到圆环的某个位置上.
    第三步, 假定有⼀个 key, 计算得到 hash 值 H, 规则很简单, 就是从 H 所在位置, 顺时针往下找, 找到的第⼀个分⽚, 即为该 key 所从属的分⽚.
    当增加分片时,只有相邻的分片才会进行搬运,大大减少了搬运规模
    优点: ⼤⼤降低了扩容时数据搬运的规模, 提⾼了扩容操作的效率.
    缺点: 数据分配不均匀 (有的多有的少, 数据倾斜).

  • 哈希槽分区算法 (Redis 使⽤) (解决搬运成本高,数据分配不均匀)
    hash_slot = crc16(key) % 16384 [0-16383]个槽位
    在这里插入图片描述
    现在需要扩容一个分片,只需要从0、1、2号分片中适当拿出一点,就可以凑够了。
    分片的规则很灵活:每个分片持有的槽位不一定连续。每个节点使用位图来表示自己持有哪些槽位。16384个槽位,需要2KB大小的内存空间表示。

问题1:Redis集群最多有16384个分片吗?
不对,每个分片的槽位应该不超过1000。
有的槽位可能有多个key,有的槽位可能没有key。因为多个key的哈希可能映射到同一个槽位。
• 节点之间通过⼼跳包通信. ⼼跳包中包含了该节点持有哪些 slots. 这个是使⽤位图这样的数据结构
表⽰的. 表⽰ 16384 (16k) 个 slots, 需要的位图⼤⼩是 2KB. 如果给定的 slots 数更多了, ⽐如 65536
个了, 此时就需要消耗更多的空间, 8 KB 位图表⽰了. 8 KB, 对于内存来说不算什么, 但是在频繁的⽹
络⼼跳包中, 还是⼀个不⼩的开销的.
• 另⼀⽅⾯, Redis 集群⼀般不建议超过 1000 个分⽚. 所以 16k 对于最⼤ 1000 个分⽚来说是⾜够⽤
的, 同时也会使对应的槽位配置位图体积不⾄于很⼤.

4.2 故障检测

1)故障判定
集群中的所有节点,都会周期性的使用心跳包进行通信。

  1. 节点A给节点B发送ping包,B就会给A返回一个pong包。ping喝pong除了message type不一样外,其它都一样。都包含了集群中的配置信息(该节点的id,该节点从属于哪个分片,是主节点还是从节点,从属于谁,持有哪些slots图)。
  2. 每个节点,每秒钟,都会给一些随机的节点发起ping包,而不是全发一遍。
  3. 当节点A给节点B发起ping包,B不能如期回应的时候,此时A就会尝试重置和B的TCP连接,看能否连接成功。如果仍然连接失败,A就会把B设置为PFALL状态。
  4. A判定B为PFALL之后,就会通过redis内置的Gossip协议,和其他节点进行沟通,向其他节点确认B的状态。
  5. 此时A发现其他很多节点,也认为B为PFALL,并且数目超过总集群个数的一半,那么A就会把B标记成FAIL(客观下线),并且这个消息同步给其他节点。

2)故障迁移(把从节点提拔成主节点)(选举过程:Raft算法)
上面的例子,B故障,A会把B FAIL的消息告知集群中的其他节点

  • 如果B是从节点,那么不需要进行故障迁移。
  • 如果B是主节点,那么就会由B的从节点(比如C和D)触发故障迁移。
    流程如下:
  1. 从节点判定自己是否具有参选资格。如果从节点和主节点已经太久没通信(数据差距太大),时间超过阈值,就是去竞选资格
  2. 具有资格的从节点,比如C和D,会先休眠一定时间,休眠时间=500ms挤出时间+[0, 500ms]随机时间+排名*1000ms。offset值越大,排名越靠考前。
  3. 比如C的休眠时间到了,C就会给其他所有集群中的节点,进行拉票操作。但是只有主节点才有投票资格。
  4. 主节点会把自己的票投给C,当C收到的票数超过主节点数目的一半,C就会晋升成主节点。
    10.同时C还会把自己成为主节点的消息,同步给其他集群的节点。

5. 缓存

最重要的是热key,热key如何生成呢?
1)定期生成
每隔一段时间,会访问的数据频次进行统计。挑选出访问频次最高的前N%数据。
优点:实现起来比较简单,过程更可控,方便排查问题。
缺点:实时性不够,如果出现一些突发事件,有一些不是热词的内容,成了热词了。新的热词就可能给后面的数据库带来较大的压力。

2)实时生成
先给缓存设定容量上限(可以通过 Redis 配置⽂件的 maxmemory 参数设定).

  • 如果redis查到了直接返回。
  • redis中不存在,就从数据库查,把查到的结果同时写入redis。
  • 如果缓存已经满了(达到上限), 就触发缓存淘汰策略, 把⼀些 “相对不那么热⻔” 的数据淘汰掉.

淘汰策略:
FIFO (First In First Out) 先进先出
把缓存中存在时间最久的 (也就是先来的数据) 淘汰掉.

LRU (Least Recently Used) 淘汰最久未使⽤的
记录每个 key 的最近访问时间. 把最近访问时间最⽼的 key 淘汰掉.

LFU (Least Frequently Used) 淘汰访问次数最少的
记录每个 key 最近⼀段时间的访问次数. 把访问次数最少的淘汰掉.

Random 随机淘汰
从所有的 key 中抽取幸运⼉被随机淘汰掉.

Redis内置淘汰策略如下:
在这里插入图片描述

5.1 缓存问题

  1. 缓存预热(Cache preheating)
    通过统计的方式把热点数据导入到redis中,通过实时生成,达到缓存预热的工作。

  2. 缓存穿透Cache penetration
    查询的某个key,在redis中没有,mysql也没有。这个key也不会更新到key中。
    原因:业务设计不合理,非法的key也被进行查询了;数据库的数据不小心被删除了;黑客恶意攻击。
    解决办法:对参数进行严格校验,是否符合查询的格式;针对数据库上不存在的key,也存到Redis中,比如value就设置”“;使用布隆过滤器先判断key是否真存在,再真正查询。

  3. 缓存雪崩Cache avalanche
    短时间内大量的key再缓存上失效,导致数据库压力骤增。
    原因:Redis挂了;Redis大量的key同时过期。
    如何解决:部署高可用的集群,并完善监控报警体系;不给key设置过期事件或者设置过期事件的时候添加随机事件因子。

  4. 缓存击穿(Cache breakdown)
    原因:缓存雪崩的特殊情况,针对热key突然过期了,导致大量的请求直接打到数据库上,引起数据库宕机。
    如何解决:基于统计方式发现热key,设置永不过期;进行必要的服务降级,利用访问数据库时使用分布式锁,限制同时请求数据库的并发数。

6. 分布式锁

分布式锁是一种或一组单独的服务器程序,给其他服务器提供加锁服务。
AB进程在访问Mysql时,在之间设置一个锁Redis服务器。
A进行买票时,会往Redis设置一个键值对,B想设置时,发现该键值对已经设置过了,就加不了锁了。A操作之后,就将该键值对删除。
问题1:若A没有执行完挂掉了,谁来释放(删除)锁呢?
解决:设置key的过期时间,来防止A挂掉。
问题2:有没有可能B去解锁呢?
解决:首先给服务器编号,加锁时,value对应着服务器的编号。后续解锁时候,就可以进行校验了。(就可能避免B可能会误解锁了)

问题3:解锁是两步的,先查询判定,在del。有可能A服务器里有两个线程会导致重复del。在这两个线程del之间,有B加锁了。因此需要保证操作为原子性。
解决:使用lua写一些逻辑,存到服务器上。 客户端可以调用lua这个API来保证操作原子性。

问题4:过期时间续约问题?设置多少合适呢?
专门负责续约的线程,每次快过期的时候再重新设置时间,叫做”看门狗“(watch dog)。这个是加锁的服务器上的。

问题5:RedLock?(少数服从多数)
在这里插入图片描述
加锁的时候, 按照⼀定的顺序, 写多个 master 节点. 在写锁的时候需要设定操作的 “超时时间”. ⽐如
50ms. 即如果 setnx 操作超过了 50ms 还没有成功, 就视为加锁失败.
如果给某个节点加锁失败, 就⽴即再尝试下⼀个节点.
当加锁成功的节点数超过总节点数的⼀半, 才视为加锁成功.
同理, 释放锁的时候, 也需要把所有节点都进⾏解锁操作. (即使是之前超时的节点, 也要尝试解锁, 尽量保
证逻辑严密).
在这里插入图片描述

相关文章:

  • 服务端高并发分布式结构演进之路
  • 51单片机,两路倒计时,LCD1602 ,Proteus仿真
  • ubuntu的虚拟机上的网络图标没有了
  • Go语言中函数 vs 方法
  • STM32项目实战:ADC采集
  • Gartner《如何将生成式人工智能(GenAI)集成到应用架构》学习心得
  • elementplus menu 设置 activeindex
  • 探索用户行为数据分析——从基础查询到高级分析 【GaussDB(for MySQL)】
  • DeepSeek本地部署全攻略:从零搭建到Web可视化及数据训练
  • Java程序员学AI(一)
  • Linux(2)——shell原理及Linux中的权限
  • GLPK(GNU线性规划工具包)中建模语言MathProg的使用
  • MySQL 数据库备份与还原
  • Python训练营打卡 Day29
  • tomcat查看状态页及调优信息
  • 【数据结构】1-3 算法的时间复杂度
  • 掘金欧洲宠物经济新蓝海:比利时天然宠粮市场爆发与跨境新机遇
  • OpenSearch入门:从文档示例到查询实战
  • Linux `touch` 命令深度解析与高阶应用指南
  • 【Linux】第十七章 归档和传输文件
  • 电子凭证会计数据标准推广至全国
  • 海南乐城管理局原局长贾宁已赴省政协工作,曾从河南跨省任职
  • 武汉警方通报一起故意伤害案件:1人死亡,嫌疑人已被抓获
  • 证监会副主席李明:支持符合条件的外资机构申请新业务、设立新产品
  • 体育文化赋能国际交流,上海黄浦举办国际友人城市定向赛
  • 李洋谈美国黑帮电影与黑帮文化