美食家大橙子 发表于 2024-12-16 23:36:26

Redis 三种摆设方式实践

Redis


[*]体系情况
[*]Docker安装
[*]常见数据范例

[*]常用数据范例
[*]常用数据布局

[*]长期化

[*]RDB
[*]AOF
[*]混淆模式

[*]Redis-逾期计谋
[*]Redis-内存淘汰
[*]Redis-哨兵
[*]Redis-集群
[*]Redis-性能测试
[*]Redis-常见问题
体系情况



[*]体系:macOS
[*]处置处罚器:2.6 GHz 六核Intel Core i7
[*]内存:16GB
[*]版本:15.0
docker 安装

redis.config
# 60s 内有3个值变动,通过bgsave持久化
save 60 3

# bgsave 失败之后,是否停止持久化数据到磁盘,yes 表示停止持久化,no 表示忽略错误继续写文件。
stop-writes-on-bgsave-error yes



# RDB 文件压缩
rdbcompression yes


# rdb & aof 开启混合持久化
aof-use-rdb-preamble yes


requirepass 123456
appendonly yes
protected-mode no
port 6379
docker pull redis:@latest
docker run -d --name redis-1 \
-e TZ='Asia/Shanghai' \
-v /Users/wx/workspace/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf \
-v /Users/wx/workspace/docker/redis/data:/data \
-p 6380:6379 \
redis redis-server /usr/local/etc/redis/redis.conf
常见数据范例

常用数据范例
String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)
新增数据范例
HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)
常用数据范例


