Redis概念:
Redis是完全开源的,使用ANSIC语言编写遵守BSD协议,是一个高性能的Key-Value数据库提供了丰富的数据结构,例如String、Hash、List、Set、SortedSet等等。数据是存在内存中的,同时Redis支持事务、持久化、LUA脚本、发布/订阅、缓存淘汰、流技术等多种功能特性提供了主从模式、Redis Sentinel和Redis Cluster集群架构方案
常用于后端程序和数据库之间做缓存,用于弥补DB和后端程序之间的性能差距,DB吞吐跟不上系统并发的时候,避免请求直接进入DB而起到保护作用。
除了做DB缓存还可以做很多功能,例如,计数器、用户在线状态、排行榜、session存储、同时redis性能客观,官方给出10w/s的QPS,实际读7-9w/s,写入6-8w/s。
官网:https://redis.io/
Redis为什么这么快?
- Redis完全基于内存
- 整体类似于HashMap,查询复杂度为O(1),不需要随机IO或全表查询
- Redis对于客户端处理时单线程的,不用上下文切换。
- 采用了select/epoll多路复用的高效非阻塞IO模型
- 客户端通信采用RESP,简单一度,避免了复杂请求的开销
常用Key命令:
命令不区分大小写,但是key区分大小写,help @类型,获得帮助文档
命令 | 作用 |
---|---|
keys * | 查看所有key |
dbsize | 查看数据库key的数量 |
exists key | 判断某key是否存在 |
del key | 删除key |
ttl key | 查看多久过期 |
expire key seconds | |
expireat key timestamp | |
unlink key | 非阻塞删除,后续异步删除 |
select dbindex | 切换数据库,默认有16个数据库,默认0号库 |
move key dbindex | 数据库key的迁移 |
flushdb | 清空当前库 |
flushall | 清空所有库 |
数据类型:
数据类型值得是K-V中的V的数据类型,K的数据类型都是string。
类型 | 基本特点 |
---|---|
string字符串 | 最基本的数据类型,二进制安全,可存储图片视频 |
hash哈希表 | 是一个字段和值的映射表,适合用于存储对象 |
list列表 | 是简单的字符串列表,底层双端队列,头尾可添加数据 |
set集合 | 无序集合,不会出现重复数据,用哈希表实现 |
zset有序集合 | 每个元素有一个double类型的分数,成员唯一 |
GEO地理空间 | 主要用于存储地理位置信息,并对存储的信息进行操作 |
HyperLogLog基数统计 | 做基数统计的算法 |
bitmap位图 | 由0和1状态表现的二进制位的bit数组 |
bitfield位域 | 可以一次性操作多个比特位域(指的是连续的多个比特位) |
Stream流 | 主要用于消息队列 |
这些数据类型的相关命令:https://redis.io/commands/
中文版本:http://www.redis.cn/commands.html
*数据类型底层细节:
新手可以跳过该部分,但这里主要讲解数据类型的相关实现原理,是很重要的部分,面试必考。
string:
value不能超过512M。
通过使用SDS的方式分配空间,能够进行预分配的方式减少后期的内存分配。
- SDS 不仅可以保存文本数据,还可以保存二进制数据
- SDS 获取字符串长度的时间复杂度是 O(1)。
- Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出。
字符串对象的内部编码(encoding)有 3 种 :int、raw和 embstr
应用场景
- 缓存对象:
直接缓存整个对象的 JSON,命令例子: SET user:1 ‘{“name”:“xiaolin”, “age”:18}’。
采用将 key 进行分离为 user:ID:属性,采用 MSET 存储,用 MGET 获取各属性值,命令例子: MSET user:1:name xiaolin user:1:age 18 user:2:name xiaomei user:2:age 20
- **常规计数:**比如点赞
- 分布式锁:setnx+lua脚本。
SET lock_key unique_value NX PX 10000
lock_key 就是 key 键;
unique_value 是客户端生成的唯一的标识;解锁的时候需要使用
NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。
- 共享session信息:
- 限流:
防止用户对网站进行一些不合理的行为,假如有100w请求,只允许50w请求。通过设置时间戳为key,然后value设置为已经访问的数量n,大于限流条件N后就不会往后端发送请求。使用数据结构string
list
lpush,rpush,lrange。没有rrange。 lpop,rpop。 llen 。。。。
一个key,多个value。可以在左右进行操作。
通过双向链表+ziplist的方式进行存储:字节跳动培训说是通过双向链表和listpack的方式进行存储。
- quicklist 实际上是ziplist和linkedList的混合体,它将linkedList 按段切分,每一段使用ziplist 来紧凑存储,多个zipList之间使用双向指针串接起来。
应用场景:
- 微信公众号订阅消息
hash:
底层:redis6:ziplist/hashtable,redis7:listpack/hashtable
hset key fied1 val1 filed2 val2
应用场景:
- 存储对象:
- 购物车:
set:
命令:sadd , smembers,srem
重点:集合运算(常用在社交网络中),sdiff差集, sunion并集,sinter交集
sintercard去重统计数。
- 抽奖程序,
- 微信朋友圈点赞:
- QQ内推可能认识的人:
zset:
就是在set前面加了一个分数。
zset有两种编码方式,ziplist和skiplist,ziplist在list中讲解:
有序集合,每个元素关联一个double类型的分数用于排序。
常用命令:
zadd key [score member]:添加一个或者多个,有的话更新
zcard key:获取元素数量
zcount key min max:
zlexcount key min max 统计字典区间内元素数量
zrange key start stop :返回指定区间的元素。
编码:分为ziplist和skiplist两种。
skiplist跳跃表:生成不同的level,生成的时候没有规则,查询的时候通过判断上层level数据快速进行定位目标在那里。
skiplist同时使用了hash和zskiplist。
zset详细信息
应用:
- 排行榜,热销商品,王者排行榜。
bitmap:
0和1标识的二进制数据。底层是一个string类型,offset从0开始。
应用场景:
- 用于签到。
- 电影广告是不是被点击。
- 上班打卡,签到天数。
HyperLogLog:
统计一个网站的UV,独立访客(需要防止作弊,去重统计,IP),关键词数据,底层是string类型。
用基数统计算法:数据元素去重后的真实数据量不能遍历出来,有一定误差。
命令:PFADD,PFCOUNT,PFMERGE
应用:
- 用来获取访问量等。
GEO:
添加经纬度坐标,底层zset,
应用:
- 附近的美女,附近的店铺,附近的核酸点,打车。经纬度编码位base32一维的数据。
Stream:
为什么有了list,还需要stream。因为list只能点对点,不能发布订阅模式的。
应用:
就是redis版本的消息中间+阻塞队列。
bitfield:位域
二进制位的数组,对任意位进行访问修改。这样就可以更快了。将一个Redis字符串看作是一个由二进制位组成的数组并能对变长位宽和任意没有字节对齐的指定整型位域进行寻址和修改
用于bit位进行修改
持久化:
Redis是一款内存数据库,数据存储在内存中,那么停电或宕机,数据怎么恢复,持久化退出解决这个问题。三种持久化方式诞生,redis早期采用了AOF(增加)、RDB(快照)持久化。后期采用混合持久化生成(AOF文件)。官网介绍:https://redis.io/docs/management/persistence/
RDB:
官网这样描述:RDB(Redis 数据库):RDB 持久性以指定的时间间隔执行数据集的时间点快照_。_恢复数据读取快照文件(dump.rdb全量快照)写回内存。
手动 save/bgsave。进行fork()一个子进程备份文件。生产上只能用bgsave,因为save命令会阻塞主线程。一段时间间隔,执行数据全量快照,以文件(dump.rdb)形式写入磁盘中。
fork是操作系统的一个函数。
**默认开启: **
数据生成快照到.rdb文件中保存,RDB 是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,重启时加载这个文件达到数据恢复。分为手动触发和自动触发:
自动触发:
- save 300 10 :300秒内有10个写操作保存
手动触发:
- save:阻塞redis进行保存,
- bgsave:fork redis进行持久化,在关闭的时候会自动进行bgsave
RDB-优点:
- RDB 是 Redis 数据的非常紧凑的单文件时间点表示形式。RDB 文件非常适合备份
- RDB 非常适合灾难恢复,它是一个紧凑的文件
- RDB 最大限度地提高了 Redis 的性能,因为 Redis 父进程为了持久化需要做的唯一工作就是派生一个子进程,该子进程将完成其余所有工作。父进程永远不会执行磁盘 I/O 或类似操作。
- 与 AOF 相比,RDB 允许更快地重新启动大数据集。
- 在副本上,RDB 支持重启和故障转移后的部分重新同步。
特点-缺点:
- 如果您需要在 Redis 停止工作(例如断电后)时最大程度地减少数据丢失的可能性,那么 RDB 并不好。您可以在生成 RDB 的位置配置不同的保存点(例如,在至少五分钟并对数据集进行 100 次写入后,您可以有多个保存点)。但是,您通常会每五分钟或更长时间创建一个 RDB 快照,因此,如果 Redis 由于任何原因在没有正确关闭的情况下停止工作,您应该做好丢失最新分钟数据的准备。
- RDB 需要经常 fork() 才能使用子进程持久保存在磁盘上。如果数据集很大,fork() 可能会非常耗时,并且如果数据集很大并且 CPU 性能不是很好,可能会导致 Redis 停止为客户端提供服务几毫秒甚至一秒。AOF 还需要 fork() 但频率较低,您可以调整重写日志的频率,而无需牺牲持久性。
- 二进制文件新老版本不兼容。
dump.rdb修复:
在写入最后一条数据的时候死机,,还未写完,导致文件写完,redis-check-rdb dump.rdb.
快照时机:
- 配置文件中默认的快照配置
- 手动save/bgsave命令
- 执行flushall/flushdb命令也会产生dump.rdb文件,但里面是空的
- 无执行shutdown且没有设置开启AOF持久化
- 主从复制时,主节点自动触发
禁用快照:
- 通过命令方式:redis-cli config set save “”
- 通过配置文件方式:save “”
AOF持久化:
AOF 持久性记录服务器接收到的每个写操作。然后可以在服务器启动时再次重播这些操作,从而重建原始数据集。命令使用与 Redis 协议本身相同的格式进行记录。
默认情况redis未开启AOF持久化,需要开启:**appendonly yes。**保存文件为appendonly.aof文件(这是redis6的默认配置文件,redis7有三个。)
**工作流程: **
- 通过client作为命令来源
- redis server不会直接写aof文件,先写缓冲区。
- 缓冲区文件写回磁盘,三种策略
- aof文件过大时,会进行AOF重写,也就是压缩
三种持久化策略:
- appendfsync always:同步持久化,每次的命令都会追加到AOF文件,每次写入都会IO,受到性能影响,但是数据安全。
- appendfsync everysec(默认):异步操作,每秒将写命令追加到AOF文件中,最多丢失1s数据,这个介于上下两个之间。
- appendfsync no:不进行调用文件同步,交由操作系统处理。
| 配置项 | 写回时机 | 优点 | 缺点 |
| — | — | — | — |
| Always | 同步写回 | 可靠性高,数据基本不丢失 | 每个写命令都要落盘,性能影响较大 |
| Everysec | 每秒写回 | 性能适中 | 宕机时丢失1秒内的数据 |
| No | 默认 | 性能好 | 宕机时丢失数据较多 |
AOF文件:
在redis7后和redis6不同了,从一个文件,变为了3个
redis6:一个持久化文件。
redis7:base基本文件+incr增量文件+manifest清单文件
加载恢复
- 正常恢复:重启加载配置文件成功
- 异常恢复:写入的数据没有写完,宕机,使得文件有问题(模拟自己修改aof文件模拟异常)。使用redis-check-aof –fix工具用于修复incr文件。
优点:
- 策略丰富,fsync保证数据是最新的,通过后台处理,对请求线程不影响,
- aof文件容易修复,更好的保护了数据。
- aof文件还能进行重写。例如flushall命令 会被记录在aof文件中。
缺点:
- 文件体积比RDB大很多,数据恢复时需要重新执行指令,恢复慢。
- 高并发下fsync也会造成一些性能问题。
AOF重写原理:
由于AOF持久化是Redis不断将写命令记录到 AOF 文件中,随着Redis不断的进行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。为了解决这个问题,Redis新增了重写机制。保存需要恢复数据的最小命令。自动触发(按照配置文件来),手动触发(bgrewriteaof)。配置文件编写如下。
auto- aof- rewrite- percentage 100
auto- aof- rewrite- min- size 64mb
注意 ,同时满足,且的关系才会触发
1 根据上次重写后的aof大小,判断当前aof大小是不是增长了1倍
2 重写时满足的文件大小
重写原理:
1:在重写开始前,redis会创建一个“重写子进程”,这个子进程会读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
2:与此同时,主进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。
3:当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中
4:当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中
5:重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
redis运行时间长后,客户端命令越来越多,然后AOF文件变大恢复缓慢,需要通过重写AOF文件来减肥。命令:bgrewriteaof来重新写文件。
base文件中记录的是上次重写需要恢复的最小文件命令,incr文件是当前还没有重写的所有命令。
参数:
4.x之后的混合模式:
官网建议混合使用
aof优先级高,重启直接加载AOF文件。
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),留着rdb作为一个万一的手段。
通过命令:aof-use-rdb-preamble yes开启混合模式。通过AOF做增量日志的方式,使得AOF文件不会太大,使用RDB进行存储主要内容。AOF做增量,RDB做全量。
先使用RDB进行快照存储,然后使用AOF持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。这样的话,重启服务的时候会从RDB和AOF两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能。简单来说:混合持久化方式产生的文件一部分是RDB格式,一部分是AOF格式。----》AOF包括了RDB头部+AOF混写
纯缓存功能:
关闭rdb和关闭aof。save"" 和appendonly no。专心做内存服务器。
Redis事务:
官网介绍:https://redis.io/docs/interact/transactions/
redis事务不像mysql事务,官网说redis事务只保证两个,事务中的所有命令都序列化并执行 顺序。
事务控制命令。
命令 | 作用 |
---|---|
multi | 标识事务开始 |
discard | 取消事务 |
exec | 实行事务 |
unwatch | 取消所有key监视 |
watch key [key …] | 监视key |
与mysql相关事务概念对比:
1 单独的隔离操作 | Redis的事务仅仅是保证事务里的操作会被连续独占的执行,redis命令执行是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的 |
---|---|
2 没有隔离级别的概念 | 因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这种问题了 |
3不保证原子性 | Redis的事务不保证原子性,也就是不保证所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力 |
4 排它性 | Redis会保证一个事务内的命令依次执行,而不会被其它命令插入 |
事务失败两种方式:
- 全体连坐:编写事务过程中出现错误,全体失败
- 冤有头债有主:执行工程中要是出现错误,不能回滚,那么只会有失败命令不会执行。
管道:
如何优化频繁命令往返造成的性能瓶颈?
Pipeline是为了解决RT「往返回时,仅仅是将命令打包一次性发送,对整个Redis的执行不造成其它任何影响。因为客户端一条命令就会返回一个响应(类似OK)。mset k1 v1 k2 v2 k3 v3类似这样的,一次性多个命令,然后一个响应就好。使用管道(pipeline)来解决这种大量命令的性能瓶颈。
如何使用:
将需要执行的命令写到一个文件cmd.txt中,然后执行以下命令:
cat cmd.txt | redis-cli -a 111111 --pipe
管道发送的指令不要太多,不然会占用很多内存,小心使用。
发布订阅(了解):
PUB、SUB->redis stream -> MQ、kafka等模式。
先订阅,再发布。
复制机制:
Redis通过复制进行读写分离、水平扩容支撑高并发、高可用和容灾备份机制。主从复制机制。master写为主,slave以读为主。当master数据变化的时候,自动将新的数据异步同步到其它slave数据库。
怎么用:
配从库,不配主库。使用密码访问主库,master如果配置了requirepass参数,需要密码登陆那么slave就要配置masterauth来设置校验密码,否则的话master会拒绝slave的访问请求。
主要命令:
- info replication:查看主从库关系和配置信息
- replicaof 主库ip 端口:写在配置文件中。
- slaveof 主库ip 端口:从机运行中,执行命令,临时切换在运行期间修改slave节点的信息,如果该数据库已经是某个主数据库的从数据库,那么会停止和原主数据库的同步关系转而和新的主数据库同步,重新拜码头,用于改换门庭(换主机)。
- slaveof no one:使自己变为主机,不跟随任何主机。用于自立为王
主从复制相关问题:
- 不能再从机上进行读取。
- slave掉队后,再上线,会同步主机数据。
- 主机下线了后,从机不会上位
- 主机重新上线后,依然是主机
重新选择主机命令操作:通过 slaveof 主机IP port 也能够连续主机。
薪火相传:slave也可以有从机。master->slave1->slave2
反客为主:slaveof no one
复制原理:
- slave启动,同步初请:从机发送sync给主机请求同步,
- 首次连接,全量复制:slave首次全新连接master,一次完全同步(全量复制)将被自动执行,slave自身原有数据会被master数据覆盖清除master节点收到sync命令后会开始在后台保存快照(即RDB持久化,主从复制时会触发RDB),
- 心跳持续,保持通信:
- 进入平稳,增量复制:Master继续将新的所有收集到的修改命令自动依次传给slave,完成同步
- 从机下线,重连续传:master会检查backlog里面的offset,master和slave都会保存一个复制的offset还有一个masterId,offset是保存在backlog中的。Master只会把已经复制的offset后面的数据复制给Slave,类似断点续传
缺点:
- 复制延时,信号衰减
- 主机挂了,全面寄。slave不会自动上位。集群处于一个无人值守的场景。
哨兵sentinel(重点):
优化简单主从复制的集群,通过引用哨兵,提出主从互换(根据投票数自动将从库转换为主库),能者上位。哨兵一般有三台(奇数台)。监控主从redis库运行是否正常,哨兵可以将故障转移的结果发送给客户端如果Master异常,则会进行主从切换,将其中一个Slave作为新Master客户端通过连接哨兵来获得当前Redis服务的主节点地址
实战:
三个哨兵(防止宕机,奇数,方便投票),1主2从.
主要命令:
- sentinel monitor
quorum:投票数,用于判断是否主机真实下线
- sentinel autu-pass
master设置了密码,连接master服务的密码
启动哨兵:
自动生成配置信息
情景:
主机,主机下线,然后发生投票选举出新的master。
配置文件:重写了配置文件,redis.conf也会被重写追加在末尾。
- 老主机会追加新主机信息。
- 新主机会去除主机信息。
原理:哨兵运行流程和选举原理:
1.主管下线:单个sentinel自己主观检查(ping)master进行了宕机也就是主观下机,SDOWN,默认30秒
2.客观下线:ODown多个sentinel达成一致认为该主机下线才是真下线,quorum这个参数就是多少个哨兵判定一致才认为是下线。
3.兵王选举:哨兵中投票选取(raft算法)一个哨兵做兵王进行故障迁移:用于选取主机和从机。
4.选举新的master:
新主登基,群臣俯首,旧主拜服。
1.新主登基,:从机优先级(小的优先级高)>偏移量(大的损失小代价小)>runID。按照权重判断
2.群臣俯首:
- sentinelleader 向被选举的结点进行slave no one 变成新的master结点
- sentinelleader向其他slave发送命令,令剩余的slave更换新的master。
3.旧主拜服:老master回来后,sentinelleader让老的变成salve
哨兵注意点:
- 多个哨兵结点
- 奇数结点
- 哨兵性能,配置几乎一致
- 可能会有数据丢失 ,引出集群。
集群(cluster重点):
官网:https://redis.io/docs/reference/cluster-spec/
由于数据量过大,单个Master复制集难以承担,因此需要对多个复制集进行集群,形成水平扩展每个复制集只负责存储整个数据集的一部分,这就是Redis的集群,其作用是提供在多个Redis节点间共享数据的程序集。
原因:哨兵模式下,由于master挂机后,这是会有数据丢失,单个的master的性能也比较优先。redis集群是一个提供在多个redis结点间共享数据的程序集。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jcP4XrXT-1689771960351)(https://cdn.nlark.com/yuque/0/2023/png/22771376/1688467928485-b0ec21e7-7229-4930-93f7-7d1f2fb2c388.png#averageHue=%23c2ae8e&clientId=u19a9aa54-ce3a-4&from=paste&height=321&id=u2e195449&originHeight=481&originWidth=803&originalType=binary&ratio=1.3625000715255737&rotation=0&showTitle=false&size=264790&status=done&style=none&taskId=u0f58d9c4-4615-4f67-9bc9-b17d22462dc&title=&width=535.7797841260084)]
支持多个master,每个master挂载多个slave,Cluster自带sentinel的故障机制,无需配置哨兵了。客户端连接集群中任意一个结点即可连接到集群,槽位slot负责分配到各个物理服务节点,由对应的集群来负责维护节点、插槽和数据之间的关系。
槽位&集群算法:
集群的密钥空间分为 16384 个插槽,有效设置上限 对于 16384 个主节点的集群大小(但是,建议的最大大小 节点大约为 ~ 1000 个节点)。
集群中的每个主节点处理16384个哈希槽的一个子集。当没有集群重新配置正在进行时(即哈希槽从一个节点移动到另一个节点),集群是稳定的。当集群稳定时,单个哈希槽将由单个节点提供服务(但是,服务节点可以有一个或多个副本,在网络分裂或故障的情况下替换它,并且可以用于扩展读取陈旧数据是可接受的操作)。
槽位&分片
槽位:引入hash槽位。
分片:使用Redis集群时我们会将存储的数据分散到多台redis机器上,这称为分片。简言之,集群中的每个Redis实例都被认为是整个数据的一个分片。找到给定key的分片,使用CRC16(Key)算法处理并通过对总分片进行取模,然后通过确定哈希函数,使得key多次始终会映射到同一个分片。这样读写都会从同一个分片进行获取。
优势:引入槽位和分片后,这种结构很容易添加或者删除节点.比攻果我想新添加个节点D,我需要从节点A,B,C中得部分槽到D上.如果我想移除节点A需要将A中的槽移到B和C节点上然后将没有任何槽的A节点从集群中移除即可.由于从一个节点将哈希槽移动到另一个节点并不会停止服务.所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
槽位映射方案3种:
哈希取余,一致性哈希算法,哈希槽分区算法。
- 哈希取余:
简单粗暴,hash(key)%n.扩容和缩容比较麻烦。变动后处理不了,不灵活
- 一致性哈希算法:
解决:变动和映射问题,
步骤三个步骤:
- 算法构建一致性hash环:将线性的0-(2^32)-1变成一个虚拟的环
- 服务器IP节点映射:redis的IP映射到环上某个位置,key进行hash计算,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OYUEhPW-1689771960352)(https://cdn.nlark.com/yuque/0/2023/png/22771376/1688472271355-312a4a16-79a4-4ded-ae16-9636402cdf97.png#averageHue=%23dfe8db&clientId=u19a9aa54-ce3a-4&from=paste&height=403&id=u33fac8c5&originHeight=604&originWidth=591&originalType=binary&ratio=1.3625000715255737&rotation=0&showTitle=false&size=132248&status=done&style=none&taskId=uad3b5d78-ad51-4f5c-b2d0-df78b58ef45&title=&width=394.32858333558033)]
- key落到服务器的落键规则:key计算hash,然后顺时针方向给结点,具有容错性。
问题:
redis服务器的落点可能在hash环上不均匀。数据倾斜问题。
- 哈希槽分区算法:只有16384个槽位
2^14=16384,一致性hash环会有分布不均的情况出现,哈希分区算法解决这个问题。
解决hash环均匀问题,在数据和结点间加了一层hash槽用于管理结点和数据的关系。Redis集群中内置了16384个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在Redis集群中放置一个key-value时,redis先对key使用crc16算法算出一个结果然后用结果对16384求余数[CRC16(key) % 16384],这样每个key都会对应一个编号在0-16383之间的哈希槽,也就是映射到某个节点上。如下代码,key之A、B在Node2,key之C落在Node3上。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYUf8hRA-1689771960353)(https://cdn.nlark.com/yuque/0/2023/png/22771376/1688475298247-650a23d9-38ab-453a-b6d2-cb1cfc56da2c.png#averageHue=%23f6f2ee&clientId=u19a9aa54-ce3a-4&from=paste&height=214&id=u4522bf30&originHeight=320&originWidth=1116&originalType=binary&ratio=1.3625000715255737&rotation=0&showTitle=false&size=183006&status=done&style=none&taskId=u19bbb0e6-c9e5-48c9-b4cd-9e3d658fa36&title=&width=744.6204720854613)]
为什么槽数是16384个?
crc16产生的是16位hash值,16位是65535位,心跳数据包可以携带配置文件信息,
1.正常的心跳数据包带有节点的完整配置,可以用幂等方式用旧的节点替换旧节点,以便更新旧的配置。
2.这意味着它们包含原始节点的插槽配置,该节点使用2k的空间和16k的插槽,但是会使用8k的空间(使用65k的插槽)。同时,由于其他设计折衷,Redis集群不太可能扩展到1000个以上的主节点。
(1)如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。当槽位为65536时,这块的大小是:65536÷8∶1024=8kb在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。当槽位为16384时,这块的大小是:16384÷8÷1024=2kb
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
因此16k处于正确的范围内,以确保每个主机具有足够的插槽,最多可容纳1000个矩阵,但数量足够少,可以轻松地将插槽配置作为原始位图传播。请注意,在小型群集中,位图将难以压缩,因为当N较小时,位图将设置的slot /N位占设置位的很大百分比。
(2)redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
(3)槽位越小,节点少的情况下,压缩比高,容易传输
Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots /N很高的话(N表示节点数),bitmap的压缩率就很低。如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。
Redis是AP的,集群不保证强一致性,可能丢失写入请求
- 异步复制,可能丢失数据。
案例:
1.配置文件
2.集群命令:
redis-cli -a 111111 --cluster create --cluster-replicas 1 192.168.101.131:6381 192.168.101.131:6382 192.168.101.132:6383 192.168.101.132:6384 192.168.101.133:6385 192.168.101.133:6386
–cluster-replicas 1 表示为每个master创建一个slave节点
3.路由,
redis-cli -a 111111 -p port -c
- -c表示。连接的一个集群,而不是一台主机。
cluster keyslot key
- 查看key属于那个槽位
4.结点从属调整:
cluster failover.调整为最初的配置从属关系。
扩展主机:
- 加入集群:
6381 就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群
redis-cli -a 111111 --cluster add-node 192.168.111.174:6387 192.168.111.175:6381
- 进行分配槽位:
特点,都是从老的结点上分配一部分过来,不会全部重新分配。
重新分派槽号
命令:redis-cli -a 密码 --cluster reshard IP地址:端口号
redis-cli -a 密码 --cluster reshard 192.168.111.175:6381
缩容主机:
只能够删除从节点,
- 检查从机ID,先删除从节点,
- 删除从机结点:
- 在不被删结点上接受需要删除结点的槽位。被删除的master结点会被从节点,
- 删除想要结点。
总结:
mget keys在不同的槽区上不能够获取,需要使用{}
CRC16算法:cluster.c的918行有一个算法
SpringBoot+Redis
使用什么连接?jedis->lettuce->RedisTemplate(推荐使用)
RedisTemplate需要引入一个包,
导入POM文件:
修改YML文件:
server.port=7777
spring.application.name=redis7_study
# ========================logging=====================
logging.level.root=info
logging.level.com.atguigu.redis7=info
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n
logging.file.name=D:/mylogs2023/redis7_study.log
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger- %msg%n
# ========================swagger=====================
spring.swagger2.enabled=true
#在springboot2.6.X结合swagger2.9.X会提示documentationPluginsBootstrapper空指针异常,
#原因是在springboot2.6.X中将SpringMVC默认路径匹配策略从AntPathMatcher更改为PathPatternParser,
# 导致出错,解决办法是matching-strategy切换回之前ant_path_matcher
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
# ========================redis单机=====================
spring.redis.database=0
# 修改为自己真实IP
spring.redis.host=192.168.101.131
spring.redis.port=6379
spring.redis.password=111111
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
修改RedisConfig,用来更改序列化方式:
连接集群:
# ========================redis集群=====================
spring.redis.password=111111
# 获取失败 最大重定向次数
spring.redis.cluster.max-redirects=3
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
spring.redis.cluster.nodes=192.168.111.175:6381,192.168.111.175:6382,192.168.111.172:6383,192.168.111.172:6384,192.168.111.174:6385,192.168.111.174:6386
问题:有master宕机了,这时候微服务会出现问题不会主动进行且换。没有动态感知到redis集群信息。
分析:lettuce有问题,不会感知
解决方案:
- 使用jedis方式
- 重写工厂模式
- 刷新结点的动态感应。感知拓扑。就是增加两个配置
支持集群拓扑动态感应刷新,自适应拓扑刷新是否使用所有可用的更新,默认false关闭
spring.redis.lettuce.cluster.refresh.adaptive=true
定时刷新
spring.redis.lettuce.cluster.refresh.period=2000