💎深入浅出带你玩转Redis7

基础篇

NoSQL及Redis介绍

在介绍Redis之前首先我们来谈谈NoSQL,这里的No并不是指“不”的意思,而是“Not Only”指的是非关系型数据库,我们从字面意思就能够看出来“不仅是SQL”,实际上NoSQL是来弥补关系型数据库在某些场景下不足。
区别于关系型数据库比如我们常见的MySQL、Oracle等,其中最明显的区别就是非关系型数据库不保证ACID特性。
那么在NoSQL中也分了几种类型的数据库:

分类

Examples举例

典型应用场景

数据模型

优点

缺点

键值(key-value)

Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB

内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。

Key 指向 Value 的键值对,通常用hash table来实现

查找速度快

数据无结构化,通常只被当作字符串或者二进制数据

列存储数据库

Cassandra, HBase, Riak

分布式的文件系统

以列簇式存储,将同一列数据存在一起

查找速度快,可扩展性强,更容易进行分布式扩展

功能相对局限

文档型数据库

CouchDB, MongoDb

Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容)

Key-Value对应的键值对,Value为结构化数据

数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构

查询性能不高,而且缺乏统一的查询语法。

图形(Graph)数据库

Neo4J, InfoGrid, Infinite Graph

社交网络,推荐系统等。专注于构建关系图谱

图结构

利用图结构相关算法。比如最短路径寻址,N度关系查找等

很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。

Redis是一个开源内存数据结构存储器,经常用作数据库、缓存以及消息代理等。
优势
●区别于关系型数据库,它提供了非常丰富的存储结构(String、List、Hash、Set、SortedSet等)
●访问速度快,这主要得益于内存存储,高效的存储结构设计(压缩表,跳表等)
●社区活跃(简洁的API、丰富的文档、活跃的社区)

Redis安装部署及启动

请大家记住这个官网网址:https://redis.io/
介于大部分同学没有Linux和Mac环境,那么我今天讲带领大家在我们的windows环境进行一个安装。
Redis本身没有官方支持windows,但是官网给我们提供了一直方式,通过WSL2来让我们在Windows中运行Linux二进制程序。它的前置条件是需要运行Windows 10 version 2004或更高的Windows 11。

Windows安装WSL参考链接

https://learn.microsoft.com/en-us/windows/wsl/install
https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package

安装Redis

Redis配置文件

redis.conf是我们在使用redis的路上非常重要的一个参数,那么我们在这里先介绍几个在运维部署这块几个重要的参数,后续其他的参数会在与之对应的章节来为大家进行详细的讲解

配置名称

描述

daemonize

守护进程,默认是no,我们修改为yes,这样当我们启动redis服务的时候,就不会占用我们的窗口了

bind

这个是绑定了我们的主机地址,意思是只允许我们主机访问redis,这句话注销掉,因为我们需要远程访问

requirepass

既然我们设置了允许远程访问,那密码是必不可少的,如果我们设置了密码,客户端连接redis时需要通过auto 命令提供密码,这里我们设置密码为root

port

这个是端口号的意思,默认是6379,我们不需要修改

Redis图形化界面介绍

下载地址:https://redis.com/redis-enterprise/redis-insight/
目前Redis客户端的GUI是百花齐放,下面这张图是Redis官网为我们推荐的一些图形化客户端,今天我们要给大家介绍的是RedisInsight,这款GUI是Redis官方为我们提供的。那为什么要推荐这款呢?
●免费(我们知道RedisGUI里面也有一个很好用的RedisDesktopManager,但是它是收费的)
●简单易用的可视化监控

我们来看看RedisInsight的效果图

Redis基础数据结构介绍

Redis基础数据结构-String类型

字符串键是Redis中最基本的键值对类型,这种类型的键值对会在数据库中把单独的一个键和单独的一个值关联起来,被关联的键和值既可以是普通的文字数据,也可以是图片、视频、音频、压缩文件等更为复杂的二进制数据。