[*] String
String 是最根本的 key-value 布局,key 是唯一标识,value 是具体的值,value实在不仅是字符串,也可以是数字(整数或浮点数),value 最多可以容纳的数据长度是 512MB
根本用法
# 设置一个键值对,以及过期时间
set key value [EX seconds|PX millise
# 获取 key 对用 value
get key
应用场景

[*] 缓存对象
SET user:1 '{"name":"xiaolin", "age":18}'

[*] 通例计数
SET aritcle:readcount:1001 0
incr aritcle:readcount:1001

[*] 分布式锁
SET lock_key unique_value NX PX 10000


[*] List
List(双向列表)列表是简朴的字符串列表,按照插入次序排序,可以从头部或尾部向 List 列表添加元素。
根本用法
# 向列表的头/尾部插入 n 个元素
LPUSH/RPUSH key value...
# 获取 列表 头/尾元素
LPOP/RPOP key
# 返回列表key中指定区间内的元素,区间以偏移量start和stop指定,从0开始
LRANGE key start stop
# 从key列表表头弹出一个元素,没有就阻塞timeout秒,如果timeout=0则一直阻塞
BLPOP/BRPOP key timeout
应用场景

[*]消息队列,生产者使用LPUSH/RPUSH往列表中插入消息,斲丧者使用BRPOP/BLPOP斲丧列表中的数据,保证了有序性,也确保了斲丧者阻塞式读取数据;生产者使用全局唯一ID确保不会重复消息处置处罚;使用BRPOPLPUSH确保消息可靠

[*] Hash
Hash是一个键值对(key-value),value=[{field1, value1},{field2, value2},{fieldN, valueN}],比较恰当存储对象
根本用法
# 存储一个哈希表的键值
hset stu1 name wx age 12

# 返回哈希表key中field的数量
hlen stu1
# 返回所有的键值
hgetall stu1
应用场景

[*] 缓存对象,对于对象中某些频仍变革的属性可以用Hash范例来存储
hset stu1 name wx age 12


[*] Set
Set范例是一个无序并唯一的键值集合,它的存储次序不会按照插入的先后次序进行存储。
根本用法
# 往集合key中存入元素,元素存在则忽略,若key不存在则新建
sadd key member
# 从集合key中删除元素
srem key member
# 获取所有元素
smembers key
# 获取集合中key中的数量
scard key

# 判断 member元素是否存在集合key中
sismember key member

# 从集合key中随机选出count个元素,元素不从key中删除
srandmember key

# 从集合key中随机选出count个元素,元素从集合中删除
spop key

## 集合 交集、并集、差集运算 ....
# 交集
SINTER key
# 交集的结果保存在新的集合中
SINTERSTORE destination key
# 并集
SUNION key
SUNIONSTORE destination key
# 差集
SDIFF key
SDIFFSTORE destination key
应用场景

[*]文章点赞,key是文章id,value是用户id
## 点赞
sadd article:1 uid:1
sadd article:1 uid:2
sadd article:1 uid:3
## 取消点赞
srem article:1 uid:1
## 查看所有点赞用户
smembers article:1
   

[*]抽奖活动,集合中存储用户名
## 集合存储用户名
sadd lockly user1 user2 user3 user4 user5 user6
## 抽取一个一等奖
SRANDMEMBER lockly 1
# 抽取2个二等奖
SRANDMEMBER lockly 2
# 抽取三个三等奖
SRANDMEMBER lockly 3

[*] Zset
Zset是有序集合,相比Set范例多了一个排序属性score(分数),对于有序集合ZSet来说,每个存储元素相当于两个值构成的,一个是有序集合的元素值,一个是排序值
根本用法
# 往有序集合key中加⼊带分值元素
ZADD key score member [...]
# 往有序集合key中删除元素
ZREM key member
# 返回有序集合key中元素member的分值
ZSCORE key member
# 返回有序集合key中元素个数
ZCARD key
# 为有序集合key中元素member的分值加上increment
ZINCRBY key increment member
# 正序获取有序集合key从start下标到stop下标的元素
ZRANGE key start stop
# 倒序获取有序集合key从start下标到stop下标的元素
ZREVRANGE key start stop
# 返回有序集合中指定分数区间内的成员,分数由低到⾼排序。
ZRANGEBYSCORE key min max
# 返回指定成员区间内的成员,按字典正序排列, 分数必须相同。
ZRANGEBYLEX key min max
# 返回指定成员区间内的成员,按字典倒序排列, 分数必须相同
ZREVRANGEBYLEX key max min
应用场景

[*]排行榜,文章欣赏量排行榜
# 初始化数据
ZADD article:book 200 article:1 50 article:2 150 article:3 220 article:4 100 article:5
# article:3 增加浏览量
ZINCRBY article:book 1 article:3
# 获取浏览量前三的文章和浏览量
ZREVRANGE article:book 0 2 withscores

[*] BitMap
BitMap,位图是⼀串连续的⼆进制数组(0和1),可以通过偏移量(offset)定位元素。BitMap通过最⼩的单位bit
来进⾏ 0|1 的设置,表示某个元素的值或者状态,时间复杂度为O(1)。
根本用法
# 设置值,其中value只能是 0 和 1
SETBIT key offset value
# 获取值
GETBIT key offset
# 获取指定范围内值为 1 的个数
# start 和 end 以字节为单位
BITCOUNT key start end

# BitMap间的运算
# operations 位移操作符,枚举值
AND 与运算 &
OR 或运算 |
XOR 异或 ^
NOT 取反 ~
# result 计算的结果,会存储在该key中
# key1 … keyn 参与运算的key,可以有多个,空格分割,not运算只能⼀个key
# 当 BITOP 处理不同⻓度的字符串时,较短的那个字符串所缺少的部分会被看作 0。返回值是保存到 destkey
BITOP
# 返回指定key中第⼀次出现指定value(0/1)的位置
BITPOS
应用场景

[*]签到统计,在签到打卡的场景中,我们只用记载签到(1)或未签到(0)
# 用户 a 11月9日 签到
SETBIT uid:a:202411 8 1
# 检查该用户是否在 11月9日签到
GETBIT uid:a:202411 8
# 统计签到次数
BITCOUNT uid:a:202411

[*] Geo
Geo 重要用于存储地理位置信息,并对存储的信息进行操作
根本用法
# 存储指定的地理空间位置,可以将⼀个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加
GEOADD key longitude latitude member
# 从给定的 key ⾥返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
GEOPOS key member
# 返回两个给定位置之间的距离。
GEODIST key member1 member2
# 根据⽤户给定的经纬度坐标来获取指定范围内的地理位置集合。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHHASH
应用场景

[*]滴滴叫车
# 设置 司机(33)车辆位置
GEOADD cars:locations 116.034567 39.030452 33
# 用户的经纬度附近 5 公里你的车辆信息
GEORADIUS cars:locations 116.054579 39.030453 5 km ASC COUNT 10

[*] Stream
Stream 实现了消息队列,支持消息的长期化、支持主动生成全局唯一ID、支持ack确认消息模式、支持斲丧组模式等
根本用法
XADD - 添加消息到末尾
XTRIM - 对流进行修剪,限制长度
XDEL - 删除消息
XLEN - 获取流包含的元素数量,即消息长度
XRANGE - 获取消息列表,会自动过滤已经删除的消息
XREVRANGE - 反向获取消息列表,ID 从大到小
XREAD - 以阻塞或非阻塞方式获取消息列表
应用场景

[*]消息队列
// todo

常用数据布局

Redis有简朴动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组,他们和常用数据范例的对应关系如下
https://i-blog.csdnimg.cn/direct/702b50f4f0e249b5a45c0e070a4b6224.png

[*] 键和值用什么布局组织?
Redis使用了哈希表来保存全部键值对,哈希表就是一个数组,数组的每个元素称为一个哈希桶,在每一个哈希桶中保存了键值对数据
https://i-blog.csdnimg.cn/direct/dd48495404364a9593ea3039749523d5.png
哈希表保存了全部的键值对,也称为全局哈希表。哈希表的最大好处很明显,就是可以用 O(1) 的时间复杂度来快速查找到键值对——我们只需要计算键的哈希值,就可以知道它所对应的哈希桶位置,然后就可以访问相应的 entry 元素
哈希表的 O(1) 复杂度和快速查找键值对,也带来一个问题,哈希冲突会带来操作阻塞
[*] 哈希表操作变慢
哈希冲突时不可避免的问题,Redis办理哈希冲突的方式是链式哈希,同一个哈希桶中的多个元素用一个链表来保存,它们之间依次用指针连接。
https://i-blog.csdnimg.cn/direct/df79076b8f774dc2bd3f4d441fc0dc40.png
这里会引发另一个问题,哈希冲突链上的元素只能通过指针逐一查找再操作。假如哈希表里写入的数据越来越多,哈希冲突大概也会越来越多,这就会导致某些哈希冲突链过长,进而导致这个链上的元素查找耗时长,服从降低
Redis的办理措施是渐进式rehash,Redis 默认使用了两个全局哈希表:哈希表 1 和哈希表 2,默认使用哈希表 1,当开始进行rehash时,分为三步:

[*]给哈希表 2 分配更大的空间,例如是当前哈希表 1 巨细的两倍;
[*]把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中;
[*]开释哈希表 1 的空间。
为了避免第二步拷贝全部数据造成阻塞,每处置处罚一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的全部 entries 拷贝到哈希表 2 中。
避免了一次性大量拷贝的开销,分摊到了多次处置处罚请求的过程中,避免了耗时操作,保证了数据的快速访问

[*] 压缩列表
压缩列表实际上类似于一个数组,数组中的每一个元素都对应保存一个数据。和数组差别的是,压缩列表在表头有三个字段 zlbytes、zltail 和 zllen,分别表示列表长度、列表尾的偏移量和列表中的 entry 个数;压缩列表在表尾还有一个 zlend,表示列表结束。
https://i-blog.csdnimg.cn/direct/b266d033d73c4142aa9926bad09e9414.png
假如我们要查找定位第一个元素和最后一个元素,可以通过表头三个字段的长度直接定位,复杂度是 O(1)。而查找其他元素时,就没有这么高效了,只能逐个查找,此时的复杂度就是 O(N)
[*] 跳表
有序链表只能逐一查找元素,导致操作起来非常缓慢,于是就出现了跳表。具体来说,跳表在链表的基础上,增长了多级索引,通过索引位置的几个跳转,实现数据的快速定位
https://i-blog.csdnimg.cn/direct/2a67edd521474580b8ac775b570dfefd.png
差别数据布局查找的时间复杂度
https://i-blog.csdnimg.cn/direct/393a64fd91764ac5a246dfccbdff4af5.png
长期化

三种长期化方式


[*]快照方式(RDB, Redis DataBase)将某一个时候的内存数据,以二进制的方式写入磁盘;
[*]文件追加方式(AOF, Append Only File),记载全部的操作命令,并以文本的情势追加到文件中;
[*]混淆长期化方式,Redis 4.0 之后新增的方式,混淆长期化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的情势写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,如许既能保证 Redis 重启时的速度,又能减低数据丢失的风险。
RDB 长期化

RDB(Redis DataBase)是将某一个时候的内存快照(Snapshot),以二进制的方式写入磁盘的过程。
RDB 的长期化触发方式有两类:一类是手动触发,另一类是主动触发。

[*] 手动触发
手动触发长期化的操作有两个: save 和 bgsave ,它们重要区别体如今:是否阻塞 Redis 主线程的实行

[*]save:在客户端实行save命令,就会触发长期化,会是Redis进入阻塞状态,慎用
[*]bgsave:bgsave(background save),通过fork()一个子进程来实行长期化,只有当创建子进程时会有短暂阻塞。

[*] 主动触发
save m n 是指在 m 秒内,假如有 n 个键发生改变,满足设置的触发条件,主动实行一次 bgsave 命令
flushall 命令用于清空Redis数据库,并把RDB文件清空
[*] 设置配置,在redis-cli操作
master-slave-config get dbfilename
master-slave-config get dir
master-slave-config set dir "" # 设置持久化路径
master-slave-config set save "" # 禁止持久化

[*] RDB 优缺点
优点

[*]RDB 的内容为二进制的数据,占用内存小,更紧凑,恰当作为备份文件
[*]RDB 备份使用的是子进程进行数据长期化至磁盘,不会影响住进程
缺点
   

[*]只能保存某个时间间隔的数据,中途Redis服务意外制止,数据会丢失
[*]fork()子进程会将数据长期化至磁盘,数据集很大,长期化CPU性能不佳,会影响到住进程

AOF 长期化

AOF(Append Only File)中文是附加到文件,顾名思义 AOF 可以把 Redis 每个键值对操作都记载到文件(appendonly.aof)中。

[*] 长期化配置
master-slave-config get appendonly #查询 AOF是否启动
master-slave-config set appendonly yes # 启动 AOF
master-slave-config set appendonly no # 关闭 AOF
redis.conf中的配置文件中设置appendonly yes即可开启AOF
[*] 触发长期化
触发条件有两种主动触发和手动触发

[*] 主动触发,满足AOF设置的计谋触发,config get appendonly,获取AOF计谋,重要计谋

[*]always:每条Redis操作命令都会写入磁盘,最多丢失一条数据
[*]everysec:每秒钟写入一次磁盘,最多丢失一秒的数据
[*]no:不设置写入磁盘的规则,根据当前操作体系来决定何时写入磁盘,Linux默认30s写入一次数据至磁盘

[*] 手动触发,在客户端实行bgrewriteaof命令,可以触发文件重写

[*] 文件重写
AOF 是通过记载 Redis 的实行命令来长期化(保存)数据的,不断对Redis实行命令AOF文件会越来越多,如许不仅增长了服务器的存储压力,也会造成 Redis 重启速度变慢,为了办理这个问题 Redis 提供了 AOF 重写的功能。主线程 fork 出后台的 bgrewriteaof 子进程,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日记

[*]auto-aof-rewrite-min-size答应 AOF 重写的最小文件容量,默认是 64mb
[*]auto-aof-rewrite-percentageAOF 文件重写的巨细比例,默认值是 100,表示 100%

[*] 优缺点
优点

[*]AOF 长期化保存的数据更加完备,最多只会丢失 1s 钟的数据
[*]AOF 采用的是命令追加的写入方式,所以不会出现文件粉碎的问题
[*]AOF 长期化文件,非常容易明白息争析,它是把全部 Redis 键值操作命令,即使使用flushall也可以恢复数据
缺点
   

[*]对于相同的数据集来说,AOF 文件要大于 RDB 文件
[*]在 Redis 负载比较高的情况下,RDB 比 AOF 性能更好

RDB & AOF 混淆长期化

config set aof-use-rdb-preamble yes开启混淆长期化
当开启了混淆长期化时,在 AOF 重写日记时,fork出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处置处罚的操作命令会被记载在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后关照主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件更换旧的的 AOF 文件。
优缺点
优点


[*]开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险
缺点


[*]AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差
[*]不能兼容redis4.0之前的版本
Redis 逾期计谋

设置逾期时间


[*] 使用pexpire

[*]expire key seconds:设置 key 在 n 秒后逾期
[*]pexpire key milliseconds:设置 key 在 n 毫秒后逾期
[*]expireat key timestamp:设置 key 在某个时间戳(准确到秒)之后逾期
[*]pexpireat key millisecondsTimestamp:设置 key 在某个时间戳(准确到毫秒)之后逾期

[*] 字符串中的逾期操作

[*]set key value ex seconds:设置键值对的同时指定逾期时间(准确到秒)
[*]set key value px milliseconds:设置键值对的同时指定逾期时间(准确到毫秒)
[*]setex key seconds valule:设置键值对的同时指定逾期时间(准确到秒)

[*] 移除逾期时间
persist key 可以移除键值的逾期时间
长期化中的逾期键

RDB,从内存状态长期化成 RDB(文件)的时候,会对 key 进行逾期检查,逾期的键不会被保存到新的 RDB 文件中,因此 Redis 中的逾期键不会对生成新 RDB 文件产生任何影响
AOF,假如数据库某个逾期键还没被删除,那么 AOF 文件会保存此逾期键,当此逾期键被删除后,Redis 会向 AOF 文件追加一条 DEL 命令来显式地删除该键值。重写之后不会被保存
逾期计谋

Redis 会删除已逾期的键值,以此来减少 Redis 的空间占用,但由于 Redis 自己是单线的,假如由于删除操作而影响主业务的实行就得不偿失了,为此 Redis 需要订定多个(逾期)删除计谋来保证糟糕的事情不会发生
常见的逾期计谋:定时删除、惰性删除、定期删除


[*] 定时删除
在设置键值逾期时间时,创建一个定时变乱,当逾期时间到达时,由变乱处置处罚器主动实行键的删除操作

[*]优点:保证内存可以被尽快开释
[*]缺点:在Redis高负载的时,会造成Redis服务器卡顿

[*] 惰性删除
不主动删除逾期键,每次从数据库获取键值时判断是否逾期,假如逾期则删除键值,并返回 null

[*]优点:每次访问时,才会判断逾期键,只使用了很少的体系资源
[*]缺点:体系占用空间删除不及时,导致空间利用率降低,造成了肯定的空间浪费

[*] 定期删除
每隔一段时间检查一次数据库,随机删除一些逾期键,配置hz 10

[*]优点:通过限制删除操作的时长和频率,来减少操作对Redis主业务的影响,同时也能删除一部份逾期的数据
[*]缺点:内存清理方面没有定时删除效果好,同时没有惰性删除使用的体系资源少

总结:Redis 使用的是惰性删除加定期删除的逾期计谋
Redis 内存淘汰

当 Redis 运行内存已经凌驾 Redis 设置的最大内存之后,将采用什么计谋来删除符合条件的键值对,以此来保障 Redis 高效的运行,其中64位操作体系的没有对内存巨细做限制,config get maxmemory查看内存限制。
内存淘汰计谋


[*] 内存淘汰计谋分类

[*]noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰计谋;
[*]allkeys-lru:淘汰整个键值中最久未使用的键值;
[*]allkeys-random:随机淘汰任意键值;
[*]volatile-lru:淘汰全部设置了逾期时间的键值中最久未使用的键值;
[*]volatile-random:随机淘汰设置了逾期时间的任意键值;
[*]volatile-ttl:优先淘汰更早逾期的键值。
[*]volatile-lfu:淘汰全部设置了逾期时间的键值中,最少使用的键值;
[*]allkeys-lfu:淘汰整个键值中最少使用的键值。
其中 allkeys-xxx 表示从全部的键值中淘汰数据,而 volatile-xxx 表示从设置了逾期键的键值中淘汰数据。

[*] 修改 Redis 内存淘汰计谋

[*]通过config set maxmemory-policy xxx 命令设置。它的优点是设置之后立即见效,不需要重启 Redis 服务,缺点是重启 Redis 之后,设置就会失效
[*]通过修改 Redis 配置文件修改,设置maxmemory-policy xxx,它的优点是重启 Redis 服务后配置不会丢失,缺点是必须重启 Redis 服务,设置才气见效

内存淘汰算法


[*] LRU算法
LRU 全称是 Least Recently Used 译为最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰
[*] LFU算法
LFU 全称是 Least Frequently Used 翻译为最不常用的,最不常用的算法是根据总访问次数来淘汰数据的,它的焦点思想是“假如数据过去被访问多次,那么将来被访问的频率也更高”
主从同步

主从模式(Master-Slave)是Redis实现读写分离和提高容灾本领的基础。一个主节点(Master)可以有多个从节点(Slave)。

[*] 特点

[*]读写分离:主节点负责写入,从节点负责读取,减轻主节点的读压力。
[*]故障转移:假如主节点故障,可以手动或通过其他工具将从节点提升为主节点。
[*]数据同步:从节点通过复制(Replication)获取主节点数据。

[*] 配置主从

[*] 主节点,无需额外配置
[*] 从节点,可以修改redis.conf文件,添加设置从节点连接主节点replicaof <master_ip> <master_port>和配置主节点暗码masterauth <master_password>,进行长期化配置。
也可以在redis-cli适用命令进行配置,如下
127.0.0.1:6379> replicaof 192.168.7.130 6379
OK
127.0.0.1:6379> master-slave-config set masterauth 123456
OK


[*] 功能测试
首先我们先在主服务器上实行保存数据操作,再去从服务器查询,从服务器只能查询数据
https://i-blog.csdnimg.cn/direct/8fc9ec8a94c1477f94296d6d7f10ed9c.png
[*] 主从数据同步
新的从服务器连接时,为了保障多个数据库的同等性,主服务器会实行一次 bgsave 命令生成一个 RDB 文件,然后再以 Socket 的方式发送给从服务器,从
服务器收到 RDB 文件之后再把全部的数据加载到自己的步伐中,就完成了一次全量的数据同步。
https://i-blog.csdnimg.cn/direct/c43f0b41339c47bc87e9804ab8cb0b55.png
主从数据同步,还会出现:主从库间网络断了怎么办?主从同步的细节。主从之间的buffer等等,临时作为遗留问题 todo
[*] 查询服务器脚色
使用 role 命令,来查询当前服务器的主从脚色信息
https://i-blog.csdnimg.cn/direct/487cdfe9b8b749f4bd550e0d63e0b2e1.png
关闭主从同步,使用 replicaof no one 命令来制止从服务器的复制
Redis 哨兵

哨兵是做什么的

在Redis的主从架构中,由于主从模式时读写分离的,假如主节点挂了,那么没有主节点来服务客户端的写操作,也没有主节点给从节点进行数据同步。
哨兵的作用是实现主从节点故障转移。它会检测主节点是否存活,假如发现主节点挂了,他就会选举一个从节点切换位主节点。
哨兵的工作机制

哨兵是一个运行在特殊模式下的Redis进程,所以它是一个节点。哨兵重要负责三件事:监控、选主、关照,哨兵是以集群的方式存在的,降低偏差,最好是技能个哨兵(3,5,7)

[*] 监控主节点,判断主节点是否故障
哨兵会每隔1秒给全部主从节点发送PING命令,当主从节点收到PING命令后,会发送一个相应给哨兵,如许判断出聪是否在正常运行。
https://i-blog.csdnimg.cn/direct/90ce93cf4846449480cf2a4e075bda3c.png
主从节点没有在规定的时间(down-after-milliseconds)内相应哨兵,哨兵就会将其标记位主观下线(SDOWN),单个哨兵的判断大概会误判,通过多个哨兵节点一起判断,就可以降低误判的情况。
当一个哨兵判读主节点为主观下线,就会向其他哨兵发起判断主节点是否下线的命令,当其他哨兵认同主观下线的数目凌驾设置的quorum时,主节点就会判定为客观下线。
哨兵判断主节点客观下线后,哨兵要在多个从节点中,选出一个从节点来做新的主节点。
[*] 选举哨兵进行主从故障转移
主从故障转移是由一个被选举为“领导者”(Leader)的哨兵负责实行的。这种机制通过哨兵间的选举流程确定具体负责实行故障转移的哨兵。当哨兵检测到主节点进入客观下线(ODOWN)状态后,任何哨兵都可以成为“候选人”。候选人会主动投票给自己,同时发起投票请求,争取其他哨兵的支持。每个哨兵只能在每轮选举中投票一次。
当满足:

[*]候选人得到凌驾半数哨兵实例(N/2 + 1)的支持票
就可以成为主从故障转移的领导者

哨兵主从故障迁移


[*] 选择新的主节点,新主节点满足以下条件:

[*]节点必须是在线状态
[*]复制优先级(replica-priority)值越高,优先级越高,主节点的选择概率高
[*]复制偏移量(数据同步状态),优先级相同的条件下,会选择复制偏移量接近主节点的从节点
[*]从节点ID,上述条件均相同,则根据从节点的Run ID,选择ID最小的节点
将选定的从节点提升为新的主节点,Sentinel 会向该从节点发送 SLAVEOF NO ONE 命令,使其成为主节点

[*] 重新配置其他从节点
将剩余的从节点重新配置为附属于新的主节点,Sentinel 会向这些从节点发送 SLAVEOF <new-master-ip> <new-master-port> 命令
[*] 将旧主节点重新配置为从节点
假如故障的旧主节点重新上线,Sentinel 会将其重新配置为新主节点的从节点,
Sentinel 向旧主节点发送 SLAVEOF <new-master-ip> <new-master-port> 命令
[*] 关照客户端更新主节点地址
Sentinel 将通过 Pub/Sub 关照机制关照客户端新的主节点地址todo
[*] 故障转移流程图
Step 1: 主节点发生故障
         ↓
Step 2: 哨兵检测到主节点 SDOWN
         ↓
Step 3: 多数哨兵确认主节点 ODOWN
         ↓
Step 4: 哨兵间选举出 Leader 哨兵
         ↓
Step 5: Leader 哨兵选定一个从节点作为新主节点
         ↓
Step 6: Leader 哨兵完成主从切换,并通知集群

遗留问题todo
从节点升为主节点,新的主节点数据和前主节点数据不完全同步,该怎么办?
在故障迁移时,有数据写入,该怎么办?
实操


[*] 项目目录
.
├── docker-compose.yml          # docker-compose 配置文件
├── redis-0                     # redis-0 配置目录
│   ├── data                  # redis-0 数据存储目录
│   └── config                  # redis-0 配置文件目录
│       └── redis.conf          # redis 配置文件
├── redis-1                     # redis-1 配置目录
│   ├── data                  # redis-1 数据存储目录
│   └── config                  # redis-1 配置文件目录
│       └── redis.conf          # redis 配置文件
├── redis-2                     # redis-2 配置目录
│   ├── data                  # redis-2 数据存储目录
│   └── config                  # redis-2 配置文件目录
│       └── redis.conf          # redis 配置文件
├── sentinel1                   # sentinel-1 配置目录
│   ├── data                  # sentinel-1 数据存储目录
│   └── config                  # sentinel-1 配置文件目录
│       └── sentinel.conf       # sentinel 配置文件
├── sentinel2                   # sentinel-2 配置目录
│   ├── data                  # sentinel-2 数据存储目录
│   └── config                  # sentinel-2 配置文件目录
│       └── sentinel.conf       # sentinel 配置文件
├── sentinel3                   # sentinel-3 配置目录
│   ├── data                  # sentinel-3 数据存储目录
│   └── config                  # sentinel-3 配置文件目录
│       └── sentinel.conf       # sentinel 配置文件

[*] redis.conf redis基础配置,注意事项

[*]主节点需要开启长期化配置,RDB-AOF混淆长期化
[*]若需要设置暗码,主从库暗码要同等
[*]从库需要配置replicaof 172.30.1.2 6379从节点连接主节点的地址和端口,以及主节点的暗码masterauth 123456

[*] sentinel.conf 哨兵配置,注意事项

[*]必须设置监控的主节点sentinel monitor <ip> <port> <quorum>
[*]哨兵在这个时间内未收到主节点的相应,则认为主节点不可用sentinel down-after-milliseconds <master-name> 5000

[*] docker-compose.yaml 文件,详情请见配置文件,注意事项如下

[*]假如在同一台机器上,使用docker做测试,需要新建一个网络通信
[*]将配置文件的目录挂载至容器,而非文件,哨兵需要对文件修改,需要得到目录和文件的权限

[*] 测试

[*]在目的目录下,使用docker-compose up -d启动主从Redis和sentinel集群,进入容器,连接Redis
https://i-blog.csdnimg.cn/img_convert/a1c9f3931dc9bab5f146577842c65d6f.png
   

[*]使用docker stop redis-0制止主库,主库挂了,sentinel重新开始设置主库
https://i-blog.csdnimg.cn/img_convert/b84f94bd6943f9cedb956980535d43ba.png
   

[*]使用docker start redis-0,前主库恢复,主库变从库
https://i-blog.csdnimg.cn/img_convert/e9040d3f04169c0ba2bc08170890775e.png
   

[*]sentinel 日记分析
1:X 28 Nov 2024 14:46:04.988 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:X 28 Nov 2024 14:46:04.989 * Redis version=7.4.1, bits=64, commit=00000000, modified=0, pid=1, just started
1:X 28 Nov 2024 14:46:04.990 * Configuration loaded         # 加载配置文件
1:X 28 Nov 2024 14:46:04.991 * monotonic clock: POSIX clock_gettime   # Redis 使用单调 POSIX 时钟来测量时间,以保证时间戳的精确性
1:X 28 Nov 2024 14:46:04.993 * Running mode=sentinel, port=26379.
1:X 28 Nov 2024 14:46:05.000 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:46:05.000 * Sentinel ID is 64d59cb8360bb0deaffb4e178f3f2d28b0cc9b3a      # Sentinel 的唯一标识符
1:X 28 Nov 2024 14:46:05.001 # +monitor master mymaster 172.30.1.2 6379 quorum 2    # 主库 ip 端口,quorum 客观下线 数量
1:X 28 Nov 2024 14:46:05.003 * +slave slave 172.30.1.4:6379 172.30.1.4 6379 @ mymaster 172.30.1.2 6379# 从节点 172.30.1.4:6379 被检测到并注册为主节点 mymaster 的从节点
1:X 28 Nov 2024 14:46:05.007 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:46:05.008 * +slave slave 172.30.1.3:6379 172.30.1.3 6379 @ mymaster 172.30.1.2 6379# 从节点 172.30.1.3:6379 被检测到并注册为主节点 mymaster 的从节点
1:X 28 Nov 2024 14:46:05.013 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:46:07.015 * +sentinel sentinel 4a4da7d251e2cad526a3f69d21f7c44bfd9027db 172.30.1.6 26379 @ mymaster 172.30.1.2 6379# 新的哨兵加入,监控主节点
1:X 28 Nov 2024 14:46:07.021 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:46:07.023 * +sentinel sentinel 171327d57c7fc1e1c2fd8fde9726ea9cd1b21628 172.30.1.7 26379 @ mymaster 172.30.1.2 6379# 新的哨兵加入,监控主节点
1:X 28 Nov 2024 14:46:07.030 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:55:26.245 # +sdown master mymaster 172.30.1.2 6379   # 主库,主观下线
1:X 28 Nov 2024 14:55:26.325 # +odown master mymaster 172.30.1.2 6379 #quorum 2/2   # 确认主库客观下线
1:X 28 Nov 2024 14:55:26.326 # +new-epoch 1 # 开启了一个新的纪元(epoch),用于协调故障转移
1:X 28 Nov 2024 14:55:26.328 # +try-failover master mymaster 172.30.1.2 6379    # Sentinel 开始对主节点进行故障转移
1:X 28 Nov 2024 14:55:26.333 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:55:26.333 # +vote-for-leader 64d59cb8360bb0deaffb4e178f3f2d28b0cc9b3a 1      # 投票给自己,当领导
1:X 28 Nov 2024 14:55:26.345 * 4a4da7d251e2cad526a3f69d21f7c44bfd9027db voted for 64d59cb8360bb0deaffb4e178f3f2d28b0cc9b3a 1    # 其他哨兵投票
1:X 28 Nov 2024 14:55:26.347 * 171327d57c7fc1e1c2fd8fde9726ea9cd1b21628 voted for 64d59cb8360bb0deaffb4e178f3f2d28b0cc9b3a 1
1:X 28 Nov 2024 14:55:26.412 # +elected-leader master mymaster 172.30.1.2 6379# 选举完成,当前 Sentinel 成为领导者
1:X 28 Nov 2024 14:55:26.413 # +failover-state-select-slave master mymaster 172.30.1.2 6379   # 领导者开始选择新的主节点
1:X 28 Nov 2024 14:55:26.468 # +selected-slave slave 172.30.1.3:6379 172.30.1.3 6379 @ mymaster 172.30.1.2 6379   # 从节点 172.30.1.3:6379 被选为新的主节点
1:X 28 Nov 2024 14:55:26.470 * +failover-state-send-slaveof-noone slave 172.30.1.3:6379 172.30.1.3 6379 @ mymaster 172.30.1.2 6379      # 向选定的新主服务器发送命令使其不再成为任何其他服务器的从属
1:X 28 Nov 2024 14:55:26.539 * +failover-state-wait-promotion slave 172.30.1.3:6379 172.30.1.3 6379 @ mymaster 172.30.1.2 6379      # 等待新主服务器的晋升完成
1:X 28 Nov 2024 14:55:26.604 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:55:26.605 # +promoted-slave slave 172.30.1.3:6379 172.30.1.3 6379 @ mymaster 172.30.1.2 6379   # 从服务器172.30.1.3已被提升为新的主服务器
1:X 28 Nov 2024 14:55:26.605 # +failover-state-reconf-slaves master mymaster 172.30.1.2 6379      # 开始重新配置剩余的从服务器以跟随新的主服务器
1:X 28 Nov 2024 14:55:26.700 * +slave-reconf-sent slave 172.30.1.4:6379 172.30.1.4 6379 @ mymaster 172.30.1.2 6379      # 向从服务器172.30.1.4发送重新配置指令
1:X 28 Nov 2024 14:55:27.455 # -odown master mymaster 172.30.1.2 6379         # 原来的主服务器172.30.1.2现在被认为是可用的(不再odown
1:X 28 Nov 2024 14:55:27.669 * +slave-reconf-inprog slave 172.30.1.4:6379 172.30.1.4 6379 @ mymaster 172.30.1.2 6379      # 从服务器172.30.1.4的重新配置正在进行中
1:X 28 Nov 2024 14:55:27.671 * +slave-reconf-done slave 172.30.1.4:6379 172.30.1.4 6379 @ mymaster 172.30.1.2 6379      # 从服务器172.30.1.4的重新配置已完成
1:X 28 Nov 2024 14:55:27.723 # +failover-end master mymaster 172.30.1.2 6379            # 故障转移结束
1:X 28 Nov 2024 14:55:27.725 # +switch-master mymaster 172.30.1.2 6379 172.30.1.3 6379      # 主服务器已切换至172.30.1.3
1:X 28 Nov 2024 14:55:27.726 * +slave slave 172.30.1.4:6379 172.30.1.4 6379 @ mymaster 172.30.1.3 6379# 从服务器172.30.1.4现在跟随新的主服务器172.30.1.3
1:X 28 Nov 2024 14:55:27.727 * +slave slave 172.30.1.2:6379 172.30.1.2 6379 @ mymaster 172.30.1.3 6379# 原来的主服务器172.30.1.2现在也是从服务器,跟随新的主服务器172.30.1.3
1:X 28 Nov 2024 14:55:27.732 * Sentinel new configuration saved on disk
1:X 28 Nov 2024 14:55:32.787 # +sdown slave 172.30.1.2:6379 172.30.1.2 6379 @ mymaster 172.30.1.3 6379# 原来的主服务器172.30.1.2(现在作为从服务器)被认为主观下线
1:X 28 Nov 2024 14:57:23.806 # -sdown slave 172.30.1.2:6379 172.30.1.2 6379 @ mymaster 172.30.1.3 6379 # 原来的主服务器172.30.1.2(现在作为从服务器)再次被认为是可用的(不再sdown)

Redis 集群

Redis scales horizontally with a deployment topology called Redis Cluster.
Redis采用集群的重要目的是为了办理单机性能和容量的限制问题,增长了高可用性
集群模式


[*] Redis Cluster
Redis 自带的原生集群模式,支持数据分片
特点:

[*]分片算法:由客户端决定分片规则,通常采用同等性哈希
[*]机动性搞:客户端完全掌控分片逻辑
[*]无高可用支持:需要客户端额外管理主从关系和故障管理
使用场景:
   

[*]数据分布计谋需要高度定制化
[*]不需要复杂的高可用管理,或已有外部的高可用方案
数据分片(data sharding)
Redis集群不使用同等哈希,而是使用一种差别情势的分片,其中每个键在概念上都是我们称之为哈希槽的一部分。
Redis集群中有16384个哈希槽,为了计算给定键的哈希槽,我们只需将键进行CRC16哈希然后对16384取模。
Redis集群中的每个节点负责哈希槽的一个子集,例如,有一个有3个节点(A,B,C)的集群,其中:
   

[*]节点A包含0 ~ 5500个哈希槽
[*]节点B包含从5501到11000的哈希槽
[*]节点C包含从11001到16383的哈希槽
集群端口,集群的每一个节点需要打开两个端口,一个用于为客户端提供服务的端口,另一个为集群的总线端口,默认情况下,集群总线端口是10000+客户端端口。可以通过cluster-port设置
Redis Cluster 主从模子,为了保证节点发送故障后节点通信是保持可用,Redis Cluster 使用主从的模式,每一个主节点都有多个副节点。
三个主节点三个从节点(A,B,C,A1,B1,C1),假如主节点B发送故障,集群将提升节点B1为新的主节点继承正常运行,假如B和B1同时发生故障,则Redis Cluster将无法继承运行。
Redis集群不能保证强同等性,集群大概会丢失客户端的确认写入

[*] Codis
Codis 是由国人开发的开源Redis分布式代理办理方案,旨在帮助用户管理多个Redis实例,不在更新
特点:

[*]隐藏了分片逻辑,客户端只需要连接Proxy,不需要感知底层的分布式布局
[*]支持横向扩展,多个Proxy可共同分担流量
[*]支持高可用,Proxy故障时,客户端可以切换到其他Proxy
使用场景:
   

[*]数据规模大
[*]请求并发量高

Redis Cluster 与 Codis 的对比
特性Redis ClusterCodis分片实现数据分布到16384个哈希槽,有节点负责基于 Proxy,由 Codis 管理分片规则客户端透明性需要支持Cluster协议的客户端完全透明,客户端无需感知动态扩容数据迁移较复杂,需手动操作支持在线迁移分片高可用性原生支持高可用,自主选举主节点依靠 Redis 主从复制和 Proxy 故障切换使用复杂度相对简朴,原生功能相对复杂,需要运维 ZooKeeper/Etcd场景适用性新体系开发,支持Redis Cluster协议恰当老体系改造,无需更改业务代码 Cluster 实操


[*] 集群目录
.
├── 7000
│   ├── conf
│   │   └── redis.conf
│   └── data

├── 7001
│   ├── conf
│   │   └── redis.conf
│   └── data

├── 7002
│   ├── conf
│   │   └── redis.conf
│   └── data

├── 7003
│   ├── conf
│   │   └── redis.conf
│   └── data

├── 7004
│   ├── conf
│   │   └── redis.conf
│   └── data

├── 7005
│   ├── conf
│   │   └── redis.conf
│   └── data

└── docker-compose.yaml

[*] redis.conf redis基础配置,注意事项

[*]cluster-enabled yes 启用 Redis 的集群模式
[*]cluster-config-file /data/nodes.conf 指定存储 Redis 集群状态的配置文件路径
[*]cluster-node-timeout 5000 设置 Redis 集群节点间的超时时间
[*]cluster-require-full-coverage yes 默认yes,某主从节点都挂掉了,整个集群都无法提供服务

[*] docker-compose.yaml
这次实操,是在一个服务器上启动了 6 个节点,并且在同一主机使用docker摆设,直接使用的是主机网络,避免出现网络问题
[*] 创建集群
# 先连接任意一个节点
docker exec -it redis-0 /bin/bash
# 创建一个新的集群 --cluster-replicas 1 每一个主节点配置一个从节点
redis-cli --cluster create 192.168.3.222:7000 192.168.3.222:7001 \
192.168.3.222:7002 192.168.3.222:7003 192.168.3.222:7004 192.168.3.222:7005 \
--cluster-replicas 1

[*] 日记分析
>>> Performing hash slots allocation on 6 nodes...    # 给三个主库分配哈希槽
Master -> Slots 0 - 5460
Master -> Slots 5461 - 10922
Master -> Slots 10923 - 16383
Adding replica 192.168.3.222:7004 to 192.168.3.222:7000         # 给每一个主节点分配一个从节点
Adding replica 192.168.3.222:7005 to 192.168.3.222:7001
Adding replica 192.168.3.222:7003 to 192.168.3.222:7002
>>> Trying to optimize slaves allocation for anti-affinity          # 避免亲和性(anti-affinity),尽量不在同一台物理机上放置主节点及其对应的从节点,下面发出警告
Some slaves are in the same host as their master
M: d886b043376653f642bbe5c45149a2f77e83b7aa 192.168.3.222:7000      # 三个主节点的唯一id,以及负责哈希槽的范围
   slots: (5461 slots) master
M: c2aa2c6a1aea895b17c80e470cc8a3805f257672 192.168.3.222:7001
   slots: (5462 slots) master
M: 41c5e7238fdf873506ba32bd21f26c6aeffdd3d1 192.168.3.222:7002
   slots: (5461 slots) master
S: 950f242794e4b26afe5e2b82d0198c79ff32ef2f 192.168.3.222:7003      # 三个从节点的信息,以及他们的主节点
   replicates c2aa2c6a1aea895b17c80e470cc8a3805f257672
S: 01212bd1dc1bed4bccc0fea21ad5dcbfe384fa5b 192.168.3.222:7004
   replicates 41c5e7238fdf873506ba32bd21f26c6aeffdd3d1
S: 25940bbff999b1dd1f143f8af5fddf9dd65a285e 192.168.3.222:7005
   replicates d886b043376653f642bbe5c45149a2f77e83b7aa
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 192.168.3.222:7000)      # 等待所有节点加入集群
M: d886b043376653f642bbe5c45149a2f77e83b7aa 192.168.3.222:7000
   slots: (5461 slots) master
   1 additional replica(s)
