Redis教程

Redis-进阶

本文主要是介绍Redis-进阶,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、Redis配置文件简介

includes (导入)
network 网络配置
日志文件
数据库 16 个
持久化
redis密码
限制 client

最大客户端
最大内存容量
内存达到最大上限的策略

二、缓存失效策略 淘汰策略

内存达到上线的处理策略

1.volatile-lru(least recently used):从设置了过期时间的键中选择空闲时间最长的键值对清除掉;

2.volatile-lfu(least frequently used):从设置了过期时间的键中选择某段时间之内使用频次最小的键值对清除掉;

3.volatile-ttl:从设置了过期时间的键中选择过期时间最早的键值对清除;

4.volatile-random:从设置了过期时间的键中,随机选择键进行清除;

5.allkeys-lru:从所有的键中选择空转时间最长的键值对清除;

6.allkeys-lfu:从所有的键中选择某段时间之内使用频次最少的键值对清除;

7.allkeys-random:所有的键中,随机选择键进行删除;

8.noeviction:不做任何的清理工作,在redis的内存超过限制之后,所有的写入操作都会返回错误;但是读操作都能正常的进行;

三、哨兵模式

​ 在主从模式的Redis系统中,从数据库在整个系统中起到了数据 冗余备份和 读写分离的作用,但是当数据库遇到异常中断服务后,我们只能通过手动的方式选择一个从数据库来升格为主数据库,显然这种方式很麻烦需要人工介入,这时通过哨兵模式可以实现自动化的系统监控和故障恢复。

1.什么是哨兵以及作用

哨兵的作用是监控Redis系统的运行状态,功能包括以下两个:

序号功能
1监控主数据库和从数据库是否正常运行
2主数据库出现故障时自动将从数据库转换为主数据库(选举)

哨兵是一个独立的进程,使用哨兵的典型结构图如下:

在这里插入图片描述

在一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统的问题。

在这里插入图片描述

2.哨兵模式配置

修改src目录下的sentinel.conf文件.

哨兵模式只需要配置其监控的主数据库即可,哨兵会自动发现所有复制该数据库的从数据库。

sentinel monitor mymaster 127.0.0.1 6380 1
# sentinel monitor master-name ip redis-port quorum
命令用例说明
master-namemymaster要监控的主数据库的名称,可以自定义, 组成大小写字母,数字和". - _"组成
ip127.0.0.1主数据库(master)的地址
redis-port6379主数据库的端口
quorum1最低通过的票数

启动哨兵—>结束主机试一下—>6379变成主机—>我们发现,主机再次上来他也能只当从机

3.一个哨兵监控多个Redis主从系统
配置

sentinel monitor mymaster 127.0.0.1 6380 2
sentinel monitor othermaster 192.168.88.60 6380 4

监控不同数据库使用不同的配置参数:

sentinel down-after-millisenconds mymaster 60000
sentinel down-after-millisenconds othermaster 10000

哨兵实现原理

哨兵启动后会与要监控的主数据库建立两条连接

和主数据库连接建立完成后,哨兵会使用连接2发送如下命令:

  1. 每10秒钟哨兵会向主数据库和从数据库发送INFO 命令

  2. 每2秒钟哨兵会向主数据库和从数据的_sentinel_:hello频道发送自己的消息。

  3. 每1秒钟哨兵会向主数据、从数据库和其他哨兵节点发送PING命令。

    ​ 首先,发送INFO命令会返回当前数据库的相关信息(运行id,从数据库信息等)从而实现新节点的自动发现,前面提到的配置哨兵时只需要监控Redis主数据库即可,因为哨兵可以借助INFO命令来获取所有的从数据库信息(slave),进而和这两个从数据库分别建立两个连接。在此之后哨兵会每个10秒钟向已知的主从数据库发送INFO命令来获取信息更新并进行相应的操作。
      接下来哨兵向主从数据库的_sentinel_:hello 频道发送信息来与同样监控该数据库的哨兵分享自己的信息。发送信息内容为:

<哨兵的地址>,<哨兵的端口>,<哨兵的运行ID>,<哨兵的配置版本>,
<主数据库的名字>,<主数据库的地址>,<主数据库的端口>,<主数据库的配置版本>

在这里插入图片描述

​ 哨兵通过监听的_sentinel_:hello频道接收到其他哨兵发送的消息后会判断哨兵是不是新发现的哨兵,如果是则将其加入已发现的哨兵列表中并创建一个到其的连接(哨兵与哨兵只会创建用来发送PING命令的连接,不会创建订阅频道的连接)。

​ 实现了自定发现从数据库和其他哨兵节点后,哨兵要做的就是定时监控这些数据和节点运行情况,每隔一定时间向这些节点发送PING命令来监控。间隔时间和down-after-milliseconds选项有关,down-after-milliseconds的值小于1秒时,哨兵会每隔down-after-milliseconds指定的时间发送一次PING命令,当down-after-milliseconds的值大于1秒时,哨兵会每隔1秒发送一次PING命令。例如:

