Redis学习笔记(五)完结

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis的发布与订阅Redis的主从复制Redis的缓存穿透和雪崩

十四、Redis发布订阅

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

Redis 客户端可以订阅任意数量的频道。

发布与订阅相关命令

  1. 订阅
subscribe channel [channel ...]

订阅一个或多个频道,返回订阅的频道数

  1. 发布
publish channel message

向一个频道发布消息,返回向多少个客户端发送了消息

  1. 按照模式订阅
psubscribe pattern [pattern ...]

如果 publish 命令发送的频道和订阅的模式成功匹配,那么客户端就会接收到发布的消息

  1. 查看频道
pubsub channels [pattern]

返回有客户端订阅的频道,是用 pattern 参数,只返回与模式匹配的有客户端订阅的频道

  1. 查看频道的订阅数
pubsub numsub [channel ...]

返回频道和其订阅数

  1. 查看模式的订阅数
pubsub numpat

实现原理:

Redis将订阅关系保存在一个字典里面(Redis的字典是用哈希表实现的,基本可以认为字典就是哈希表),其中键是字符串类型就是频道名,值是一个链表存着所有订阅了该频道的客户端指针。

图1

如上图所示,有三个频道:aaa,bbb和ccc。其中client-1订阅了全部三个频道,client-2订阅了 aaa 和 bbb,client-3订阅了 aaa。其中客户端指针指向客户端状态结构,这个结构中保存着套接字描述符以及各种其他信息,Redis可以通过此结构提供的信息向客户端发送数据。

  1. 频道订阅
    当订阅一个频道的时候,只需要将客户端指针添加到该频道对应的链表的末尾即可,如果该频道还不是字典的键,那么创建键和对应的链表,并将客户端指针添加到链表末尾。比如,client-3 执行了 subscribe ccc ,那么字典结构将如下图所示:

图二

  1. 退订频道
    当一个客户端使用 unsubscribe 命令时,就会将客户端指针从要退订的频道对应的列表中删除。比如,在图二基础上,client-3 执行 unsubscribe ccc,那么字典结构将由图二变为图一。如果将客户端指针删去后,频道对应的列表为空了,那么也会将频道删去。比如,在图一基础上,client-1 执行unsubscribe ccc,那么字典结构将如下图所示:

图三

  1. 查看频道
    当执行pubsub channels时,只需要遍历字典的所有键,将其返回即可。如果给了模式参数,只需要将与模式匹配的键返回即可。如果对图一结构的字典,使用pubsub channels命令,返回值将是:
1) "aaa"
2) "bbb"
3) "ccc"
  1. 查看频道的订阅数
    当执行pubsub numsub [channel ...]命令时,只需要返回频道对应的列表的长度即可,如果键值不存在返回0即可,如果对图一结构的字典,使用pubsub numsub aaa,返回值将是:
1) "aaa"
2) (integer) 3
  1. 订阅模式
    订阅模式和客户端的关系存在一个链表中,每一个节点都会保存模式字符串和客户端指针,链表结构如下图所示:

图四

如果此时 client-4 执行psubscribe c*,那么链表结构将如下图所示:

图五

  1. 模式退订
    当执行punsubscribe pattern命令时,就会将链表中模式和客户端指针都对应相等的节点删去,比如,client-3 执行punsubscribe b*,那么链表结构将如下图所示:

图六b

  1. 发布信息
    在发布信息的时候,需要分别将信息发送给订阅频道的客户端和订阅模式的客户端。
  • 在将信息发送给订阅频道的客户端时,只需要在字典中找到该频道对应的链表,然后将信息发送给链表中的所有客户端。
  • 在将信息发送给订阅模式的客户端时,需要遍历整个保存模式订阅的链表,如果发布的频道和模式匹配,则将其发送给这个客户端
    以图一和图五为例,如果执行命令publish bbb message,那么首先在字典中找到频道 bbb 对应的客户端链表,将其发送给 client-1,client-2,然后遍历模式链表,发现之后模式 b* 匹配,那么再将信息发送给 client-3。
测试

订阅端:

# 订阅
127.0.0.1:6379> SUBSCRIBE chenhao        # 订阅一个频道 chenhao
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "chenhao"
3) (integer) 1
# 等待读取推送的信息
1) "message"    # 消息
2) "chenhao"    # 频道名称
3) "Hey,hello!"    # 消息的内容

发布端:

# 发布
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> PUBLISH chenhao Hey,hello!  # 发布者发布消息到频道!
(integer) 1
127.0.0.1:6379> 

十五、Redis主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。

默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从复制的作用

主从复制的作用主要包括:

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  4. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

环境配置

只配置从库,不用配置主库

127.0.0.1:6379> info replication    # 查看当前库的信息
# Replication
role:master    # 角色 master
connected_slaves:0    # 连接的从机
master_failover_state:no-failover
master_replid:ed2c33065f3151b8e44586c757acd211a7d5fefc
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

复制3个配置文件,修改对应的信息

1、端口号

2、pid 的名字

3、log文件名

4、dump.rdb 名字

修改完毕之后,启动我们的3个redis服务可以通过进程信息查看

一主二从

默认情况下,每台Redis服务器都是主节点;我们一般情况下只用配置从机就好了!

一主(79)二从(80,81)

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379        # SLAVEOF 127.0.0.1 6379  找谁当自己的老大
OK
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_read_repl_offset:14
slave_repl_offset:14
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:8e1feea7c3142c373b9aba16869225bfd0046f2c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14