M: 41c5e7238fdf873506ba32bd21f26c6aeffdd3d1 192.168.3.222:7002
   slots: (5461 slots) master
   1 additional replica(s)
S: 25940bbff999b1dd1f143f8af5fddf9dd65a285e 192.168.3.222:7005
   slots: (0 slots) slave
   replicates d886b043376653f642bbe5c45149a2f77e83b7aa
S: 01212bd1dc1bed4bccc0fea21ad5dcbfe384fa5b 192.168.3.222:7004
   slots: (0 slots) slave
   replicates 41c5e7238fdf873506ba32bd21f26c6aeffdd3d1
M: c2aa2c6a1aea895b17c80e470cc8a3805f257672 192.168.3.222:7001
   slots: (5462 slots) master
   1 additional replica(s)
S: 950f242794e4b26afe5e2b82d0198c79ff32ef2f 192.168.3.222:7003
   slots: (0 slots) slave
   replicates c2aa2c6a1aea895b17c80e470cc8a3805f257672
All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
All 16384 slots covered.
nodes.conf,中重要记载着集群的主从,以及其分配的哈希槽
# 格式 提供了每个节点的状态、角色、连接情况以及其他相关信息
<node_id> <ip:port>@<cluster_bus_port>,<optional_fields> <flags> <master_id> <last_ping_sent> <last_pong_rcvd> <config_epoch> <link_state> <slots>
# 详细如下
41c5e7238fdf873506ba32bd21f26c6aeffdd3d1 192.168.3.222:7002@17002,,tls-port=0,shard-id=895b2a0394527b0cd73aa5434be44130a1fc3254 master - 0 1733302987000 3 connected 10923-16383
d886b043376653f642bbe5c45149a2f77e83b7aa 192.168.3.222:7000@17000,,tls-port=0,shard-id=dfb8b8992365af8b487dc1f3078b5c07c70d8379 myself,master - 0 0 1 connected 0-5460
25940bbff999b1dd1f143f8af5fddf9dd65a285e 192.168.3.222:7005@17005,,tls-port=0,shard-id=dfb8b8992365af8b487dc1f3078b5c07c70d8379 slave d886b043376653f642bbe5c45149a2f77e83b7aa 0 1733302987520 1 connected
01212bd1dc1bed4bccc0fea21ad5dcbfe384fa5b 192.168.3.222:7004@17004,,tls-port=0,shard-id=895b2a0394527b0cd73aa5434be44130a1fc3254 slave 41c5e7238fdf873506ba32bd21f26c6aeffdd3d1 0 1733302987000 3 connected
c2aa2c6a1aea895b17c80e470cc8a3805f257672 192.168.3.222:7001@17001,,tls-port=0,shard-id=426b50aaddaa31109ca57eb0b50853bbe58fd9c7 master - 0 1733302987520 2 connected 5461-10922
950f242794e4b26afe5e2b82d0198c79ff32ef2f 192.168.3.222:7003@17003,,tls-port=0,shard-id=426b50aaddaa31109ca57eb0b50853bbe58fd9c7 slave c2aa2c6a1aea895b17c80e470cc8a3805f257672 0 1733302987722 2 connected
vars currentEpoch 6 lastVoteEpoch 0
连接和使用
root@localhost:/data# redis-cli -c -p 7000
127.0.0.1:7000> set foo bar
-> Redirected to slot located at 192.168.3.222:7002
OK
192.168.3.222:7002> set name wang
-> Redirected to slot located at 192.168.3.222:7001
OK
192.168.3.222:7001> get name
"wang"
192.168.3.222:7001> get foo
-> Redirected to slot located at 192.168.3.222:7002
"bar"
192.168.3.222:7002>