命令名称

功能

SET

为字符串键设置值

MSET

一次为多个字符串键设置值

GET

获取字符串键的值

MGET

一次获取多个字符串键的值

GETSET

获取旧值并设置新值

MSETNX

只在键不存在的情况下,一次为多个字符串键设置值

STRLEN

获取字符串值的字节长度

GETRANGE

获取字符串值指定索引范围上的内容

SETRANGE

对字符串值的指定索引范围进行设置

APPEND

追加新内容到值的末尾

INCRBY、DECRBY

对整数值执行加法操作和减法操作

INCR、DECR

对整数值执行加1操作和减1操作

INCRBYFLOAT

对数字值执行浮点数加法操作

Redis基础数据结构-Hash类型

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。

命令

功能

HSET

为字段设置值

HGET

获取字段的值

HSETNX

只在字段不存在的情况下为它设置值

HINCRBY

对字段存储的整数值执行加法或减法操作

HINCRBYFLOAT

对字段存储的数字值执行浮点数加法或减法操作

HSTRLEN

获取字段值的字节长度

HEXISTS

检查字段是否存在

HDEL

删除字段

HLEN

获取散列包含的字段数量

HMSET

一次为多个字段设置值

HMGET

一次获取多个字段的值

HMGET hash field [field …]

HKEYS、HVALS、HGETALL

获取所有字段、所有值、所有字段和值

Redis基础数据结构-List类型

命令

功能

LPUSH

将元素推入列表左端

RPUSH

将元素推入列表右端

LPUSHX、RPUSHX

只对已存在的列表执行推入操作

LPOP

弹出列表最左端的元素

RPOP

弹出列表最右端的元素

RPOPLPUSH

将右端弹出的元素推入左端

LLEN

获取列表的长度

LINDEX

获取指定索引上的元素

LRANGE

获取指定索引范围上的元素

LSET

为指定索引设置新元素

LINSERT

将元素插入列表

LTRIM

修剪列表,只保留给定范围之内的元素

LREM

从列表中移除指定元素

BLPOP

阻塞式左端弹出操作

BRPOP

阻塞式右端弹出操作

BRPOPLPUSH

阻塞式弹出并推入操作

Redis基础数据结构-Set类型

命令

功能

SADD

将元素添加到集合

SREM

从集合中移除元素

SMOVE

将元素从一个集合移动到另一个集合

SMEMBERS

获取集合包含的所有元素

SCARD

获取集合包含的元素数量

SISMEMBER

检查给定元素是否存在于集合

SRANDMEMBER

随机获取集合中的元素

SPOP

随机地从集合中移除指定数量的元素

SINTER、SINTERSTORE

对集合执行交集计算

SUNION、SUNIONSTORE

对集合执行并集计算

SDIFF、SDIFFSTORE

对集合执行差集计算

Redis基础数据结构-SortedSet类型

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。

命令

功能

ZADD

添加或更新成员

ZREM

移除指定的成员

ZSCORE

获取成员的分值

ZINCRBY

对成员的分值执行自增或自减操作

ZCARD

获取有序集合的大小

ZRANK、ZREVRANK

获取成员在有序集合中的排名

ZRANGE、ZREVRANGE

获取指定索引范围内的成员

ZRANGEBYSCORE、ZREVRANGEBYSCORE

获取指定分值范围内的成员

ZCOUNT

统计指定分值范围内的成员数量

ZREMRANGEBYRANK

移除指定排名范围内的成员

ZREMRANGEBYSCORE

移除指定分值范围内的成员

ZUNIONSTORE、ZINTERSTORE

有序集合的并集运算和交集运算

ZRANGEBYLEX、ZREVRANGEBYLEX

返回指定字典序范围内的成员

ZLEXCOUNT

统计位于字典序指定范围内的成员数量

ZREMRANGEBYLEX

