目录
Ⅲ、type 命令可以获取 key 对应的 value 值类型
Ⅴ、ttl 命令可以查看 key 还有多少秒过期,-1表示永不过期,-2表示已过期
Ⅵ、rename 命令是对已有 key 进行重命名。(覆盖)
Ⅸ、使用config set requirepass 命令设置密码
一.故事背景
本节分享有关Redis模块的相关内容,包含Redis数据库基础,高可用的集群部署等内容。
二.Redis数据库基础
相比于其他的内存/缓存数据库,redis可以方便的实现持久化的功能(保存至磁盘中)
一、关系数据库与非关系数据库概述
1、关系型数据库
-
关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。
-
SQL 语句(标准数据查询语言)就是一种基于关系型数据库的语言,用于执行对关系型数据库中数据的检索和操作。
-
主流的关系型数据库包括 Oracle、MySQL、SQL Server、Microsoft Access、DB2 等。
2、非关系型数据库
-
NoSQL(NoSQL = Not Only SQL ),意思是“不仅仅是 SQL”,是非关系型数据库的总称。
-
除了主流的关系型数据库外的数据库,都认为是非关系型。
-
主流的 NoSQL 数据库有 Redis、MongBD、Hbase、Memcached、Varnish 等。
3、关系数据库与非关系数据库的区别
(1)数据存储方式不同
-
关系型和非关系型数据库的主要差异是数据存储的方式。关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。
-
与其相反,非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。非关系型数据通常存储在数据集中,就像文档、键值对或者图结构。你的数据及其特性是选择数据存储和提取方式的首要影响因素。
关系型 | 依赖于关系模型E-R图,同时以表格形式的方式存储数据 |
---|---|
非关系型 | 除了以表格形式存储之外,通常会以大块的形式组合在一起进行存储数据 |
(2)扩展方式不同
-
SQL和NoSQL数据库最大的差别可能是在扩展方式上,要支持日益增长的需求当然要扩展。
-
要支持更多并发量,SQL数据库是纵向扩展,也就是说提高处理能力,使用速度更快速的计算机,这样处理相同的数据集就更快了。因为数据存储在关系表中,操作的性能瓶颈可能涉及很多个表,这都需要通过提高计算机性能来客服。虽然SQL数据库有很大扩展空间,但最终肯定会达到纵向扩展的上限。
-
而NoSQL数据库是横向扩展的。因为非关系型数据存储天然就是分布式的,NoSQL数据库的扩展可以通过给资源池添加更多普通的数据库服务器(节点)来分担负载。
关系型数据库 | 纵向 | 天然表格式 |
---|---|---|
非关系型数据库 | 横向 | 天然分布式 |
(3)对事务性的支持不同
-
如果数据操作需要高事务性或者复杂数据查询需要控制执行计划,那么传统的SQL数据库从性能和稳定性方面考虑是你的最佳选择。SQL数据库支持对事务原子性细粒度控制,并且易于回滚事务。
-
虽然NoSQL数据库也可以使用事务操作,但稳定性方面没法和关系型数据库比较,所以它们真正闪亮的价值是在操作的扩展性和大数据量处理方面。
关系型 | 特别适合高事务性要求和需要执行控制执行计划的任务 |
---|---|
非关系型 | 此处会稍显弱势,其价值点在于高扩展性和大数据量方面 |
4、非关系型数据库产生背景
-
可用于应对web2.0纯动态网站类型的三高问题
High performance | 对数据库高并发读写要求 |
---|---|
Huge Storage | 对海量数据高效存储与访问需求 |
High Scalability && High Availability | 对数据库高可扩展性与高可用性需求 |
-
关系型数据库和非关系型数据库都有各自的特点与应用场景,两者的紧密结合将会给Web2.0的数据库发展带来新的思路。
-
让关系数据库关注在关系上,非关系型数据库关注在存储上。
-
例如,在读写分离的MySQL数据库环境中,可以把经常访问的数据存储在非关系型数据库中,提升访问速度。
总结
关系型数据库
实例 -> 数据库 -> 表(table)-> 记录行(row)、数据字段(column)
非关系型数据库
实例 -> 数据库 -> 集合(collection) -> 键值对(key-value)
二、Redis简介
-
Redis 是一个开源的、使用 C 语言编写的 NoSQL 数据库。
-
Redis 基于内存运行并支持持久化(支持存储在磁盘里),采用key-value(键值对)的存储形式,是目前分布式架构中不可或缺的一环。
1、Redis服务器程序的单进程模型
-
Redis服务器程序是单进程模型,也就是在一台服务器上可以同时启动多个Redis进程,Redis的实际处理速度则是完全依靠于主进程的执行效率。
-
若在服务器上只运行一个Redis进程,当多个客户端同时访问时,服务器的处理能力是会有一定程度的下降。
-
若在同一台服务器上开启多个Redis进程,Redis在提高并发处理能力的同时会给服务器的CPU造成很大压力。
-
在实际生产环境中,需要根据实际的需求来决定开启多少个Redis进程。若对高并发要求更高一些,可能会考虑在同一台服务器上开启多个进程。若 CPU 资源比较紧张,采用单进程即可。
-
建议可以开2个线程
-
原因:
-
1、备份
-
2、提高并发的同时尽量不要给CPU造成太大的压力
-
2、Redis的优点
(1)具有极高的数据读写速度:数据读取的速度最高可达到 110000 次/s,数据写入速度最高可达到 81000 次/s。
(2)支持丰富的数据类型:支持 key-value、Strings、Lists、Hashes、Sets 及 Ordered Sets 等数据类型操作。
string | 字符串(可以为整型、浮点型和字符串,通称为元素) |
---|---|
list | 列表(实现队列,元素不唯一,先入先出原则) |
set | 集合(各不相同的元素) |
hash | hash散列值(hash的key必须是唯一的) |
ordered set | 有序集合 |
(3)支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
(4)原子性,Redis 所有操作都是原子性的。
(5)支持数据备份,即 master-salve 模式的数据备份。
Redis作为基于内存运行的数据库,缓存是其最常应用的场景之一。
除此之外,Redis常见应用场景还包括获取最新N个数据的操作、排行榜类应用、计数器应用、存储关系、实时分析系统、日志记录。
3、哪些数据适合放入缓存中?
-
即时性。例如查询最新的物流状态信息。
-
数据一致性要求不高。例如门店信息,修改后,数据库中已经改了,五分钟后缓存中才是最新的,但不影响功能使用。
-
访问量大且更新频率不高,例如网站首页的广告信息,访问量大,但是不会经常变化。
4、Redis为什么这么快?
-
Redis是一款纯内存结构,避免了磁盘I/O等耗时操作。
-
Redis命令处理的核心模块为单线程,不存在多线程切换而消耗CPU,不用考虑各种锁的问题,不存在加锁、释放锁的操作,没有因为可能出现死锁而导致性能消耗。
-
采用了 I/O 多路复用机制,大大提升了并发效率。
5、Redis的数据类型
-
String(字符串)
-
list(双向列表):
-
Hash(哈希):
-
set(集合):
-
zset(有序集合):
注:在 Redis 6.0 中新增加的多线程也只是针对处理网络请求过程采用了多线性,而数据的读写命令,仍然是单线程处理的。
三、Redis部署
安装redis
1、配置文件
bind 127.0.0.1 192.168.71.166 :绑定的 IP 地址,Redis 将只在这些 IP 上监听连接请求。
protected-mode no :关闭保护模式,允许外部访问 Redis 服务器。
port 6379 :Redis 服务器监听的端口,默认为 6379。
tcp-backlog 511 :TCP 连接的 backlog 数量。
timeout 0 :客户端连接的超时时间,0 表示没有超时。
tcp-keepalive 300 :TCP 连接的保活时间。
daemonize yes :以守护进程方式运行 Redis。
pidfile /usr/local/redis/log/6379.pid :保存 PID 的文件路径。
loglevel notice :日志级别。
logfile "/usr/local/redis/log/6379.log" :日志文件的路径。
databases 16 :Redis 数据库的数量。
always-show-logo no :不总是显示启动时的 logo。
set-proc-title yes :设置进程标题。
proc-title-template "{title} {listen-addr} {server-mode}" :进程标题的模板。
locale-collate "" :本地化排序设置。
stop-writes-on-bgsave-error yes :在后台保存时出现错误则停止写入。
rdbcompression yes :RDB 文件压缩。
rdbchecksum yes :RDB 文件校验。
dbfilename dump.rdb :RDB 文件名。
rdb-del-sync-files no :不同步删除 RDB 文件。
dir /usr/local/redis/data/ :数据文件存储目录。
replica-serve-stale-data yes :从服务器可以提供过期数据。
replica-read-only yes :从服务器只读。
repl-diskless-sync yes :无磁盘复制。
repl-diskless-sync-delay 5 :无磁盘复制的延迟。
repl-diskless-sync-max-replicas 0 :无磁盘复制的最大从服务器数量。
repl-diskless-load disabled :禁用无磁盘加载。
repl-disable-tcp-nodelay no :不禁用 TCP_NODELAY。
replica-priority 100 :从服务器的优先级。
acllog-max-len 128 :ACL 日志的最大长度。
lazyfree-lazy-eviction no :不延迟释放被驱逐的键。
lazyfree-lazy-expire no :不延迟释放过期的键。
lazyfree-lazy-server-del no :不延迟释放服务器删除的键。
replica-lazy-flush no :不延迟从服务器的 flush。
lazyfree-lazy-user-del no :不延迟释放用户删除的键。
lazyfree-lazy-user-flush no :不延迟释放用户 flush 的键。
oom-score-adj no :不调整 OOM(内存不足)分数。
oom-score-adj-values 0 200 800 :OOM 分数的值。
disable-thp yes :禁用透明大页(Transparent HugePages)。
appendonly no :不使用 AOF(Append Only File)持久化。
appendfilename "appendonly.aof" :AOF 文件名。
appenddirname "appendonlydir" :AOF 文件存储目录。
appendfsync everysec :AOF 同步策略。
no-appendfsync-on-rewrite no :在重写时不同步 AOF。
auto-aof-rewrite-percentage 100 :自动重写 AOF 的百分比。
auto-aof-rewrite-min-size 64mb :自动重写 AOF 的最小大小。
aof-load-truncated yes :加载不完整的 AOF。
aof-use-rdb-preamble yes :AOF 使用 RDB 作为前缀。
aof-timestamp-enabled no :AOF 中不记录时间戳。
slowlog-log-slower-than 10000 :慢日志记录的阈值。
slowlog-max-len 128 :慢日志的最大长度。
latency-monitor-threshold 0 :延迟监控的阈值。
notify-keyspace-events "" :发布订阅的事件。
hash-max-listpack-entries 512 :哈希列表的最大条目数。
hash-max-listpack-value 64 :哈希列表的最大值。
list-max-listpack-size -2 :列表的最大 Listpack 大小。
list-compress-depth 0 :列表压缩深度。
set-max-intset-entries 512 :集合整数集的最大条目数。
set-max-listpack-entries 128 :集合列表的最大条目数。
set-max-listpack-value 64 :集合列表的最大值。
zset-max-listpack-entries 128 :有序集合列表的最大条目数。
zset-max-listpack-value 64 :有序集合列表的最大值。
hll-sparse-max-bytes 3000 :HyperLogLog 稀疏格式的最大字节数。
stream-node-max-bytes 4096 :Stream 节点的最大字节数。
stream-node-max-entries 100 :Stream 节点的最大条目数。
activerehashing yes :激活重哈希。
client-output-buffer-limit normal 0 0 0 :普通客户端输出缓冲区限制。
client-output-buffer-limit replica 256mb 64mb 60 :从客户端输出缓冲区限制。
client-output-buffer-limit pubsub 32mb 8mb 60 :发布订阅客户端输出缓冲区限制。
hz 10 :时间事件的频率。
dynamic-hz yes :动态调整 hz。
aof-rewrite-incremental-fsync yes :AOF 重写时增量同步。
rdb-save-incremental-fsync yes :RDB 保存时增量同步。
jemalloc-bg-thread yes :使用 jemalloc 后台线程。
2、Redis 命令工具
redis-server: #服务器启动命令
redis-benchmark: #性能测试工具,用于检测 Redis 在本机的运行效率
redis-check-aof: #修复有问题的 AOF 持久化文件
redis-check-rdb: #修复有问题的 RDB 持久化文件
redis-cli: #客户端命令行工具
redis-sentinel: #哨兵集群使用
redis-cli
命令行工具
主要用法:
-h :指定远程主机
-p :指定 Redis 服务的端口号
-a :指定密码,未设置数据库密码可以省略-a 选项
redis-server
复制一个配置文件,并重命名,再将配置文件中的端口全部改为6380
此时启动该服务只能在前台运行
再次配置文件,将309行的守护进程打开(改成yes)
此时再次启动,可以后台运行
查看进程发现6379端口和6380全部启动
redis-benchmark
3、Redis 数据库常用命令
-
set:存放数据,命令格式为 set key value
-
get:获取数据,命令格式为 get key
通过set命令为键设置新值,并覆盖原有值。
keys 命令可以取符合规则的键值列表,通常情况可以结合*、?等选项来使用。.
查看当前数据库中所有键
查看当前数据库中以 G开头的数据
查看当前数据库中以 Game开头后面包含任意一位的数据
Ⅰ、exists 命令可以判断键值是否存在。
Ⅱ、del 命令可以删除当前数据库的指定 key。
Ⅲ、type 命令可以获取 key 对应的 value 值类型
Ⅳ、expire 命令可以为给定的 key 设置过期时间
Ⅴ、ttl 命令可以查看 key 还有多少秒过期,-1表示永不过期,-2表示已过期
Ⅵ、rename 命令是对已有 key 进行重命名。(覆盖)
Ⅶ、renamenx 命令
作用是对已有 key 进行重命名,并检测新名是否存在,如果目标 key 存在则不进行重命名。(不覆盖)
Ⅷ、dbsize 命令
作用是查看当前数据库中 key 的数目
Ⅸ、使用config set requirepass 命令设置密码
## 使用config get requirepass命令查看密码
##命令行验证密码 auth
Ⅹ、使用append追加并返回value的长度
该键并不存在,因此append命令返回当前Value的长度。
该键已经存在,因此返回追加后Value的长度。
ⅩⅠ 、strlen获取指定Key的字符长度。
ⅩⅡ、批量设置键和查看键
192.168.71.166:6379>mset 键名1 "值1" 键名2 "值2"
192.168.71.166:6379>mget 键1 键2
4、Redis 多数据库常用命令
-
Redis 支持多数据库,Redis 默认情况下包含 16 个数据库,数据库名称是用数字 0-15 来依次命名的。
-
多数据库相互独立,互不干扰。
Ⅰ、多数据库间切换
命令格式:select 序号
使用 redis-cli 连接 Redis 数据库后,默认使用的是序号为 0 的数据库。
Ⅱ、多数据库间移动数据
move 键值 序号
Ⅲ、清除数据库内数据
FLUSHDB #清空当前数据库数据
FLUSHALL #清空所有数据库的数据,慎用!
三.Redis持久化详解
Redis是一种高级 key-value 型的NoSQL数据库。它跟memcached类似是内存型数据库,不过Redis数据可以做持久化,即内存中的数据可以同步到磁盘进行存储。而且Redis所支持的数据类型很丰富。有字符串,链表,集合和有序集合等。Redis支持在服务器端计算集合的并交和补集(difference)等,还支持多种排序功能。所以Redis也可以被看成是一个数据结构型服务器。
Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为"半持久化模式"RDB);也可以把每一次数据变化都写入到一个 append only file(AOF)里面(这称为"全持久化模式")。
Redis的数据都存放在内存中,如果没有配置持久化功能,Redis重启后数据就全丢失了,于是需要开启Redis的持久化功能,将数据保存到磁盘上,当Redis重启后,可以从磁盘中恢复数据。Redis提供两种方式进行持久化,一种是RDB 持久化(原理是将 Redis在内存中的数据库记录定时 dump 到磁盘上的 RDB持久化),另外一种是 AOF(append only file)持久化(原理是将Redis的操作日志以追加的方式写入文件)。那么这两种持久化方式有什么区别呢,改如何选择呢?
RDB和AOF的区别
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork 一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作。查询操作不会记录(类似于MySQL数据库的Binlog),以文本的方式记录,可以打开文件看到详细的操作记录。
RDB和AOF的优缺点
RDB存在哪些优势呢?
1) 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
2) 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
3) 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO 操作了。
4) 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。
RDB又存在哪些劣势呢?
1) 如果想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
2) 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
AOF 的优势有哪些呢?
1) 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3种同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。
2) 由于该机制对日志文件的写入操作采用的是 append(追加)模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。 3) 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite 切换时可以更好的保证数据安全性。
4) AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。
AOF 的劣势有哪些呢?
1) 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB在恢复大数据集时的速度比AOF的恢复速度要快。
2) 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。
二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。
Redis 持久化配置
1、RDB持久化配置
Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开redis.conf文件之后,我们搜索save可以看到下面的配置信息
-
save 900 1 #在900秒(15 分钟)之后,如果至少有1个key发生变化,则dump内存快照。
-
save 300 10 #在300秒(5 分钟)之后,如果至少有10个key发生变化,则dump内存快照。
-
save 60 10000 #在60秒(1 分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
先查看dump.rdb文件
然后在redis中进行操作
然后再次查看dump.rdb文件,发现此时文件内容发生了变换(因为没满足快照频率,所以使用save手动保存)
然后进入redis再次创建,并非法关闭程序,发现并未成功保存
但是使用systemctl 命令重启是可以保存的
2、AOF持久化配置(尝试修复会删除aof文件内容)
Redis的配置文件中存在三种同步方式,它们分别是:
-
appendfsync always #每次有数据修改发生时都会写入AOF文件。
-
appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
-
appendfsync no #从不同步。高效但是数据不会被持久化。
在1387行,将默认关闭打开
然后重启redis之后会发现/etc/lib/redis/下会多出一个目录
此时文件内容为空,然后去redis进行操作
再回到文件中查看,会发现其将操作记录下来了
3、AOF 重写功能
AOF持久化存在以下缺点:
-
Redis会不断地将被执行的命令记录到AOF文件里面,所以随着Redis不断运行,AOF文件的体积也会不断增长。在极端情况下,体积不断增大的AOF文件甚至可能会用完硬盘的所有可用空间。
-
Redis在重启之后需要通过重新执行 AOF文件记录的所有写命令来还原数据集,所以如果AOF文件的体积非常大,那么还原操作执行的时间就可能会非常长。
为了解决AOF文件体积不断增大的问题,用户可以向Redis发送BGREWRITEAOF命令,这个命令会通过移除AOF文件中的冗余命令来重写(rewrite)AOF文件,使AOF文件的体积变得尽可能地小。BGREWRITEAOF的工作原理和BGSAVE创建快照的工作原理非常相似:Redis会创建一个子进程,然后由子进程负责对AOF文件进行重写。因为AOF文件重写也需要用到子进程,所以快照持久化因为创建子进程而导致的性能问题和内存占用问题,在AOF持久化中也同样存在。
跟快照持久化可以通过设置save选项来自动执行BGSAVE一样,AOF持久化也可以通过设置auto-aof-rewrite-percentage选项和 auto-aof-rewrite-min-size选项来自动执行BGREWRITEAOF。
举个例子,假设用户对Redis设置了配置选项auto-aof-rewrite-percentage 100和auto-aof-rewrite-min-size 64mb,并且启动了AOF持久化,那么当AOF文件的体积大于64MB,并且AOF文件的体积比上一次重写之后的体积大了至少一倍(100%)的时候,Redis将执行 BGREWRITEAOF 命令。如果AOF重写执行得过于频繁的话,用户可以考虑将auto-aof-rewrite-percentage选项的值设置为100以上,这种做法可以让Redis在AOF文件的体积变得更大之后才执行重写操作,不过也会让Redis在启动时还原数据集所需的时间变得更长。
四.Redis主从复制
一、环境概述
在分布式集群系统中为了解决服务单点故障问题,通常会把数据复制出多个副本部署到不同的机器中,满足故障恢复和负载均衡等需求。Redis也是如此,它为我们提供了复制功能,实现了相同数据的多个Redis副本。复制功能是高可用Redis的基础,Redis的哨兵和集群(Cluster)模式都是在主从复制模式的基础上实现的。复制也是Redis日常运维的常见维护点。因此深刻理解复制的工作原理与使用技巧对日常的运维非常有帮助。
二、Redis主从结构
2.1、一主一从结构
一主一从结构是Redis最简单的复制拓扑结构,用于主节点出现宕机时从节点来提供故障转移支持。当应用写命令并发量较高且需要持久化时,可以只在从节点上开启AOF,这样既保证数据安全性同时,也避免了持久化对主节点的性能压力。
2.2、一主多从结构
一主多从结构(又称为星型拓扑结构)使得应用端可以利用多个从节点实现读写分离方案。对于读占比较大的场景,可以把读命令发送到多个从节点来分担主节点压力。同时在日常开发中如果需要执行一些比较耗时的读命令,可以在其中一台从节点上执行,防止慢查询对主节点造成阻塞从而影响线上服务的稳定性。对于写并发量较高的场景,多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,同时也加重了主节点的负载影响服务稳定性。
2.3、树状主从结构
树状结构(级联结构)使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节点继续向下层复制。通过引入中间复制层,可以有效降低主节点负载和需要传送给从节点的数据量。当主节点需要挂载多个从节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点压力。
三、主从复制原理
3.1、复制流程
在从节点执行slaveof命令后,复制过程便开始运作,大致分为6个过程:
1.保存主节点信息
执行slaveof命令后从节点只保存主节点的地址信息便直接返回,这时建立复制流程还没有开始,在从节点执行info replication命令可以看到主节点的相关信息,并在日志中记录复制启动信息。
2.主从建立socket连接
从节点内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接。
从节点会建立一个socket套接字连接,专门用于接收主节点发送的复制命令。如果从节点无法建立连接,定时任务会无限重试,直到连接成功或者执行slaveof no one命令取消复制。
3.发送ping命令
连接建立成功后,从节点发送ping请求进行首次通信,目的在于:
-
检测主从之间网络套接字是否可用。
-
检测主节点当前是否可接受处理命令。
如果发送ping命令后,从节点没有收到主节点回复pong或者超时未回复,从节点会断开复制连接,等待下次定时任务发起重连。
4.权限验证
如果主节点设置了requirepass参数,则需要密码验证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过验证。如果验证失败复制将终止,从节点重新发起复制流程。
5.同步数据集
主从复制连接正常通信后,对于首次建立复制的场景,主节点会把所有的数据全部发送给从节点,这部分操作是耗时最长的步骤。在同步过程中会分为两种情况:全量同步和部分同步。
6.命令持续复制
当主节点把当前的数据同步给从节点后,便完成了复制建立的流程。接下来主节点会持续地把写命令发送给从节点,保证数据的一致性。
3.2、复制类型
Redis在2.8及以上版本使用psync命令完成主从数据同步,过程分为:全量复制和部分复制。
-
全量复制:一般用于初次复制的场景,Redis早期支持的复制功能只有全量复制,他会把主节点全部数据一次性发送给从节点,当数据量较大时,会对主从节点和网络造成很大的开销。
-
部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。而补发的数据量远远小于全量数据,可以有效避免全量复制的过高开销。
部分复制是对老版复制的重大优化,有效避免了不必要的全量复制操作。因此当使用复制功能时,尽量采用2.8以上版本的Redis。
psync命令运行时需要以下组件的支持:
1.复制偏移量
参与复制的主从节点都会维护自身复制偏移量。主节点在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在info replication中的master_repl_offset指标中。
从节点每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点复制偏移量。并且从节点在收到主节点发送的命令后,也会累加记录自身的偏移量,记录在自己的统计信息中。
2.复制积压缓冲区
复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,当主节点进行主从复制时,写命令不但会发送给从节点,还会写入复制积压缓冲区中。
缓冲区本质上是一个先进先出的定长队列,可以实现保存最近已复制数据的功能,用于部分复制和复制命令丢失的数据补救。统计信息记录与info replication中
-
repl_backlog_active:1 //开启复制缓冲区
-
repl_backlog_size:1048576 //缓冲区最大大小
-
repl_backlog_first_byte_offset:7479 //起始偏移量
-
repl_backlog_histlen:1048576 //已保存数据的有效长度
3.主节点运行ID
每个Redis节点启动后,都会动态分配一个运行ID,用来唯一识别一个Redis节点。从节点通过保存主节点的运行ID,来判断自己正在向谁进行复制,当主的运行ID改变后,从节点将进行全量复制。
注意:Redis重启时,运行ID会随之改变
若不希望从节点对主节点进行全量复制,则需要保持主节点ID不变,而当修改配置文件需要重启操作时,可以对主节点使用redis-cli debug reload命令来进行对配置文件的重新加载,而不用关闭Redis。但是,debug reload命令会阻塞Redis主进程,阻塞期间会生成本地RDB快照并清空数据之后再加载RDB文件。因此对于无法容忍阻塞的场景,谨慎使用。
4.psync命令
命令格式:psync { runID } { offset }
runID:主节点的运行id。
offset:当前从节点已复制的数据偏移量。
流程说明:
-
从节点发送psync命令给主节点,如果没有runID,默认值为?,如果是第一次复制,则offset默认值为-1.
-
主节点根据psync参数和自身数据情况决定响应结果。
-
如果回复+FULLRESYNC {runID} {offset},从节点开始全量复制。
-
如果回复+CONTINUE,从节点开始部分复制。
-
如果回复+ERR,说明主节点版本低于2.8,不支持psync,从节点会发送sync命令进行全量复制。
3.2.1、全量复制
全量复制流程说明:
-
从节点发送psync命令给主节点,psync ? -1
-
主节点根据命令解析出当前进行全量复制,回复+FULLRESYNC相应。
-
从节点接收主节点的相应数据保存运行ID和偏移量offset。
-
主节点执行bgsave保存RDB文件(持久化)到本地。
-
主节点发送RDB文件给从节点,从节点把接收的RDB文件保存在本地并直接作为从节点的数据文件。
注意:对于数据量较大的主节点,生成的RDB文件超过6GB以上时要格外小心。如果传输时间超过repl-timeout所配置的值(默认60秒),从节点将放弃接受RDB文件并清理已经下载的临时文件,导致全量复制失败。
6.从节点开始接收RDB文件到完成期间,主节点仍然响应读写命令,所以在RDB文件传输期间,主节点会将修改操作保存在缓冲区中。当从节点加载完RDB文件后,主节点再把缓冲区中的数据发送给从节点,保证数据一致。
注意:高流量写入场景可能会导致主节点复制客户端缓冲区溢出,默认配置为client-output-buffer-limit slave 256MB 64MB 60,如果60秒内缓冲区消耗持续大于64MB或者直接超过256MB时,主节点将直接关闭复制客户端连接,造成复制失败。
对于主节点,当发送完所有的数据后就认为全量复制完成,但是对于从节点全量复制还有后续步骤要处理。
7.从节点接收完主节点传送来的全部数据后,会清空自身旧数据。
8.加载RDB文件。
9.加载完毕后,如果当前节点开启了AOF持久化功能,会立刻进行bgrewriteaof操作,为了保证全量复制后AOF持久化文件可立刻可用。
3.2.2、部分复制
当主从复制过程中,如果出现网络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区内存在这部分数据则会直接发给从节点。在减少开销的同时,保证了数据的一致性。流程如下:
部分复制流程说明:
-
当主从网络断开,超过repl-timeout设置的时间,主会认为从故障而中断数据连接。
-
连接中断期间,主节点依然会响应客户端命令,但无法发送给从节点,不过主节点内部存在复制加压缓冲区,所以可以保存最近一段时间的写命令数据,默认最大1MB。
-
当网络恢复,从节点会重新连接到主节点。
-
连接恢复后,从节点会将之前保存的自己已复制的偏移量和运行ID用psync命令发送给主节点,要求进行部分复制操作。
-
主节点接到psync命令后,核对信息。之后根据offset在自身复制积压缓冲区查找,如果偏移量之后的数据存在缓冲区中,则对从节点发送+CONTINUE响应,表示可以进行部分复制。
-
主节点根据偏移量把复制积压缓冲区里的数据发给从节点,保证主从复制进入正常状态。
3.3、心跳(heart beat)
主从节点在建立复制后,他们之间维护着长连接并彼此发送心跳命令。
心跳的关键机制如下:
1、主从都有心跳检测机制,各自模拟成对方的客户端进行通信,通过 client list 命令查看复制相关客户端信息,主节点的连接状态为 flags = M,从节点的连接状态是 flags = S。
2、主节点默认每隔 10 秒对从节点发送 ping 命令,可修改配置repl-ping-slave-period 控制发送频率。
3、从节点在主线程每隔一秒发送 replconf ack{offset}命令,给主节点上报自身当前的复制偏移量。
4、主节点收到 replconf 信息后,判断从节点超时时间,如果超过repl-timeout 60 秒,则判断节点下线。
注意:为了降低主从延迟,一般把 redis主从节点部署在相同的机房/同城机房,避免网络延迟带来的网络分区造成的心跳中断等情况。
3.4、异步复制
主节点不但负责数据读写,还负责把写命令同步给从节点,写命令的发送过程是异步完成,也就是说主节点处理完写命令后立即返回客户端,并不等待从节点复制完成。异步复制的步骤很简单,如下:
1、主节点接受处理命令
2、主节点处理完后返回响应结果
3、对于修改命令,异步发送给从节点,从节点在主线程中执行复制的命令。
四、主从配置管理
4.1、建立复制
主从复制配置方式有三种:
-
在配置文件中加入slaveof {masterHost} {masterPort}随Redis启动生效,若master有密码,则使用CONFlG SET masterauth "your_master_password"配置密码
-
在redis-server启动命令后加入--slaveof {masterHost} {masterPort}
-
直接使用命令redis-cli -h IP slaveof {masterHost} {masterPort}生效。
4.2、断开复制
slaveof命令不但可以建立复制,也可以执行slaveof no one来断开与主节点复制关系。
4.3、安全性
主节点通常会设置requirepass参数进行密码验证,这时所有的客户端访问必须使用auth命令实行校验。因此需要在从节点配置masterauth参数与主节点密码保持一致。
4.4、只读(默认)
默认情况下,从节点使用slave-read-only=yes配置为只读模式。由于复制只能从主节点到从节点,对于从节点的任何修改主节点都是无法感知的,修改从节点会造成主从数据不一致。因此建议线上将从节点设置为只读模式。
4.5、传输延迟
主从节点一般在不同机器上,复制时的网络延迟就成为需要考虑的问题,Redis为我们提供了repl-disable-tcp-nodelay参数用于控制是否关闭TCP_NODELAY,默认为no,为开启状态。
设置为no时,表示开启TCP_NODELAY,允许小包发送,使得网络延迟变小,但是会增加网络带宽消耗。
设置为yes时,表示关闭TCP_NODELAY,主节点会将小包合并发送,默认发送时间间隔取决于Linux内核,一般为40ms。节省了带宽,但是增大了延迟。
五、部署Redis主从复制
5.1、案例环境
系统 | IP地址 | 主机名 | 所需软件 | Redis角色及端口 |
---|---|---|---|---|
OpenEule6.6 | 192.168.71.166 | master.redis.com | master:6379 | |
OpenEule6.6 | 192.168.71.169 | slave.redis.com | slave:6379(6379.conf)slave:6380(6380.conf) |
5.2、案例步骤
-
安装并配置master角色的redis服务;
-
安装并配置slave角色的redis服务(双实例);
-
配置master角色的文件;
-
配置slave角色的文件;
-
验证主从复制功能;
5.3、案例实施
一主一从
修改slave的文件88行
修改slave的文件534行
112行把保护模式关掉
在主服务器上重启redis服务
使用从服务器连接
在主上增改内容,从上可以同步
也可以直接使用命令对master的redis进行操作
配置一主两从
第二个从服务器同样修改
启动redis服务即可登录
此时将170的缓存删除,将主改成169服务器
登录192.168.71.170自己的redis
发现数据已经同步192.168.71.169,而169是同步166的
六.Redis Sentinel哨兵集群
一、Redis主从复制中的问题
Redis主从复制集群可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:第一:作为主节点的一个备份,一旦主节点出了故障不能继续对外提供服务时,从节点可以作为后备"顶"上来,并且保证数据尽量不丢失。第二,从节点可以扩展主节点的读能力,通过实现读写分离结构,可以大大减轻主节点在进行高并发读写操作时的访问压力。
但是主从同步也带了一些问题:
-
一旦主节点故障,需要手动将一个从节点晋升为主节点,slaveof no one。
-
需要修改客户端或者应用程序的主节点地址。
-
如果是一主多从结构,还需将其他从节点调整,让其从新的主节点进行复制而以上整个过程都需要人工干预。
二、Redis高可用方案
Redis Sentinel是Redis的高可用实现方案,在实际的生产环境中,对提高整个系统的高可用性是非常有帮助的。哨兵是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点都会对数据节点和其他Sentinel节点进行监控,当他发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,他还会和其他Sentinel节点进行"协商",当半数以上Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis的应用方。整个过程是完全自动的,不需要人工来介入,所以这套方案很有效的解决了Redis的高可用问题。
整个故障转移的处理逻辑基本上可分为4步:
-
主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败
-
每个Sentinel节点通过定期监控发现主节点出现了故障
-
多个Sentinel节点对主节点的故障达成一致,选举出其中一个Sentinel节点作为领导者(leader)负责本次故障转移工作。
-
-
Sentinel领导者节点执行了故障转移,如下图所示
4.故障转移后整个Redis Sentinel的拓扑关系如下所示
三、Sentinel实现原理
3.1、三个定时监控任务
Redis Sentinel通过三个定时监控任务完成对各个节点发现和监控:
1.每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构,这个定时任务的作用具体可以表现在三个方面:
-
通过向主节点执行info命令,获取从节点的信息,这也是为什么Sentinel节点不需要显示配置监控从节点
-
当有新的从节点加入时都可以立刻感知出来
-
节点不可达或者故障转移后,可以通过info命令实时更新节点拓扑信息
2.每隔2秒,每个Sentinel节点会向Redis数据节点的sentinel:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断,所以这个定时任务可以完成以下两个工作:
-
发现新的Sentinel节点:通过订阅主节点的sentinel:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该Sentinel节点创建连接。
-
Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。
3.每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。
3.2、主观下线
每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这个节点超过down-after-milliseconds时间没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个判定行为叫做主观下线。从字面意思也可以很容易看出主观下线是当前Sentinel节点的一家之言,存在误判断的可能。
3.3、客观下线
当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过Sentinel is-master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,当超过半数的Sentinel节点都认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定,这样客观下线的含义是比较明显了,也就是大部分Sentinel节点都对主节点的下线做了同意的判定,那么这个判定就是客观的。
3.4、领导者选举
故障转移的工作只需要一个Sentinel节点来完成即可,所以Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。Redis使用了Raft算法实现领导者选举,大致思路如下:
-
每个在线的Sentinel节点都有资格成为领导者,当他确认主节点主观下线时,会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。
-
收到命令的节点,如果没有同意过其他节点的请求,则会同意该请求,否则拒绝。
-
如果某一个节点的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为领导者。
-
如果此过程没有选举出领导者,将进入下一次选举。
3.5、故障转移
领导者选举出的Sentinel节点负责故障转移,具体步骤如下:
-
在从节点列表中选出一个节点作为新的主节点,方法如下:
-
过滤:"不健康"(主观下线、断线)、5秒内没有回复过Sentinel节点ping响应,与主节点失联超过down-after-milliseconds*10秒。
-
选择slave-priority(从优先级)最高的从节点列表,如果存在则返回,不存在则继续。
-
选择复制偏移量最大的从节点
-
选择runid最小的从节点
-
领导者节点会对第一步选出来的从节点执行slaveof no one命令,使其成为新主
-
领导者节点会向剩余的从节点发送命令,让他们成为新主的从节点
-
Sentinel节点集合会将原来的主节点更新为从节点并保持对其关注,当其恢复后命令它去复制新的主节点。
四、Sentinel集群部署
4.1、案例环境
系统 | IP地址 | 主机名 | 端口 | Redis角色 |
OpenEule6.6 | 192.168.71.166 | master | 6379 | master |
OpenEule6.6 | 192.168.71.166 | master | 26379 | Sentinel |
OpenEule6.6 | 192.168.71.169 | slave1 | 6379 | slave1 |
OpenEule6.6 | 192.168.71.169 | slave1 | 26379 | Sentinel |
OpenEule6.6 | 192.168.71.170 | slave2 | 6379 | slave2 |
OpenEule6.6 | 192.168.71.170 | slave2 | 26379 | Sentinel |
4.2、案例实施
配置哨兵文件
15行改成yes
92行改ip,最后的2代表一主两从,几个从改成几
重启redis和打开哨兵
检查状态
从服务器只改92行,并打开主从复制
查看状态
此时,关闭master的redis,那么两个从会进行选举,此时master已经被选举成170
登录192.168.71.170查看,发现身份已经更改为master
此时再次启用原master,则会发现原master现在变成slave
此时,再将现master停掉,那么会发现169变成了master
Sentinel集群总结
-
对于master节点故障判断是由多个Sentinel节点共同完成,这样可以有效地防止误判。
-
Sentinel集群可由若干个(奇数)Sentinel节点组成的,这样即使个别Sentinel节点不可用,整个Sentinel节点集合依然是健壮的。
七.Redis Cluster集群理论
常见的几种模式对比
模式 | 版本 | 优点 | 缺点 |
---|---|---|---|
主从模式 | redis2.8之前 | 1、解决数据备份问题 2、做到读写分离,提高服务器性能 | 1、master故障,无法自动故障转移,需人工介入 2、master无法实现动态扩容 |
哨兵模式 | redis2.8级之后的 模式 | 1、Master状态监测 2、master节点故障,自动切换主从,故障自愈 3、所有slave从节点,随之更改新的master节点 | 1、slave节点下线,sentinel不会对其进行故障转移,连接从节点的客户端因为无法获取到新的可用从节点 2、master无法实行动态扩容 |
redis cluster 模式 | redis3.0版本之后 | 1、有效的解决了redis在分布式方面的需求 2、遇到单机内存,并发和流量瓶颈等问题时,可采用 Cluster方案达到负载均衡的目的 3、可实现动态扩容 4、P2P模式,无中心化 5、通过Gossip协议同步节点信息 6、自动故障转移、Slot迁移中数据可用 7、自动分割数据到不同的节点上 8、整个集群的部分节点失败或者不可达的情况下能够继 续处理命令 | 1、架构比较新,最佳实践较少 2、为了性能提高,客户端需要缓存路由表信息 3、节点发现、reshard操作不够自动化 4、不支持处理多个keys的命令,因为这需要在不同的节点间移动数据 5、Redis集群不像单机Redis那样支持多数据库功能,集群只使用默认的0号数据库,并且不能使用SELECT index命令 |
一、redis cluster 是什么
Redis集群是一个由多个主从节点群组成的分布式服务集群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。redis集群的运用主要是针对海量数据+高并发+高可用的场景。
二、集群架构图
在这个图中,每一个蓝色的圈都代表着一个redis的服务器节点。它们任何两个节点之间都是相互连通的。客户端可以与任何一个节点相连接,然后就可以访问集群中的任何一个节点。对其进行存取和其他操作
三、集群原理图
介绍:
对象保存到redis之前先经过CRC16哈希到一个指定的Node上(这个过程即redis cluster的分片),集群内部将所有的key映射到16384个Slot中,集群中的每个Redis Instance负责其中的一部分的Slot的读写。集群客户端连接集群中任一Redis Instance即可发送命令,当Redis Instance收到自己不负责的Slot的请求时,会将负责请求Key所在Slot的Redis Instance地址返回给客户端,客户端收到后自动将原请求重新发往这个地址,对外部透明。一个Key到底属于哪个Slot由 (HASH_SLOT = CRC16(key) mod 16384) 决定。只有master节点会被分配槽位,slave节点不会分配槽位。
四、集群通信
1)集群发现:MEET
最开始时,每个redis实例自己是一个集群,我们通过cluster meet让各个节点互相“握手”,需要续建一个真正可工作的集群,我们必须将各个节点连接起来,构成一个包含多个节点的集群。连接各个节点的工作使用CLUSTER MEET命令来完成。
CLUSTER MEET命令实现:
-
节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
-
节点A根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条MEET消息。
-
节点B接收到节点A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
-
节点B向节点A返回一条PONG消息。
-
节点A将受到节点B返回的PONG消息,通过这条PONG消息节点A可以知道节点B已经成功的接收了自己发送的MEET消息。
-
之后,节点A将向节点B返回一条PING消息。
-
节点B将接收到的节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功的接收到了自己返回的PONG消息,握手完成。
-
之后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点B进行握手,最终,经过一段时间后,节点B会被集群中的所有节点认识。
2)gossip协议
gossip协议包含多种消息,包含ping、pong、meet、fail等
-
meet:某个节点在内部发送了一个gossip meet 消息给新加入的节点,通知那个节点去加入我们的集群。然后新节点就会加入到集群的通信中
-
ping:每个节点都会频繁给其它节点发送 ping,其中包含自己的状态还有自己维护的集群元数据,互相通过 ping 交换元数据
-
pong:ping 和 meet消息的返回响应,包含自己的状态和其它信息,也用于信息广播和更新
-
fail:某个节点判断另一个节点 fail 之后,就发送 fail 给其它节点,通知其它节点说这个节点已宕机
五、集群概念
1)多slave选举
选新主的过程基于Raft协议选举方式来实现的
-
当从节点发现自己的主节点进行已下线状态时,从节点会广播一条CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到这条消息,并且具有投票权的主节点向这个从节点投票
-
如果一个主节点具有投票权,并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条,CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点
-
每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持
-
如果集群里有N个具有投票权的主节点,那么当一个从节点收集到大于等于集群N/2+1张支持票时,这个从节点就成为新的主节点
-
如果在一个选举周期没有从能够收集到足够的支持票数,那么集群进入一个新的选举周期,并再次进行选主,直到选出新的主节点为止
2)slot(槽)
Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1、2、3……16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster中的每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。这里值得一提的是,在Redis Cluster中,只有Master才拥有槽的所有权,如果是某个Master的slave,这个slave只负责槽的使用,但是没有所有权。
3)数据分片
在Redis Cluster中,拥有16384个slot,这个数是固定的,存储在Redis Cluster中的所有的键都会被映射到这些slot中。数据库中的每个键都属于这16384个哈希槽的其中一个,集群使用公式CRC16(key) % 16384来计算键key属于哪个槽,其中CRC16(key)语句用于计算键key的CRC16校验和。集群中的每个节点负责处理一部分哈希槽。
4)请求重定向
由于每个节点只负责部分slot,以及slot可能从一个节点迁移到另一节点,造成客户端有可能会向错误的节点发起请求。因此需要有一种机制来对其进行发现和修正,这就是请求重定向。有两种不同的重定向场景:
MOVED错误
-
请求的key对应的槽不在该节点上,节点将查看自身内部所保存的哈希槽到节点ID的映射记录, 节点回复一个MOVED错误。
-
需要客户端进行再次重试
ASK错误(一般发生在数据迁移的过程中)
-
请求的key对应的槽目前的状态属于MIGRATING状态,并且当前节点找不到这个key了,节点回 复ASK错误。ASK会把对应槽的IMPORTING节点返回给你,告诉你去IMPORTING的节点尝试找找。
-
客户端进行重试首先发送ASKING命令,节点将为客户端设置一个一次性的标志(flag),使得 客户端可以执行一次针对IMPORTING状态的槽的命令请求,然后再发送真正的命令请求。
-
不必更新客户端所记录的槽至节点的映射。
5)数据迁移
当槽x从Node A向Node B迁移时,Node A和Node B都会有这个槽x,Node A上槽x的状态设置为MIGRATING,Node B上槽x的状态被设置为IMPORTING。
MIGRATING状态
-
如果key存在则成功处理
-
如果key不存在,则返回客户端ASK,客户端根据ASK首先发送ASKING命令到目标节点,然后发送请求的命令到目标节点
-
当key包含多个命令
-
如果都存在则成功处理
-
如果都不存在,则返回客户端ASK
-
如果一部分存在,则返回客户端TRYAGAIN,通知客户端稍后重试,这样当所有的 key都迁移完毕的时候客户端重试请求的时候回得到ASK,然后经过一次重定向就 可以获取这批键
-
-
此时不刷新客户端中node的映射关系
IMPORTING状态
-
如果key不在该节点上,会被MOVED重定向,刷新客户端中node的映射关系
-
如果是ASKING命令则命令会被执行,key不在迁移的节点已经被迁移到目标的节点
-
Key不存在则新建
6)故障转移
当从节点发现自己的主节点变为已下线(FAIL)状态时,便尝试进Failover,以期成为新的主。
以下是故障转移的执行步骤:
-
从下线主节点的所有从节点中选中一个从节点
-
被选中的从节点执行SLAVEOF NO ONE命令,成为新的主节点
-
新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己
-
新的主节点对集群进行广播PONG消息,告知其他节点已经成为新的主节点
-
新的主节点开始接收和处理槽相关的请求
-
集群slots必须完整才能对外提供服务
当redis.conf的配置cluster-require-full-coverage为no时,表示当负责一个插槽的主库下线且没有相应的从库进行故障恢复时,集群仍然可用,如果为yes则集群不可用。
六、集群健康检查机制
-
集群的每个节点都会相互发送一个活跃的ping包,当ping包确认返回的时间超过node_timeout的时间,我们认为节点失效
-
当然,当节点在等待时间超过一半node_timeout的时间还没有收到目标节点对于ping包的回复的时候,就会立马尝试重连该节点,这个机制可以确保链接都保证有效,所以节点之间的失效链接都不会导致错误的失效报告
-
节点从正常状态到fail状态,需要收集每个节点对不正常节点(B)的确认:1)当节点(A)发送的ping包没有返回,此时将B节点的状态信息标记为(PFAIL)状态,然后将信息发送到集群的其它节点,同理当A收集本地有关B状态的信息,当大多数主节点认为B节点是PFAIL状态时,节点A将标记B的状态为FAIL状态,然后向所有的可达节点发送这个消息
-
当大多数主节点都将B节点标示为FAIL状态时,B节点才最终被集群标记为FAIL状态,此时B1(B的从节点)提升为主节点提供服务
-
本质上来说,FAIL 标识只是用来触发从节点提升(slave promotion)算法的安全部分。理论上一个从节点会在它的主节点不可达的时候独立起作用并且启动从节点提升程序,然后等待主节点来拒绝认可该提升(如果主节点对大部分节点恢复连接)。PFAIL -> FAIL 的状态变化、弱协议、强制在集群的可达部分用最短的时间传播状态变更的 FAIL 消息,这些东西增加的复杂性有实际的好处。由于这种机制,如果集群处于错误状态的时候,所有节点都会在同一时间停止接收写入操作,这从使用 Redis 集群的应用的角度来看是个很好的特性。还有非必要的选举,是从节点在无法访问主节点的时候发起的,若该主节点能被其他大多数主节点访问的话,这个选举会被拒绝掉
七、注意事项
-
redis master 宕机,恢复后不会自动切为主
-
扩容redis cluster如果我们大量使用redis cluster的话,有一个痛点就是扩容的机器加入集群的时候,分配主从。现在只能使用命令去操作,非常的凌乱。而且如果redis cluster是线上集群或者是有数据的话,极其容易造成丢数据,或者扩容是hang住等..隐患
-
redis cluster 从节点支持可读的前提下得在执行readonly=yes后执行,程序读取从节点时也需要执行。一次连接需要执行一次,客户端关闭即失效。
-
redis cluster 启动必须以绝对路径的方式启动,如果不用绝对路径启动,会产生新的nodes.conf 文件,集群的主从对应关系就会变乱,从而导致集群奔溃。
八、业务使用Redis Cluster注意事项
操作 | 业务侧需改造内容 | 示例 | 说明 |
---|---|---|---|
批量操作 | 需要保证批量操作的key在同一个slot中 | mset {test4}:test4 6 {test4}:test8 10 | 1.示例中的key是{type}:keyword格式,当key中包含{type}的时候,会使用type进行hash运算去判断slot,keyword不影响运算结果。 2.示例中的test4和test8不在同一个slot,但是这两个slot分配给了同一个server。 |
事务操作 | 需要保证事务中的key在同一个slot中 | MULTI set {test4}:test4 14 get {test4}:test4 EXEC | |
Lua脚本 | 需要保证Lua脚本中的key在同一个slot中 | EVAL "return {KEYS[1],KEYS[2]}" 2 {test4}:key1 {test4}:key2 | |
PipeLine | 需要保证PipeLine中的key在同一台Server上 | Jedis jedis = new Jedis("10.138.20.141", 6379); Pipeline pipelined = jedis.pipelined(); pipelined.set("test4","pipelineTest4"); pipelined.set("test8","pipelineTest8"); List<Object> objects = pipelined.syncAndReturnAll(); | |
代码控制 | 连接方式和命令执行的依赖需要改变 | JedisCluster jedisCluster = new JedisCluster(new HostAndPort("10.138.20.141", 6379)); jedisCluster.set("test4","abcd"); | 1.cluster需要连接到这个key所在的节点上,才能进行操作。 2.一般都会有封装好的依赖包,例如Jedis(Java) |
多DB | 不支持多DB | select命令禁用 |
八.Redis Cluster 手动部署
一、环境规划
主机名 | IP地址 | 端口 | 描述 |
---|---|---|---|
redis-master | 192.168.71.166 | 6379 | redis-master01 |
redis-master | 192.168.71.169 | 6379 | redis-master02 |
redis-master | 192.168.71.170 | 6379 | redis-master03 |
redis-slave | 192.168.71.166 | 6380 | redis-slave01 |
redis-slave | 192.168.71.169 | 6382 | redis-slave02 |
redis-slave | 192.168.71.170 | 6384 | redis-slave03 |
二、基础环境
配置文件,在redis.conf 最后增加以下语句
然后通过绝对路径启动(若无法开启,则先将yes改成no,再普通打开,再改回yes,再绝对路径启动)
此时已经生成了nodes.文件
开启其他两台服务(要先将主从注释掉)
此时启动不了,先将每个/var/lib/redis/目录下的缓存全部清掉,然后把redis.conf的310行改成yes
此时就可以启动了
现在将其他节点加入集群
再启动端口6380(配置redis_6380.conf)cp原文件将79改80,并在最下边加上
启动6380文件
然后将该文件复制给其他IP,修改bind后的IP即可
全部启动
然后继续将其他从节点加入集群
分配slot
规划谁是master节点
然后在对应的主机上设置从,将上图前面的id粘贴下来(若无法成功,检查是否没关保护模式,改成no并重启服务即可)
三台重复操作,完成主从搭配
集群操作命令
命令 | 作用 |
---|---|
cluster info | 打印集群的信息 |
cluster nodes | 列出集群当前已知的所有节点( node),以及这些节点的相关信息。 |
cluster meet <ip> <port> | 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。 |
cluster forget <node_id> | 从集群中移除 node_id 指定的节点。 |
cluster replicate <master_node_id> | 将当前从节点设置为 node_id 指定的master节点的slave节点。只能针对slave节点操作。 |
cluster saveconfig | 将节点的配置文件保存到硬盘里面。 |
cluster addslots <slot> [slot ...] | 将一个或多个槽( slot)指派( assign)给当前节点。 |
cluster delslots <slot> [slot ...] | 移除一个或多个槽对当前节点的指派。 |
cluster flushslots | 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。 |
cluster setslot <slot> node <node_id> | 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。 |
cluster setslot <slot> migrating <node_id> | 将本节点的槽 slot 迁移到 node_id 指定的节点中。 |
cluster setslot <slot> importing <node_id> | 从 node_id 指定的节点中导入槽 slot 到本节点。 |
cluster setslot <slot> stable | 取消对槽 slot 的导入( import)或者迁移( migrate)。 |
cluster keyslot <key> | 计算键 key 应该被放置在哪个槽上。 |
cluster countkeysinslot <slot> | 返回槽 slot 目前包含的键值对数量。 |
cluster getkeysinslot <slot> <count> | 返回 count 个 slot 槽中的键 。 |
cluster reset | 重置集群命令 |
三、故障恢复
1、模式故障
kill -9 PID #杀死其中一个redis进程
2、故障恢复
-
启动杀死的redis进程
-
通过 cluster nodes查看节点状态
-
通过 cluster info 查看集群状态
-
登录所有从节点
3、cluster failover命令
Redis Cluster模式下的cluster failover(集群故障转移)的作用是确保Redis集群在主节点(Master)发生故障或不可用的情况下,能够自动将主节点的工作负载转移到备用节点(Slave)上,并使备用节点成为新的主节点,保证Redis集群的高可用性和持续可用性。
当主节点发生故障时,Redis Cluster会自动检测到主节点的不可用,并从备用节点中选出一个合适的备用节点作为新的主节点。该备用节点经过选举后,会接管原主节点的数据和工作负载,并开始对外提供服务。集群的其它节点也会更新集群拓扑信息,以便客户端能够正确地路由请求到新的主节点。
cluster failover的作用主要包括:
-
高可用性:当主节点发生故障时,能够及时切换到备用节点,保证Redis集群的继续正常运行,避免因单点故障而导致服务不可用。
-
数据保护:在主节点发生故障时,能够将数据从主节点复制到备用节点,并在故障转移时将备用节点提升为新的主节点,从而保证数据的持久性和一致性。
-
自动化管理:集群故障转移是自动化过程,无需人工干预,减少了对系统运维的依赖,并提高了系统的可靠性和可维护性。
九.总结
本节内容全面介绍Redis数据库及其高可用集群部署,还有Redis主从复制原理与部署,Sentinel哨兵机制实现故障转移,以及Redis Cluster集群的数据分片与自动容错机制,很有用。