[*] 对集群重新分片
具体文章请查阅Redis官方文档
重新分片可以主动实行,无需以交互方式手动输入参数
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes

[*]<host>:<port> 指定一个集群中的任意节点地址和端口,这个节点将被用来连接到集群并发起重新分片操作
[*]--cluster-from <node-id> 指定源节点的 ID,从此节点中移出哈希槽的主节点 ID
[*]--cluster-to <node-id> 指定目的节点的 ID,将哈希槽移入的目的主节点 ID
[*]--cluster-slots <number of slots> 指定要移动的哈希槽数目,这个值必须在1到16384之间
[*]--cluster-yes 主动确认全部提示,这使得命令可以在没有效户交互的情况下完成整个重新分片过程

Redis 性能测试

对Redis进行性能测试,评估Redis缓存能不能顶住体系的吞吐量,对Redis的性能进行测试,有两种测试方式:


[*]编写脚本模拟并发测试
[*]使用redis-benchmark进行测试,Redis自带的性能测试工具
如今的程度有限,也不是专攻Redis中间件,就不编写脚本实现测试todo,学习和记载一下redis-benchmark工具,官方原文
redis-benchmark -h查看redis最常用的命令
Usage: redis-benchmark

Options:
-h <hostname>      Server hostname (default 127.0.0.1)
-p <port>          Server port (default 6379)
-s <socket>      Server socket (overrides host and port)
-a <password>      Password for Redis Auth
--user <username>Used to send ACL style 'AUTH username pass'. Needs -a.
-u <uri>         Server URI on format redis://user:password@host:port/dbnum
                  User, password and dbnum are optional. For authentication
                  without a username, use username 'default'. For TLS, use
                  the scheme 'rediss'.