移除位于字典序指定范围内的成员

ZPOPMAX、ZPOPMIN

弹出分值最高和最低的成员

BZPOPMAX、BZPOPMIN

阻塞式最大/最小元素弹出操作

Redis的Jedis快速入门 & Jedis连接池介绍

Jedis 是Redis官方推荐的java连接开发工具, 如果你要使用java操作redis那么一定要对Jedis十分的熟悉,使用的第一步我们首先通过pom来导入依赖

Redis的Spring-Data-Redis快速入门 & RedisTemplate实战使用

Spring Data Redis (SDR) 框架通过 Spring 出色的基础架构支持消除了与存储交互所需的冗余任务和样板代码,从而使编写使用 Redis 键值存储的 Spring 应用程序变得容易。
我们发现其实在使用纯SDK时是比较难受的,那么Spring也为我们提供了对于Redis封装的组件“spring-data-redis”,那么我们首先来看怎么集成

高级篇

基于HyperLogLog数据结构实现网络UV统计

Redis 在 2.8.9 版本添加了 HyperLogLog 结构。Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

命令

描述

pfadd

添加

pfcount

统计

pfmerge

合并两组

基于Geo数据结构实现附近的人查找

Redis 3.2 版本新增了geo相关命令,用于存储和操作地理位置信息。提供的命令包括添加、计算位置之间距离、根据中心点坐标和距离范围来查询地理位置集合。

命令

描述

geoadd

添加地理位置

geopos

获取指定成员的经纬度

geodist

计算两个成员之间的直线距离

georadius

计算某个位置半径内的成员

georadiusbymenbers

计算指定成员半径内的成员

基于Bitmap实现千万级海量数据统计

位图(bitmap)同样属于 string 数据类型。Redis 中一个字符串类型的值最多能存储 512 MB 的内容,每个字符串由多个字节组成,每个字节又由 8 个 Bit 位组成。位图结构正是使用“位”来实现存储的,它通过将比特位设置为 0 或 1来达到数据存取的目的,这大大增加了 value 存储数量,它存储上限为2^32 ,时间复杂度为O(1)。由于bit是计算机中最小的单位,使用它进行储存将非常节省空间,特别适合一些数据量大且使用二值统计的场景。

Redis持久化之RDB

一句话介绍什么是RDB

RDB(Redis DataBase)持久化是Redis默认的一种持久化功能,它可以创建出一个经过压缩的二进制文件,这个文件包含了服务器在各个数据库中存储的键值对信息。

怎么使用RDB

阻塞服务器创建RDB文件

它是一同步的方式去创建的RDB文件,那么在执行期间Redis服务器将持续阻塞直到完成。如果已经存在RDB文件则会直接替代,它的时间复杂度为O(N),其中N指的是整个Redis包含的键值对总和

非阻塞方式创建RDB文件

它区别于SAVE,它是由子进程通过异步方式执行的,它不会等待RDB文件生成完成之后再返回,而是直接返回OK,然后后台执行,那么在BGSAVE同步期间,Redis还是可以处理其他客户端的命令请求。

通过配置选项自动创建RDB文件

那么我们除了SAVE和BGSAVE我们还可以通过设置SAVE的参数,让它满足条件时自动执行后台创建RDB。
SECONDS
这个命令是指触发持久化操作所需要的时长
CHANGES
这个命令指的是执行多少次修改操作来触发持久化操作命令
举个例子
Redis也是允许多个SAVE共存的,只要满足其中一个条件就会执行持久化
tip:
●为了避免频繁的执行持久化,每次持久化之后就会将时间计数器和修改计数器进行清零
●RDB持久化是Redis默认使用的持久化功能,如果我们没有关闭RDB也没有启动AOF的时候,那么Redis就会按照以下save命令进行持久化

RDB运行的机制是什么

RDB总体结构