// 每隔1秒发送一次PING命令
sentinel down-after-milliseconds mymaster 60000
// 每隔600毫秒发送一次PING命令
sentinel down-after-milliseconds othermaster 600

主观下线

当超过down-after-milliseconds指定时间后,如果被PING的数据库或节点仍然未回复,则哨兵认为其主观下线(subjectively down),主观下线表示从当前的哨兵进程看来,该节点已经下线。

客观下线

在主观下线后,如果该节点是主数据库,则哨兵会进一步判断是否需要对其进行故障恢复,哨兵发送SENTINEL is-master-down-by-addr 命令询问其他哨兵节点以了解他们是否也认为该主数据库主观下线,如果达到指定数量时,哨兵会认为其客观下线(objectively down),并选举领头的哨兵节点对主从系统发起故障恢复。这个指定数量就是前面配置的 quorum参数。

sentinel monitor mymaster 127.0.0.1 6380 2

选举领头哨兵

当前哨兵虽然发现了主数据客观下线,需要故障恢复,但故障恢复需要由领头哨兵来完成。这样来保证同一时间只有一个哨兵来执行故障恢复,选举领头哨兵的过程使用了Raft算法,具体过程如下:

1.发现主数据库客观下线的哨兵节点(A)向每个哨兵节点发送命令,要求对象选择自己成为领头哨兵

2.如果目标哨兵节点没有选过其他人,则会同样将A设置为领头哨兵
3.如果A发现有超过半数且超过quorum参数值的哨兵节点同样选择自己成为领头哨兵,则A成功成为领头哨兵
4.当有多个哨兵节点同时参选领头哨兵,则会出现没有任何节点当选的可能,此时每个参选节点将等待一个随机事 件重新发起参选请求进行下一轮选举,直到选举成功。

故障恢复

1.选出领头哨兵后,领头哨兵将会开始对主数据库进行故障恢复。步骤如下

首先领头哨兵将从停止服务的主数据库的从数据库中挑选一个来充当新的主数据库。

序号挑选依据
1所有在线的从数据库中,选择优先级最高的从数据库。优先级通过replica-priority参数设置
2优先级相同,则复制的命令偏移量越大(复制越完整)越优先
3如果以上都一样,则选择运行ID较小的从数据库

​ 2.选出一个从数据库后,领头哨兵将向从数据库发送SLAVEOF NO ONE命令使其升格为主数据库,而后领头哨兵向其他从数据库发送 SLAVEOF命令来使其成为新主数据库的从数据库,最后一步则是更新内部的记录,将已经停止服务的旧的主数据库更新为新的主数据库的从数据库,使得当其恢复服务时自动以从数据库的身份继续服务

四、redis的缓存击穿、穿透和雪崩

服务的高可用问题

在生产环境中,会因为很多的原因造成访问请求绕过了缓存,都需要访问数据库持久层,虽然对Redsi缓存服务器不会造成影响,但是数据库的负载就会增大,使缓存的作用降低

缓存穿透(查不到)

缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓存保护后端持久的意义

演示

解决方案:

  1. 缓存空对象
    • 缓存空对象:是指在持久层没有命中的情况下,对key进行set (key,null)
  • 缓存空对象会有两个问题:第一,value为null 不代表不占用内存空间,空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间,比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。第二,缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象
  1. 布隆过滤器拦截
    • 在访问缓存层和存储层之前,将存在的key用布隆过滤器提前保存起来,做第一层拦截,当收到一个对key请求时先用布隆过滤器验证是key否存在,如果存在在进入缓存层、存储层。可以使用bitmap做布隆过滤器。这种方法适用于数据命中不高、数据相对固定、实时性低的应用场景,代码维护较为复杂,但是缓存空间占用少。
    • 布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

布隆过滤器

布隆过滤器,英文叫BloomFilter,可以说是一个二进制向量和一系列随机映射函数实现。 可以用于检索一个元素是否在一个集合中。

布隆过滤器:一种数据结构,是由一串很长的二进制向量组成,可以将其看成一个二进制数组。既然是二进制,那么里面存放的不是0,就是1,但是初始默认值都是0。

安装:

# 默认redis已经安装好了 文章redis是安装在/usr/local/redis下的
# 进入到redis目录
cd /usr/local/redis
# 创建module文件夹
mkdir module && cd module
# 如果没有安装git 可以安装一下git
yum -y install git
# 下载并且解压
wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
tar -zxvf v1.1.1.tar.gz 
#成功后可看到目录下有个.so文件
cd RedisBloom-1.1.1
make
# 编译后会得到一个redisbloom.so的文件 然后在redis.conf增加如下配置
loadmodule ../module/RedisBloom-1.1.1/rebloom.so
# 重启redis
pkill redis
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf

2.3 布隆过滤器的基本命令

bf.reserve(key, error_rate, capacity)
● key 键
● error_rate 错误比例,该比例越小,则需要的空间越大
● capacity 容量,当实际容量超过这个数的时候,误判率会增加

bf.add(key, value)
● key 键
● value 值

bf.exists(key, value)
● key 键
● value 值

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

微博

系统中存在以下两个问题时需要引起注意:

  • 当前key是一个热点key(例如一个秒杀活动),并发量非常大。
  • 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的SQL、多次IO、多个依赖等。

在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。

解决方案:

1. 分布式互斥锁

只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可。

2. 永不过期

  • 从缓存层面来看,确实没有设置过期时间,所以不会出现热点key过期后产生的问题,也就是“物理”不过期。
  • 从功能层面来看,为每个value设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去更新缓

2种方案对比:

分布式互斥锁:这种方案思路比较简单,但是存在一定的隐患,如果在查询数据库 + 和 重建缓存(key失效后进行了大量的计算)时间过长,也可能会存在死锁和线程池阻塞的风险,高并发情景下吞吐量会大大降低!但是这种方法能够较好地降低后端存储负载,并在一致性上做得比较好。

“永远不过期”:这种方案由于没有设置真正的过期时间,实际上已经不存在热点key产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。

缓存雪崩

什么是应用服务雪崩

雪崩问题

分布式系统都存在这样一个问题,由于网络的不稳定性,决定了任何一个服务的可用性都不是 100% 的。当网络不稳定的时候,作为服务的提供者,自身可能会被拖死,导致服务调用者阻塞,最终可能引发雪崩连锁效应。

缓存雪崩

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力,造成数据库后端故障,从而引起应用服务器雪崩。

双11:停掉一些服务,保证主要的服务可用(服务降级)

雪崩效应产生的几种场景

  • 流量激增:比如异常流量、用户重试导致系统负载升高;
  • 缓存刷新:假设A为client端,B为Server端,假设A系统请求都流向B系统,请求超出了B系统的承载能力,就会造成B系统崩溃;
  • 程序有Bug:代码循环调用的逻辑问题,资源未释放引起的内存泄漏等问题;
  • 硬件故障:比如宕机,机房断电,光纤被挖断等。
  • 数据库严重瓶颈,比如:长事务、sql超时等。
  • 线程同步等待:系统间经常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。如果一个核心业务线程调用非核心线程,这个非核心线程交由第三方系统完成,当第三方系统本身出现问题,导致核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的,最终这条线程将断掉,也可能引发雪崩;

五、Redis管道

5.1redis的管道

使用管道技术的原因

redis是一个客户端-服务器(CS)模型和请求/响应协议的TCP服务器,使用和http类似的请求响应协议。一个client可以通过一个socket连接发起多个请求命令。每个请求命令发出后client通常会阻塞并等待redis服务处理,redis处理完请求命令后会将结果通过响应报文返回给client。所以,如果一个业务逻辑中需要多次发送redis操作时,每一条命令在网络传输中的往返时延(计算机网络了解一下)会远远大于执行时间,这也是为什么说影响redis性能的最大难题是网络时延。
而管道(pipeline)可以一次性发送多条命令并在执行完后一次性将结果返回,pipeline通过减少客户端与redis的通信次数来实现降低往返延时时间,而且Pipeline 实现的原理是队列,而队列的原理是时先进先出,这样就保证数据的顺序性。
单个执行与管道执行对比图

在这里插入图片描述

Redis管道的特点

redis自带的命令行是没有管道技术的,只存在于各个客户端语音中,因为管道作用主要是,客户端在远程操作redis时,减少发送多个命令造成的网络时延TTL。

通过pipeline方式当有大批量的操作时候。我们可以节省很多原来浪费在网络延迟的时间。但是,需要注意到用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并是不是打包的命令越多越好。此外,pipeline期间将“独占”当前redis连接,此期间将不能进行非“管道”类型的其他操作,直到该pipeline关闭。

​ pipeline不是原子性的,中间可能会存在部分失败的情况,也就是说不能保证每条命令都能执行成功,如果中间有命令出现错误,redis不会中断执行,而是直接执行下一条命令,然后将所有命令的执行结果(执行成果或者执行失败)放到列表中统一返回,如果需要每条命令都执行成功,我们在批量执行过程中需要监控执行数量和返回的成功数量是否一致。

pipeline管道使用场景

​ Pipeline在某些场景下非常有用,比如有多个command需要被及时提交,而且他们对相应结果没有互相依赖,对结果响应也无需立即获得,那么pipeline就可以充当这种批处理的工具;

这篇关于Redis-进阶的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!