-c <clients>       Number of parallel connections (default 50).
                  Note: If --cluster is used then number of clients has to be
                  the same or higher than the number of nodes.
-n <requests>      Total number of requests (default 100000)
-d <size>          Data size of SET/GET value in bytes (default 3)
--dbnum <db>       SELECT the specified db number (default 0)
-3               Start session in RESP3 protocol mode.
--threads <num>    Enable multi-thread mode.
--cluster          Enable cluster mode.
                  If the command is supplied on the command line in cluster
                  mode, the key must contain "{tag}". Otherwise, the
                  command will not be sent to the right cluster node.
--enable-trackingSend CLIENT TRACKING on before starting benchmark.
-k <boolean>       1=keep alive 0=reconnect (default 1)
-r <keyspacelen>   Use random keys for SET/GET/INCR, random values for SADD,
                  random members and scores for ZADD.
                  Using this option the benchmark will expand the string
                  __rand_int__ inside an argument with a 12 digits number in
                  the specified range from 0 to keyspacelen-1. The
                  substitution changes every time a command is executed.
                  Default tests use this to hit random keys in the specified
                  range.
                  Note: If -r is omitted, all commands in a benchmark will
                  use the same key.
-P <numreq>      Pipeline <numreq> requests. Default 1 (no pipeline).
-q               Quiet. Just show query/sec values
--precision      Number of decimal places to display in latency output (default 0)
--csv            Output in CSV format
-l               Loop. Run the tests forever
-t <tests>         Only run the comma separated list of tests. The test
                  names are the same as the ones produced as output.
                  The -t option is ignored if a specific command is supplied
                  on the command line.