●RDB文件标识符
●版本号
●设备附加信息
●数据库数据
○数据库结构
■数据库号码
■键值对总数量
■带有过期时间的键值对数量
■键值对数据部分
●类型
●键
●值
●LFU信息
●LRU信息
●过期时间
●Lua脚本缓存
●EOF
●CRC64校验和

Redis持久化之AOF

一句话介绍什么是AOF

AOF(Append Only File)以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来 (读操作不记录), 只许追加文件但不可以改写文件,redis 启动之初会读取该文件重新构建数据。

如何使用AOF

我们通过配置redis.conf文件来开启AOF

AOF有哪些持久化策略

AOF持久化策略(即缓冲区内容写入和同步sync到AOF中),可以通过配置appendfsync属性来选择AOF持久化策略:
●always:
○将aof_buf缓冲区中的所有内容写入并同步到AOF文件,每次有新命令追加到 AOF 文件时就执行一次 fsync。
○效率最慢的,但安全性是最安全的,即使出现故障宕机,持久化也只会丢失一个事件 循环的命令数据
●everysec(默认):
○如果上次同步AOF的时间距离现在超过一秒,先将aof_buf缓冲区中的所有内容写入到AOF文件,再次对AOF文件进行同步,且同步操作由一个专门线程负责执行。
○兼顾速度和安全性,出现宕机也只是丢失一秒钟的命令数据
●no:
○将aof_buf缓冲区中的所有内容写入到AOF文件,但并不对AOF文件进行同步,何时同步由操作系统(OS)决定。
○写入最快,但综合起来单次同步是时间是最长的,且出现宕机时会丢失上传同步AOF文件之后的所有命令数据。

Redis之混合持久化

概括

在实际生产场景中我们会碰到Redis重启的这种场景,那么我们在重启之后就要通过RDB或者AOF来恢复我们内存中的数据,通常情况下来说我们不会使用RDB来恢复,因为可能会丢失大量数据。所以基本采用的是AOF,但是AOF也有自身的缺陷那就是恢复过程时间长,在Redis实例很大的情况下,启动需要花费很长的一段时间。

使用方式

修改配置文件

原理

如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理
并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件
才会进行改名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。
于是在Redis重启的时候,可以先加载RDB的内容,然后再重放增量AOF日志就可以完全替代之前的AOF全量文件重放,因此重启效率大幅得到提升

Redis之主从复制

概括

复制功能是Redis提供的多机功能中最基础的一个,这个功能是通过主从复制(master-slave replication)模式实现的,它允许用户为存储着目标数据库的服务器创建出多个拥有相同数据库副本的服务器,其中存储目标数据库的服务器被称为主服务器(master server),而存储数据库副本的服务器则被称为从服务器(slave server,或者称为replica)
对于Redis来说,一个主服务器可以拥有任意多个从服务器,而从服务器本身也可以用作其他服务器的主服务器,并以此构建出一个树状的服务器结构,如图18-2所示。需要注意的是,虽然一个主服务器可以拥有多个从服务器,但一个从服务器只能拥有一个主服务器。换句话说,Redis提供的是单主复制功能,而不是多主复制功能。
在默认情况下,处于复制模式的主服务器既可以执行写操作也可以执行读操作,而从服务器则只能执行读操作,图18-3和图18-4分别展示了Redis服务器在无复制和有复制两种状态下的客户端访问模式。
对于开启了复制功能的主从服务器,主服务器在每次执行写操作之后,都会与所有从服务器进行数据同步,以此来将写操作产生的改动反映到各个从服务器之上。举个例子,在主服务器执行了客户端发来的写命令W之后,主服务器会将相同的写命令W发送至所有从服务器执行,以此来保持主从服务器之间的数据一致性,如图18-5所示。
Redis的复制功能可以从性能、安全性和可用性3个方面提升整个Redis系统:
●首先,在性能方面,Redis的复制功能可以给系统的读性能带来线性级别的提升。从理论上来说,用户每增加一倍数量的从服务器,整个系统的读性能就会提升一倍。
●其次,通过增加从服务器的数量,用户可以降低系统在遭遇灾难故障时丢失数据的可能性。具体来说,如果用户只有一台服务器存储着目标数据库,那么当这个服务器遭遇灾难故障时,目标数据库很有可能会随着服务器故障而丢失。但如果用户为Redis服务器(即主服务器)设置了从服务器,那么即使主服务器遭遇灾难故障,用户也可以通过从服务器访问数据库。从服务器的数量越多,因为主服务器遭遇灾难故障而出现数据库丢失的可能性就越低。
●最后,通过同时使用Redis的复制功能和Sentinel功能,用户可以为整个Redis系统提供高可用特性。具有这一特性的Redis系统在主服务器停机时,将会自动挑选一个从服务器作为新的主服务器,以此来继续为客户提供服务,避免造成整个系统停机。