# 在主机中查看!
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1        # 多了从机的配置
slave0:ip=127.0.0.1,port=6380,state=online,offset=266,lag=0        # 多了从机的配置
master_failover_state:no-failover
master_replid:8e1feea7c3142c373b9aba16869225bfd0046f2c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:266
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:266
127.0.0.1:6379> 

如果两个都配置完成,就是有两个从机了

细节

主机可以写,从机不能写只能读!

主机写:

从机不能写只能读取内容:

复制原理

Slave 启动成功连接到master后会发送一个sync同步命令

Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。

  1. 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
  2. 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步

但是只要是从新连接master,一次完全同步(全量复制)将被自动执行!我们的数据一定可以在从机中看到

  • 如果主机断开了连接我们可以使用SLAVEOF no one让自己变成主机。其他的节点就可以手动连接到最新的这个主节点!

哨兵模式

(自动选举老大模式)

概述

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务器不可用。这不是一种推荐的方式,更时候,我们优先考虑哨兵模式。

谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据票数自动将从库转为主库。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵命令,哨兵是一个独立进程,作为进程,他会独立运行,其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

测试

我们目前的状态是 一主二从!

1、配置哨兵配置文件sentinel.conf

# sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1

后面的数字1,代表主机挂了,slave投票看让谁接替成为主机,票数多的,就会成为主机!

2、启动哨兵!

[root@iZbp17w9kj059pgbpavd3qZ bin]# redis-sentinel cconfig/sentinel.conf 
18493:X 02 Mar 2022 08:37:06.335 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
18493:X 02 Mar 2022 08:37:06.335 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=18493, just started
18493:X 02 Mar 2022 08:37:06.335 # Configuration loaded
18493:X 02 Mar 2022 08:37:06.336 * Increased maximum number of open files to 10032 (it was originally set to 1024).
18493:X 02 Mar 2022 08:37:06.336 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.6 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 18493
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           https://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

18493:X 02 Mar 2022 08:37:06.336 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
18493:X 02 Mar 2022 08:37:06.343 # Sentinel ID is c468f0be30188a4e81a9756804ca03d10a2d6d04
18493:X 02 Mar 2022 08:37:06.343 # +monitor master myredis 127.0.0.1 6379 quorum 1
18493:X 02 Mar 2022 08:37:06.344 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
18493:X 02 Mar 2022 08:37:06.349 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
哨兵模式

优点:

1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全都有!

2、主从可以切换,故障可以转移,系统的可用性就会更好

3、哨兵模式就是主从复制的升级,手动到自动,更加健壮。

缺点:

1、redis不好在线扩容,集群容量一旦达到上限,在线扩容非常麻烦,

2、实现哨兵模式的配置是十分麻烦的,里面有很多选择!

哨兵模式的全部配置
# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
 
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
 
 
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
 
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
# 这个数字越小,完成failover所需的时间就越长,
# 但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
# 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
 
 
 
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。  
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
 
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
#这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
#一个是事件的类型,
#一个是事件的描述。
#如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
  sentinel notification-script mymaster /var/redis/notify.sh
 
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。 
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

十六、Redis缓存穿透和雪崩(面试高频,工作常用)

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。

另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。

缓存穿透

概念

缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(秒杀!),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

解决方案

布隆过滤器:

布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;

但是这种方法会存在两个问题:

1.如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键。

2.即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

缓存击穿(量太大,缓存过期)

概述

这里需要注意和缓存穿透的区别,缓存击穿,是指一个key非常热点,在不停 的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大。

解决方案

1.设置热点数据永不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。

2.加互斥锁

分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

缓存雪崩

概念

缓存雪崩,是指在某一个时间段,缓存集中过期失效,Redis宕机!

产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会到达存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方案

redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。(异地多活)

限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

数据预热
数据预热的含义是,在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4月前
|
存储 NoSQL Linux
小白带你学习linux的Redis基础(三十二)
小白带你学习linux的Redis基础(三十二)
74 0
|
3月前
|
存储 NoSQL Redis
redis源码学习
redis源码学习
|
4月前
|
存储 NoSQL Ubuntu
在Ubuntu上安装Redis并学习使用get、set和keys命令
在Ubuntu上安装Redis并学习使用get、set和keys命令
|
5月前
|
缓存 NoSQL Redis
【Redis 系列】redis 学习十六,redis 字典(map) 及其核心编码结构
【Redis 系列】redis 学习十六,redis 字典(map) 及其核心编码结构
|
5月前
|
NoSQL 算法 Redis
【Redis 系列】redis 学习十四,sorted_set 初步探究梳理
【Redis 系列】redis 学习十四,sorted_set 初步探究梳理
|
5月前
|
存储 NoSQL Redis
【Redis 系列】redis 学习十五,redis sds数据结构和底层设计原理
【Redis 系列】redis 学习十五,redis sds数据结构和底层设计原理
|
5月前
|
负载均衡 NoSQL Redis
【Redis 系列】redis 学习十,Redis 集群搭建和主从复制
【Redis 系列】redis 学习十,Redis 集群搭建和主从复制
|
3月前
|
NoSQL 中间件 API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)(下)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
82 2
|
3月前
|
NoSQL Java API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)(上)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
73 0
|
5月前
|
存储 NoSQL 算法
[Redis 系列]redis 学习 17,redis 存储结构原理 1
[Redis 系列]redis 学习 17,redis 存储结构原理 1

热门文章

最新文章