-I               Idle mode. Just open N idle connections and wait.
-x               Read last argument from STDIN.


[*]-c <clients>指定并发的连接数目,默认50
[*]-n <requests>发出的请求总数,默认100000
[*]-d <size> 对于 SET/GET 操作的数据巨细(单位:字节),默认是 3 字节
[*]--dbnum <db>选择使用的数据库编号,默认是 0
[*]--threads <num>启用多线程模式,并指定使用的线程数
[*]--cluster启用集群模式
[*]-k <boolean>是否保持长连接(keep alive),默认 1 表示保持,0 表示每次请求后断开并重新连接
[*]-r <keyspacelen>用于生成随机键的空间长度。如 -r 1000,那么键将从 0 到 999 中随机选取
[*]-P <numreq>通过管道一次发送的请求数目,默认是 1
[*]-q静默测试,只显示 QPS 的值
[*]--precision设定输出耽误结果的小数点精度,默认是 0,意味着不保存小数部分
[*]--csv将测试结果输出为 CSV 格式的文件
[*]-l循环测试
[*]-t <tests>仅运行逗号分隔的测试列表
[*]-I空闲模式
redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000 -d 256 -t set,get -r 100 -a 123456
====== SET ======                                                   
100000 requests completed in 1.59 seconds
50 parallel clients
256 bytes payload
keep alive: 1
host configuration "save": 60 3
host configuration "appendonly": yes
multi-thread: no
......
Summary:
throughput summary: 62932.66 requests per second
latency summary (msec):
avg       min       p50       p95       p99       max
0.709   0.160   0.695   0.943   1.135   8.031