全量复制

在我们Redis 2.8 之前只有我们的全量同步,但是在之后又新增了增量复制功能~
确立主从关系
例如,现在有实例 1(ip:172.16.19.3)和实例 2(ip:172.16.19.5),我们在实例 2 上执行以下这个命令后,实例 2 就变成了实例 1 的从库,并从实例 1 上复制数据:
全量复制的三个阶段
第一阶段是主从库间建立连接、协商同步的过程,主要是为全量复制做准备。在这一步,从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库间就可以开始同步了。
具体来说,从库给主库发送 psync 命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。psync 命令包含了主库的 runID 和复制进度 offset 两个参数。runID,是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例。当从库和主库第一次复制时,因为不知道主库的 runID,所以将 runID 设为“?”。offset,此时设为 -1,表示第一次复制。主库收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset,返回给从库。从库收到响应后,会记录下这两个参数。这里有个地方需要注意,FULLRESYNC 响应表示第一次复制采用的全量复制,也就是说,主库会把当前所有的数据都复制给从库。
第二阶段,主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的 RDB 文件。
具体来说,主库执行 bgsave 命令,生成 RDB 文件,接着将文件发给从库。从库接收到 RDB 文件后,会先清空当前数据库,然后加载 RDB 文件。这是因为从库在通过 replicaof 命令开始和主库同步前,可能保存了其他数据。为了避免之前数据的影响,从库需要先把当前数据库清空。在主库将数据同步给从库的过程中,主库不会被阻塞,仍然可以正常接收请求。否则,Redis 的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的 RDB 文件中。为了保证主从库的数据一致性,主库会在内存中用专门的 replication buffer,记录 RDB 文件生成后收到的所有写操作。
第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库。具体的操作是,当主库完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从库,从库再重新执行这些操作。这样一来,主从库就实现同步了。

增量复制

为什么会设计增量复制?
如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。
增量复制的流程
先看两个概念: replication buffer 和 repl_backlog_buffer
repl_backlog_buffer:它是为了从库断开之后,如何找到主从差异数据而设计的环形缓冲区,从而避免全量复制带来的性能开销。如果从库断开时间太久,repl_backlog_buffer环形缓冲区被主库的写命令覆盖了,那么从库连上主库后只能乖乖地进行一次全量复制,所以repl_backlog_buffer配置尽量大一些,可以降低主从断开后全量复制的概率。而在repl_backlog_buffer中找主从差异的数据后,如何发给从库呢?这就用到了replication buffer。
replication buffer:Redis和客户端通信也好,和从库通信也好,Redis都需要给分配一个 内存buffer进行数据交互,客户端是一个client,从库也是一个client,我们每个client连上Redis后,Redis都会分配一个client buffer,所有数据交互都是通过这个buffer进行的:Redis先把数据写到这个buffer中,然后再把buffer中的数据发到client socket中再通过网络发送出去,这样就完成了数据交互。所以主从在增量同步时,从库作为一个client,也会分配一个buffer,只不过这个buffer专门用来传播用户的写命令到从库,保证主从数据一致,我们通常把它叫做replication buffer。
●如果在网络断开期间,repl_backlog_size环形缓冲区写满之后,从库是会丢失掉那部分被覆盖掉的数据,还是直接进行全量复制呢?
对于这个问题来说,有两个关键点:
1一个从库如果和主库断连时间过长,造成它在主库repl_backlog_buffer的slave_repl_offset位置上的数据已经被覆盖掉了,此时从库和主库间将进行全量复制。
2每个从库会记录自己的slave_repl_offset,每个从库的复制进度也不一定相同。在和主库重连进行恢复时,从库会通过psync命令把自己记录的slave_repl_offset发给主库,主库会根据从库各自的复制进度,来决定这个从库可以进行增量复制,还是全量复制。