====== GET ======                                                   
100000 requests completed in 0.89 seconds
50 parallel clients
256 bytes payload
keep alive: 1
host configuration "save": 60 3
host configuration "appendonly": yes
multi-thread: no
Summary:
throughput summary: 111731.84 requests per second
latency summary (msec):
avg       min       p50       p95       p99       max
0.236   0.096   0.231   0.327   0.407   0.863
SETGET吞吐量62,932.66 请求/秒111,731.84 请求/秒均匀耽误 (avg)0.709 ms0.236 ms最小耽误 (min)0.160 ms0.096 ms中位数耽误 (p50)0.695 ms0.231 ms95 百分位耽误 (p95)0.943 ms0.327 ms99 百分位耽误 (p99)1.135 ms0.407 ms最大耽误 (max)8.031 ms0.863 ms Redis 常见问题

缓存中的数据和数据库差别等

数据的同等性
a. 缓存中有数据,那么,缓存的数据值需要和数据库中的值相同
b. 缓存中自己没有数据,那么,数据库中的值必须是最新值
办理措施:

[*] 先删除缓存,再更新数据库
大概会出现的问题,假设线程 A 删除缓存值后,还没有来得及更新数据库(好比说有网络耽误),线程 B 就开始读取数据了,那么这个时候,线程 B 会发现缓存缺失,就只能去数据库读取。这会带来两个问题:
a. 线程 B 读取到了旧值;
b. 线程 B 是在缓存缺失的情况下读取的数据库,所以,它还会把旧值写入缓存,这大概会导致其他线程从缓存中读到旧值。
https://i-blog.csdnimg.cn/img_convert/c9533031ec552dbd7131c2e38a46ba0a.png
在线程 A 更新完数据库值以后,我们可以让它先 sleep 一小段时间,再进行一次缓存删除操作。
[*] 先更新数据库值,再删除缓存值
大概会出现的问题,假如线程 A 删除了数据库中的值,但还没来得及删除缓存值,线程 B 就开始读取数据了,那么此时,线程 B 查询缓存时,发现缓存掷中,就会直接从缓存中读取旧值。
不过,在这种情况下,假如其他线程并发读缓存的请求不多,那么,就不会有许多请求读取到旧值。而且,线程 A 一样平常也会很快删除缓存值,如许一来,其他线程再次读取时,就会发生缓存缺失,进而从数据库中读取最新值。所以,这种情况对业务的影响较小。
https://i-blog.csdnimg.cn/direct/52906fe4afa54bd48dfbe86ffcc94f76.png
在大多数业务场景下,把 Redis 作为只读缓存使用。针对只读缓存来说,我可以先删除缓存值再更新数据库,也可以先更新数据库再删除缓存。发起是,优先使用先更新数据库再删除缓存的方法,原因重要有两个:


[*]先删除缓存值再更新数据库,有大概导致请求因缓存缺失而访问数据库,给数据库带来压力;
[*]假如业务应用中读取数据库和写缓存的时间不好估算,那么,耽误双删中的等候时间就不好设置。
不过,当使用先更新数据库再删除缓存时,也有个地方需要注意,假如业务层要求必须读取同等的数据,那么,我们就需要在更新数据库时,先在 Redis 缓存客户端暂存并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据同等性。
缓存雪崩

大量数据不在缓存中,大量请求发送到数据库层,导致数据库层的压力激增,出现原因:

[*] 大量数据同事逾期,办理措施

[*]避免给大量数据设置相同的逾期时间
[*]服务降级,指发生缓存雪崩时,针对差别的数据采取差别的处置处罚

[*] Redis突然宕机

[*]在业务体系中实现服务熔断或请求限流机制
[*]事前预防,构建可靠集群

缓存击穿

热点数据请求,没有在缓存中进行处置处罚
办理措施:超长逾期时间,或者不设置逾期时间
缓存穿透

访问的数据既不在 Redis 缓存中,也不在数据库中,导致请求在访问缓存时,发生缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据
出现的两种情况:


[*]业务层误操作
[*]恶意攻击
办理措施


[*]缓存空值
[*]使用布隆过滤器快速判断数据是否存在,避免从数据库中查询数据是否存在,减轻数据库压力
[*]前端进行请求检测,黑名单
https://i-blog.csdnimg.cn/direct/e67a2f6a0bf043b9a482faa02e5a1a5a.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Redis 三种摆设方式实践