Redis哨兵机制

在上文主从复制的基础上,如果注节点出现故障该怎么办呢? 在 Redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制,它有效地解决了主从复制模式下故障转移的问题。

哨兵机制及架构

●监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。
●自动故障转移(Automatic failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
●配置提供者(Configuration provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
●通知(Notification):哨兵可以将故障转移的结果发送给客户端。

哨兵机制的组件

上图中哨兵集群是如何组件的呢?哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。
在主从集群中,主库上有一个名为__sentinel__:hello的频道,不同哨兵就是通过它来相互发现,实现互相通信的。在下图中,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到__sentinel__:hello频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。然后,哨兵 2、3 可以和哨兵 1 建立网络连接。
通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。

哨兵监控Redis库

哨兵监控什么呢?怎么监控呢?
这是由哨兵向主库发送 INFO 命令来完成的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。哨兵 1 和 3 可以通过相同的方法和从库建立连接。

主库下线的判定

哨兵如何判断主库已经下线了呢?
首先要理解两个概念:主观下线和客观下线
●主观下线:任何一个哨兵都是可以监控探测,并作出Redis节点下线的判断;
●客观下线:有哨兵集群共同决定Redis节点是否下线;
当某个哨兵(如下图中的哨兵2)判断主库“主观下线”后,就会给其他哨兵发送 is-master-down-by-addr 命令。接着,其他哨兵会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。

如果赞成票数(这里是2)是大于等于哨兵配置文件中的 quorum 配置项(比如这里如果是quorum=2), 则可以判定主库客观下线了。

哨兵集群的选举

判断完主库下线后,由哪个哨兵节点来执行主从切换呢?这里就需要哨兵集群的选举机制了。
●为什么必然会出现选举/共识机制?
为了避免哨兵的单点情况发生,所以需要一个哨兵的分布式集群。作为分布式集群,必然涉及共识问题(即选举问题);同时故障的转移和通知都只需要一个主的哨兵节点就可以了。
●哨兵的选举机制是什么样的?
哨兵的选举机制其实很简单,就是一个Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举
Raft算法你可以参看这篇文章分布式算法 - Raft算法
●任何一个想成为 Leader 的哨兵,要满足两个条件:
○第一,拿到半数以上的赞成票;
○第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
以 3 个哨兵为例,假设此时的 quorum 设置为 2,那么,任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。
更进一步理解
这里很多人会搞混 判定客观下线 和 是否能够主从切换(用到选举机制) 两个概念,我们再看一个例子。
Redis 1主4从,5个哨兵,哨兵配置quorum为2,如果3个哨兵故障,当主库宕机时,哨兵能否判断主库“客观下线”?能否自动切换?
经过实际测试:
1、哨兵集群可以判定主库“主观下线”。由于quorum=2,所以当一个哨兵判断主库“主观下线”后,询问另外一个哨兵后也会得到同样的结果,2个哨兵都判定“主观下线”,达到了quorum的值,因此,哨兵集群可以判定主库为“客观下线”。
2、但哨兵不能完成主从切换。哨兵标记主库“客观下线后”,在选举“哨兵领导者”时,一个哨兵必须拿到超过多数的选票(5/2+1=3票)。但目前只有2个哨兵活着,无论怎么投票,一个哨兵最多只能拿到2票,永远无法达到N/2+1选票的结果。

新主库的选出

主库既然判定客观下线了,那么如何从剩余的从库中选择一个新的主库呢?
●过滤掉不健康的(下线或断线),没有回复过哨兵ping响应的从节点
●选择salve-priority从节点优先级最高(redis.conf)的
●选择复制偏移量最大,只复制最完整的从节点

故障的转移

新的主库选择出来后,就可以开始进行故障的转移了。
假设根据我们一开始的图:(我们假设:判断主库客观下线了,同时选出sentinel 3是哨兵leader),故障转移流程如下:

●将slave-1脱离原从节点(PS: 5.0 中应该是replicaof no one),升级主节点,
●将从节点slave-2指向新的主节点
●通知客户端主节点已更换
●将原主节点(oldMaster)变成从节点,指向新的主节点
转移之后

Redis集群分片

1分片集群需要的节点数量较多,这里我们搭建一个最小的分片集群,包含3个master节点,每个master包含一个slave节点
2准备新的redis.conf文件
3创建集群

优化篇

缓存雪崩 & 击穿 & 穿透问题

面试题分析

这道题主要考察的是求职者是否具有高并发思维,它也是在面试中一道高频的考点

缓存穿透

缓存穿透代表的意思是在我们的缓存中没有找到缓存信息,那么我们在高并发场景下就会面临所有的请求都会直接打到DB,缓存则失去了它原本的意义,并且极有可能导致数据库压力过大而造成服务不可用。
●缓存空结果信息
●布隆过滤器(不存在的一定不存在,存在的可能不存在,通过bitmap实现,想深入布隆过滤器可以专门去看看这部分专题内容)
●过滤常见非法参数,拦截大部分无效请求()

缓存击穿

缓存击穿代表的意思是我们数据库中存在数据,但是缓存中不存在数据.这种场景一般是在缓存失效时发生的. 在高并发的场景下极有可能瞬间打垮数据库.
●我们可以考虑面对请求量大的热点接口直接将缓存设置永不过期.
●当然我们也可能碰到一些特殊场景不能设置永久缓存,那么我们可以在db为空时设置互斥锁,当查询完db更新至缓存时再释放锁

缓存雪崩

缓存雪崩代表是意思是我们在某一个时间段,碰到大量热点缓存数据过期导致大量请求直接打垮数据库
●我们可以考虑面对请求量大的热点接口直接将缓存设置永不过期.
●缓存过期时间可以设置一个随机的波动值,防止大量数据在同一时间过期

如何解决双写问题?

我们来分析一下这道面试题,这道题主要是偏实际应用的
缓存可以提升性能,减轻数据库压力,在获取这部分好处的同时,它却带来了一些新的问题,缓存和数据库之间的数据一致性问题。
想必大家在工作中只要用了咱们缓存势必就会遇到过此类问题,那这道题该如何回答呢?
首先我们来看看一致性:
●强一致性
●弱一致性
解决双写一致性方案:
●延迟双删
○延迟双删策略是分布式系统中数据库存储和缓存数据保持一致性的常用策略,但它不是强一致。
○实现思路:也是非常简单的,先删除缓存然后更新DB在最后延迟 N 秒去再去执行一次缓存删除
○弊端:小概率会出现不一致情况、耦合程度高
●通过MQ进行重试删除
○更新完DB之后进行删除,如果删除失败则向MQ发送一条消息,然后消费者不断进行删除尝试。
●binlog异步删除
○实现思路:低耦合的解决方案是使用canal。canal伪装成mysql的从机,监听主机mysql的二进制文件,当数据发生变化时发送给MQ。最终消费进行删除