Redis 入门
Redis的全称是什么?
Remote Dictionary Server。
Redis集群最大节点个数是多少?
16384个。
Redis有哪些适合的场景?
1、会话缓存(Session Cache)
最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
2、全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降
3、队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
4、排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。
5、发布/订阅
发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!

缓存类型
本地缓存
本地缓存就是在进程的内存中进行缓存,比如我们的 JVM 堆中,可以用 LRUMap 来实现,也可以使用 Ehcache 这样的工具来实现。
本地缓存是内存访问,没有远程交互开销,性能最好,但是受限于单机容量,一般缓存较小且无法扩展。
分布式缓存
分布式缓存一般都具有良好的水平扩展能力,对较大数据量的场景也能应付自如。缺点就是需要进行远程请求,性能不如本地缓存。
多级缓存
为了平衡这种情况,实际业务中一般采用多级缓存,本地缓存只保存访问频率最高的部分热点数据,其他的热点数据放在分布式缓存中。
Redis过期键的删除策略
- 定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
- 惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!
参考:https://www.cnblogs.com/zjoch/p/11149278.html
redis 内存淘汰机制(redis内存耗尽,会发生内存淘汰)
redis 配置文件 redis.conf 中有相关注释,大家可以自行查阅或者通过这个网址查看: http://download.redis.io/redis-stable/redis.conf
剔除策略有 FIFO 淘汰最早数据、LRU 剔除最近最少使用、和 LFU 剔除最近使用频率最低的数据几种策略。
redis 提供 6种数据淘汰策略:
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的).
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
Redis的同步机制
同步(sync):将从节点的数据库状态更新至与主节点的数据库状态一致
- 从节点向主节点发送 SYNC 指令
- 收到 SYNC 指令,主节点执行 BGSAVE 指令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始执行的所有写指令
- 主节点 BGSAVE 指令执行后,会将生成的 RDB 文件发送给从节点
- 从节点接收、载入 RDB 文件,将数据库状态更新至主节点执行 BGSAVE 指令时的数据库状态
- 从节点加载完 RDB 文件,通知主节点将记录在缓冲区里面的所有写指令发送给从节点,从节点执行这些写指令,将数据库状态更新至主节点当前数据库状态
指令传播(command propagate):主节点数据被修改,会主动向从节点发送执行的写指令,从节点执行之后,两个节点数据状态又保持一致
为了解决主从节点断线复制低效的问题(SYNC过程中生成、传输、载入 RDB 文件耗费大量 CPU、内存、磁盘 IO 资源),2.8 版开始新增 PSYNC 指令。
PSYNC 具有两种模式
- 完整重同步(full resynchronization),与SYNC过程基本一致
- 部分重同步(partial resynchronization),借助复制偏移量、复制积压缓冲区、服务器运行 ID ,完成主从节点断开连接后,从节点重连主节点后,条件允许,主节点将连接断开期间执行的写指令发送给从节点,从节点接收并执行写指令,将数据库更新至主节点当前状态
redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)
快照(snapshotting)持久化(RDB)
保存某个时间点的全量数据快照
-
手动触发
-
SAVE:阻塞Redis的服务器进程,知道RDB文件被创建完毕
-
BGSAVE:Fork出一个子进程来创建RDB文件,不阻塞服务器进程
lastsave指令可以查看最近的备份时间
-
-
自动触发
-
根据redis.conf配置里的save m n定时触发(用的是BGSAVE)
-
主从复制时,主节点自动触发
-
执行Debug Relaod
-
执行Shutdown且没有开启AOF持久化
-
快照持久化是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:
# 在几秒内改动了多少数据就触发持久化 # 想禁用的话不设置save 或者save "" # 在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。 save 900 1 # 在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。 save 300 10 # 在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。 save 60 10000 # 备份进程出错主进程停止写入操作 stop-writes-on-bgsave-error yes # 是否压缩rdb文件 推荐no 相对于硬盘成本cpu更值钱 rdbcompression yes
AOF(append-only file)持久化
-
记录除了查询以外的所有变更数据库状态的指令
-
以append的形式追加保存到AOF文件中(增量)
-
日志重写解决AOF文件不断增大的问题,原理如下:
-
调用fork,创建一个子进程
-
子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件
-
主进程持续将新的变动同时写到内存和原来的AOF里
-
主进程获取子进程重写AOF完成信号,往新AOF同步增量变动
-
使用新的AOF文件替换掉旧的AOF文件
-
# 默认关闭若要开启将no改为yes appendonly no # append文件的名字 appendfilename "appendonly.aof" # 当AOF文件大小的增长率大于该配置项时自动开启重写(这里指超过原大小的100%)。 auto-aof-rewrite-percentage 100 # 当AOF文件大小大于该配置项时自动开启重写 auto-aof-rewrite-min-size 64mb
刷盘策略:
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
Redis 4.0 对于持久化机制的优化
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。
- 如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
- AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,但体积更小。
- AOF重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有AOF文件进行任伺读入、分析或者写入操作。
- 在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作
数据恢复
当我们开启了混合持久化时,启动redis依然优先加载aof文件,aof文件加载可能有两种情况如下:
aof文件开头是rdb的格式, 先加载 rdb内容再加载剩余的 aof。
aof文件开头不是rdb的格式,直接以aof格式加载整个文件。
实践
开启混合持久化,并在开启后立马执行写操作,为了证实混合持久化的后半部分AOF过程

查看日志:

此时的aof文件已经和只开启AOF持久化文件不一样了,上半部分是RDB持久化的数据,下半部分是AOF格式数据。

优缺点
RDB
优点:
RDB 是一个非常紧凑(compact)的文件,体积小,因此在传输速度上比较快,因此适合灾难恢复。
RDB 可以最大化Redis 的性能:父进程在保存RDB 文件时唯一要做的就是fork出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘I/O 操作。
RDB 在恢复大数据集时的速度比AOF 的恢复速度要快。
缺点:
RDB是一个快照过程,无法完整的保存所以数据,尤其在数据量比较大时候,一旦出现故障丢失的数据将更多。
当redis中数据集比较大时候,RDB由于RDB方式需要对数据进行完成拷贝并生成快照文件,fork的子进程会耗CPU,并且数据越大,RDB快照生成会越耗时。
RDB文件是特定的格式,阅读性差,由于格式固定,可能存在不兼容情况。
AOF
优点:
数据更完整,秒级数据丢失(取决于设置fsync策略)。
兼容性较高,由于是基于redis通讯协议而形成的命令追加方式,无论何种版本的redis都兼容,再者aof文件是明文的,可阅读性较好。
缺点:
数据文件体积较大,即使有重写机制,但是在相同的数据集情况下,AOF文件通常比RDB文件大。
相对RDB方式,AOF速度慢于RDB,并且在数据量大时候,恢复速度AOF速度也是慢于RDB。
由于频繁地将命令同步到文件中,AOF持久化对性能的影响相对RDB较大,但是对于我们来说是可以接受的。
混合持久化
优点:
混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
缺点:
兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差
相关命令
aof文件检查
redis-check-aof /etc/redis/appendonly.aof
rdb文件检查
redis-check-rdb /etc/redis/dump.rdb
查看持久化信息
info Persistence
查看状态信息
info stats
缓存雪崩和缓存穿透问题解决方案
缓存雪崩
简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决办法:
- 事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
- 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
- 事后:利用 redis 持久化机制保存的数据尽快恢复缓存
缓存穿透
简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。例如,对用户信息进行了缓存,但恶意攻击者使用不存在的用户id频繁请求接口,导致查询缓存不命中,然后穿透 DB 查询依然不命中。
解决办法:
- 对不存在的用户,在缓存中保存一个空对象(过期时间会很短,最长不超过五分钟)进行标记,防止相同 ID 再次访问 DB。不过有时这个方法并不能很好解决问题,可能导致缓存中存储大量无用数据。
- 使用 BloomFilter 过滤器,BloomFilter 的特点是存在性检测,如果 BloomFilter 中不存在,那么数据一定不存在;如果 BloomFilter 中存在,实际数据也有可能会不存在。非常适合解决这类的问题。
缓存击穿
缓存击穿,就是某个热点数据失效时,大量针对这个数据的请求会穿透到数据源。
解决这个问题有如下办法。
- 可以使用互斥锁更新,保证同一个进程中针对同一个数据不会并发请求到 DB,减小 DB 压力。
- 使用随机退避方式,失效时随机 sleep 一个很短的时间,再次查询,如果失败再执行更新。
- 针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。
单节点方案:Redis Standalone
原文地址:https://wangxin1248.github.io/linux/2018/07/ubuntu18.04-install-redis.html
原理简介
这是最简单的 redis 部署方案,所有数据存储和读写操作都在同一个 redis 服务上。
这种方式优点很明显:部署简单,无论是部署成本还是运维成本都很低,本地测试时最常见也是最方便的方式。
但同时缺点也很明显:不能实现高可用,也不能应对高并发场景,也无法轻易水平拓展,数据储存量很容易见顶。
安装 Redis
$ sudo apt update $ sudo apt install redis-server
卸载redis
sudo apt-get remove --purge redis-server sudo apt autoremove
Redis常用命令
启动 redis 服务:
$ sudo service redis start
/usr/sbin/redis-server /etc/redis/redis_9011.conf --daemonize yes --port 9011
关闭 redis 服务:
$ sudo service redis stop
重启 redis 服务:
$ sudo service redis restart
redis-server -v
查看redis运行情况
ps -ef | grep redis app 21794 1 0 Jan28 ? 03:31:25 ./redis-server *:6379
redis 客户端连接
在命令行中输入如下命令来登陆进 redis 客户端
$ redis-cli
redis 远程连接
打开配置文件
$ sudo vi /etc/redis/redis.conf 将 bind 127.0.0.1 ::1 改为 bind 0.0.0.0
之后重新启动 redis
Redis 设置密码
打开 Redis 的配置文件
sudo vi /etc/redis/redis.conf
修改密码
# 找到下面这一行,默认密码为foobared # requirepass foobared requirepass 123456 #将注释符号去掉,将后面修改成自己的密码
源码安装
wget http://download.redis.io/releases/redis-5.0.7.tar.gz # 下载 Redis 二进制安装包: tar –zxvf redis-5.0.7.tar.gz # 解压二进制包 cd redis-5.0.7 # 进入解压文件夹并编译二进制文件 make ./src/redis-server start # 启动Redis服务,好像不需要make install
在 make install 和 make test 的时候可能会遇到下面这个问题:
You need tcl 8.5 or newer in order to run the Redis test
make: *** [test] Error 1
这是因为系统中的 TCL 语言版本太低,TCL 语言是一种工具脚本语言,在安装 Redis 的过程中 make test 命令需要用到这个脚本语言,这个时候我们需要升级一下系统中的 TCL 版本:
# 下载一个高于 8.5 版本的 TCL 安装包,比如 8.6.8
wget http://downloads.sourceforge.net/tcl/tcl8.6.8-src.tar.gz
# 解压
tar -zxvf tcl8.6.8-src.tar.gz -C /usr/local/
# 切换到解压后的源码目录
cd /usr/local/tcl8.6.8/unix/
# 编译和安装
sudo ./configure
sudo make
sudo make install
分布式与集群
下文会涉及到大量的分布式和集群术语,这里我们先来复习一下集群和分布式的概念,加深一下理解。
目前的项目很少会采用单机架构了,一是因为单机性能有限,二是因为单机服务一旦故障整个系统就无法继续提供服务了。所以目前集群和分布式的架构使用得很广泛,主要就是为了解决上述两个问题,一个性能问题,一个故障问题,通过分布式架构解决性能(高并发)问题,通过集群架构解决故障服务(高可用)问题。
分布式架构
分布式:一个业务分拆多个子业务,部署在不同的服务器上
网上很多文章把分布式架构说得很复杂,但都没有切中关键,其实理解起来很简单,所有的计算机系统都是为业务服务的,将同一个业务拆分成多个子业务,各个子业务部署在不同的服务器上,这就是分布式架构,通过将业务拆细,为不同的子业务配置不同性能的服务器,提高整个系统的性能。我个人认为目前很火的微服务概念其实本质上就是分布式。
按照类型大致可以分为两种:分布式计算和分布式存储。
分布式计算很好理解,就是将大量计算任务分配到多个计算单元上以提高总计算性能。例如暴力破解某个密码需要遍历某个字符组合10万次,假设一台计算机需要10分钟,那么10台计算机同时遍历,每台遍历1万次,最后将结果汇总,那么就只需要1分钟。这10台计算机组合起来就是一个分布式计算系统,这里的业务就是计算。
同理,分布式储存也很好理解,就是将大量数据分配到多个储存单元上以提高总存储量。例如100ZB的数据一个储存单元放不下,那就拆成100份,每个储存单元存1份,那么这100个存储单元组合起来就是一个分布式储存系统,这里的业务就是存储。目前主流的关系型数据库都有比较成熟的分布式存储方案,如 MySQL 的 MySQL Fabric、MyCat 等,Oracle Database 有Oracle Sharding 等。Redis 作为流行的非关系型数据库,由于是内存数据库,理论上一般不会在 Redis 中存放太多的数据,但是在某些特殊情况下还是会有储存空间不够的情况,或者需要预防储存空间不够的情况发生,这个时候就需要 Redis 分布式架构了。
例如某集团下有很多的子公司,每个子公司都有多套 IT 系统,其中很多 IT 系统都是需要使用 Redis 的,集团为了统一管理,搭建了一套中央 Redis 系统,要求所有子公司下的 IT 系统统一使用集团的中央 Redis 库,这个时候即使当前储存容量够用,但是为了应对后期发展就必须使用到分布式储存,因为分布式架构理论上都支持无限水平拓展。
集群架构
集群:同一个业务,部署在多个服务器上
集群同样也非常好理解,就是在多个服务器上部署同一个业务,这样可以起到两个作用:
- 分散每台服务器的压力
- 任意一台或者几台服务器宕机也不会影响整个系统
例如一个典型的 Web 集群服务架构图如下:

这里三个 Web Server 服务器实际上都是运行着同一套业务,但是三台服务器就可以显著分散单台服务器压力,并且任意一台宕机也不会导致无法提供服务。
分布式与集群的关系
分布式和集群区别很好理解,用下面一张图表示:
需要注意的是分布式不一定能用上,但是集群一般都是需要的。因为不是所有系统都需要应对高并发场景,但高可用是一个系统能够长期稳定运行基本保障。因此用到分布式架构的系统基本上都会用到集群,而用集群架构的系统却不一定会用到分布式。
Redis 部署指南
Redis 高可用方案:Redis Sentinel
原理简介
Redis Sentinel 是 Redis 官方推荐的高可用性(HA)解决方案,这是生产环境中最实用也是最常用的方案。
这里涉及到另一个概念:master-slaver(主从模式)。很好理解,就是常用的主备模式,例如 nginx 的主备模式。一个主 redis 节点可以配置多个从节点,当主节点挂掉时,从节点自动顶上代替主节点,这样就可以有效的避免一个节点挂掉导致整个系统挂掉的问题,实现 redis 服务的高可用。如下图:
但是这个方案需要解决两个基本问题:
- 如何提前判断各个节点(尤其是主节点)的运行健康状况?
- 当主节点宕机的时候如何从多个从节点中选出一个作为新的主节点并实现自动切换?
这时 Redis Sentinel 应运而生,它主要有以下三个特点:
- 监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification):当被监控的某个 Redis 服务器出现问题时,Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。
总结来说就是 sentinel 可以监控一个或者多个 master-slaver 集群,定时对每个节点进行健康检查,可以通过 API 发送通知,并自动进行故障转移。这时r redis 结构就变成了
使用了 redis sentinel 之后客户端不再直接连接 redis 节点获取服务,而是使用 sentinel 代理获取 redis 服务,类似 Nginx 的代理模式。那么这里又有一个新问题,就是如果 sentinel 宕机了,那么客户端就找不到 redis 服务了,所以 sentinel 本身也是需要支持高可用。
好在sentinel 本身也支持集群部署,并且各个 sentinel 之间支持自动监控,如此一来 redis 主从服务和 sentinel 服务都可以支持高可用。预期结构如下:
部署实例
master-slaver 一主二从
那么下面我们就来实操一下,以下过程大部分参考 redis 官方 Redis Sentinel 文档。
安装 redis 就不重复了,和单机 redis 一样。
redis 解压后,redis home 目录下有 redis 配置的样例文件,我们不直接在此文件上就行修改,在redis home目录下新建文件夹 master-slave ,将配置文件都放于此目录下,下面是三个 redis 节点配置的关键部分
-
master 配置文件:redis-6379.conf
port 6379 daemonize yes logfile "6379.log" dbfilename "dump-6379.rdb" dir "/opt/soft/redis/data"
- slave-1 配置文件:redis-6380.conf
port 6380 daemonize yes logfile "6380.log" dbfilename "dump-6380.rdb" dir "/opt/soft/redis/data" # 关键配置:将这个 redis 指定为某个第一个 redis 的 slaver slaveof 127.0.0.1 6379
- slave-2 配置文件:redis-6381.conf
port 6381 daemonize yes logfile "6381.log" dbfilename "dump-6381.rdb" dir "/opt/soft/redis/data" # 关键配置:将这个 redis 指定为某个第一个 redis 的 slaver slaveof 127.0.0.1 6379
分别启动这三个 redis 服务,启动过程就不罗嗦了,和分别启动三个单机 redis 是一样的,分别指定三个配置文件即可。启动后如下图所示:

6379、6380、6381 端口分别在运行一个 redis-server。
接下来查看这三个 redis-server 之间的关系:连接到主 redis 上用 info replication即可查看
可以看到当前连接的 redis 服务为 master 角色,下面有两个 slaver,IP 和端口都能看到。
这样我们就顺利的完成了 一主二从 redis 环境的搭建,下面开始搭建 sentinel 集群。
sentinel 集群
sentinel 本质上是一个特殊的 redis,大部分配置和普通的 redis 没有什么区别,主要区别在于端口和其哨兵监控设置,下面是三个典型的 sentinel 配置文件中的关键内容:
- sentinel-26379.conf
#设置 sentinel 工作端口 port 26379 #后台运行 daemonize yes #日志文件名称 logfile "26379.log" #设置当前 sentinel 监控的 redis ip 和 端口 sentinel monitor mymaster 127.0.0.1 6379 2 #设置判断 redis 节点宕机时间 sentinel down-after-milliseconds mymaster 60000 #设置自动故障转移超时 sentinel failover-timeout mymaster 180000 #设置同时故障转移个数 sentinel parallel-syncs mymaster 1
- sentinel-26380.conf
#设置 sentinel 工作端口 port 26380 #后台运行 daemonize yes #日志文件名称 logfile "26380.log" #设置当前 sentinel 监控的 redis ip 和 端口 sentinel monitor mymaster 127.0.0.1 6379 2 #设置判断 redis 节点宕机时间 sentinel down-after-milliseconds mymaster 60000 #设置自动故障转移超时 sentinel failover-timeout mymaster 180000 #设置同时故障转移个数 sentinel parallel-syncs mymaster 1
- sentinel-26381.conf
#设置 sentinel 工作端口 port 26391 #后台运行 daemonize yes #日志文件名称 logfile "26381.log" #设置当前 sentinel 监控的 redis ip 和 端口 sentinel monitor mymaster 127.0.0.1 6379 2 #设置判断 redis 节点宕机时间 sentinel down-after-milliseconds mymaster 60000 #设置自动故障转移超时 sentinel failover-timeout mymaster 180000 #设置同时故障转移个数 sentinel parallel-syncs mymaster 1
针对几个监控设置的配置做一下详细说明:
-
sentinel monitor [master-group-name] [ip] [port] [quorum]
这个命令中【master-group-name】是 master redis 的名称;【ip】和【port】分别是其 ip 和端口,很好理解。最后一个参数【quorum】是”投票数“
举个栗子,redis 集群中有3个 sentinel 实例,其中 master 挂掉了,如果这里的票数是2,表示有2个 sentinel 认为 master 挂掉啦,才能被认为是正真的挂掉啦。其中 sentinel 集群中各个 sentinel 之间通过 gossip 协议互相通信。
具体怎样投票还涉及到 redis 集群中的【主观下线】和【客观下线】的概念,后面再详细介绍。-
down-after-milliseconds
sentinel 会向 master 发送心跳 PING 来确认 master 是否存活,如果 master 在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个 sentinel 会主观地认为这个 master 已经不可用了。而这个down-after-milliseconds 就是用来指定这个“一定时间范围”的,单位是毫秒。
-
failover-timeout
这个参数 redis 官方文档中并未做详细说明,但是很好理解,就是 sentinel 对 redis 节点进行自动故障转移的超时设置,当 failover(故障转移)开始后,在此时间内仍然没有触发任何 failover 操作,当前sentinel 将会认为此次故障转移失败。
-
parallel-syncs
当新master产生时,同时进行 slaveof 到新 master 并进行同步复制的 slave 个数,也就是同时几个 slave 进行同步。因为在 salve 执行 salveof 与新 master 同步时,将会终止客户端请求,因此这个值需要权衡。此值较大,意味着“集群”终止客户端请求的时间总和和较大,此值较小,意味着“集群”在故障转移期间,多个 salve 向客户端提供服务时仍然使用旧数据。
我们配置三个 sentinel 几点组成一个 sentinel 集群,端口分别是 23679,23680,23681
-
然后就可以启动 sentinel 集群了
启动 sentinel 有两种方式:
redis-sentinel /path/to/sentinel.confredis-server /path/to/sentinel.conf --sentinel
这两种启动方式没有区别,按照顺序分别启动三个 sentinel 节点之后,我们任意连接其中的一个 sentinel 节点查看集群关系,如下图:
我们连接 26379 这个端口的 sentinel,用 info sentinel命令可以看到这个 sentinel 监控的 master redis 服务的 ip,端口,以及 maste 的 slaver 节点数量,以及 sentinel 的数量。
再连接 26380 这个节点试试:
可以看到结果和上面一样。
如此,我们的 sentinel 集群也部署完成了
那么,当前这个 redis sentinel 高可用集群的做种拓扑图如下:

高可用故障测试
下面我们来测试一下这个高可用方案的实际能力。
我们手动把一主二从中的主节点 kill 掉:

然后连接 6380 节点,查看集群状态:

可以看到 6380 节点已经自动升级为了 master 节点,还有 6381 这一个 slaver 节点,自动故障转移成功
我们再手动启动 6379 节点,观察集群状态:

如图,6379节点重新启动后,自动变成了 6380 节点的从节点。
如此一套完整的 redis 高可用方案就部署完成了。
Redis 主观下线和客观下线
前面说过, Redis 的 Sentinel 中关于下线(down)有两个不同的概念:
- 主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断。
- 客观下线(Objectively Down, 简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后, 得出的服务器下线判断。 (一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线)
如果一个服务器没有在 master-down-after-milliseconds 选项所指定的时间内, 对向它发送 PING 命令的 Sentinel 返回一个有效回复(valid reply), 那么 Sentinel 就会将这个服务器标记为主观下线。
服务器对 PING 命令的有效回复可以是以下三种回复的其中一种:
- 返回 +PONG 。
- 返回 -LOADING 错误。
- 返回 -MASTERDOWN 错误。
如果服务器返回除以上三种回复之外的其他回复, 又或者在指定时间内没有回复 PING 命令, 那么 Sentinel 认为服务器返回的回复无效(non-valid)。
注意, 一个服务器必须在 master-down-after-milliseconds 毫秒内, 一直返回无效回复才会被 Sentinel 标记为主观下线。
举个栗子, 如果 master-down-after-milliseconds 选项的值为 30000 毫秒(30 秒), 那么只要服务器能在每 29 秒之内返回至少一次有效回复, 这个服务器就仍然会被认为是处于正常状态的。
从主观下线状态切换到客观下线状态并没有使用严格的法定人数算法(strong quorum algorithm), 而是使用了流言协议: 如果 Sentinel 在给定的时间范围内, 从其他 Sentinel 那里接收到了足够数量的主服务器下线报告, 那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线。 如果之后其他 Sentinel 不再报告主服务器已下线, 那么客观下线状态就会被移除。
有一点需要注意的是:客观下线条件只适用于主服务器: 对于任何其他类型的 Redis 实例, Sentinel 在将它们判断为下线前不需要进行协商, 所以从服务器或者其他 Sentinel 永远不会达到客观下线条件。
只要一个 Sentinel 发现某个主服务器进入了客观下线状态, 这个 Sentinel 就可能会被其他 Sentinel 推选出, 并对失效的主服务器执行自动故障迁移操作。
Redis 分布式高可用方案:Redis Cluster
官方Redis Cluster 文档:https://redis.io/topics/cluster-tutorial
原理简介
作为一个内存数据库,实现高可用是一个基本保障,当储存服务在可预见的将来需要做存储拓展时,分布式储存就是一个必须要考虑到的事情。例如部署一个中央 redis 储存服务,提供给集团下所有的子公司所有需要的系统使用,并且系统数量在不断的增加,此时在部署服务的时候,分布式储存结构几乎是必然的选择。
Redis 3.0 版本之前,可以通过前面说所的 Redis Sentinel(哨兵)来实现高可用 ( HA ),从 3.0 版本之后,官方推出了Redis Cluster,它的主要用途是实现数据分片(Data Sharding),同时提供了完整的 sharding、replication(复制机制仍使用原有机制,并且具备感知主备的能力)、failover 解决方案,称为 Redis Cluster,同样可以实现 HA,是官方当前推荐的方案。在 Redis Sentinel 模式中,每个节点需要保存全量数据,冗余比较多,而在Redis Cluster 模式中,每个分片只需要保存一部分的数据,对于内存数据库来说,还是要尽量的减少冗余。在数据量太大的情况下,故障恢复需要较长时间,另外,内存的价格也是非常高昂的。
Redis Cluste r的具体实现细节是采用了 Hash 槽的概念,集群会预先分配16384个槽(slot),并将这些槽分配给具体的服务节点,通过对 Key 进行 CRC16(key)%16384 运算得到对应的槽是哪一个,从而将读写操作转发到该槽所对应的服务节点。当有新的节点加入或者移除的时候,再来迁移这些槽以及其对应的数据。在这种设计之下,我们就可以很方便的进行动态扩容或缩容。
当然,关于高可用的实现方案,也可以将 Redis-Sentinel 和 Redis-Cluster 两种模式结合起来使用,不过比较复杂,并不太推荐。
下图展示了 Redis Cluster 分配 key 和 slot 的基本原理:
上图中,key-value 全集被分成了 5 份,5个 slot(实际上Redis Cluster有 16384 [0-16383] 个slot,每个节点服务一段区间的slot,这里面仅仅举例)。A和B为master节点,对外提供写服务。分别负责 1/2/3 和 4/5 的slot。A/A1 和B/B1/B2 之间通过主备复制的方式同步数据。
上述的5个节点,两两通过 Redis Cluster Bus 交互,相互交换如下的信息:
1、数据分片(slot)和节点的对应关系;
2、集群中每个节点可用状态;
3、集群结构发生变更时,通过一定的协议对配置信息达成一致。数据分片的迁移、主备切换、单点 master 的发现和其发生主备关系变更等,都会导致集群结构变化。
4、publish/subscribe(发布订阅)功能,在Cluster版内部实现所需要交互的信息。
Redis Cluster Bus 通过单独的端口进行连接,由于Bus是节点间的内部通信机制,交互的是字节序列化信息。相对 Client 的字符序列化来说,效率较高。
Redis Cluster是一个去中心化的分布式实现方案,客户端和集群中任一节点连接,然后通过后面的交互流程,逐渐的得到全局的数据分片映射关系。
更多更详细的 redis cluster 的说明请移步Redis Cluster 官方文档
部署实例
Redis Cluster 集群至少需要三个 master 节点,本文将以单机多实例的方式部署3个主节点及3个从节点,6个节点实例分别使用不同的端口及工作目录
安装 redis 同上,不赘述。
-
为每个 redis 节点分别创建工作目录
在redis安装目录 /usr/local/redis-5.0.2 下新建目录 redis-cluster,并在该目录下再新建6个子目录,7000,7001,8000,8001,9000,9001,此时目录结构如下图所示:
修改配置
#开启后台运行 daemonize yes #工作端口 port 7000 #绑定机器的内网IP或者公网IP,一定要设置,不要用 127.0.0.1 bind 172.27.0.8 #指定工作目录,rdb,aof持久化文件将会放在该目录下,不同实例一定要配置不同的工作目录 dir /usr/local/redis-cluster/7000/ #启用集群模式 cluster-enabled yes #生成的集群配置文件名称,集群搭建成功后会自动生成,在工作目录下 cluster-config-file nodes-7000.conf #节点宕机发现时间,可以理解为主节点宕机后从节点升级为主节点时间 cluster-node-timeout 5000 #开启AOF模式 appendonly yes #pid file所在目录 pidfile /var/run/redis_8001.pid
-
按照上面的样例将配置文件复制到另外5个目录下,并对 port、dir、cluster-config-file 三个属性做对应修改,这里就不一一列举了。
-
安装 Ruby 和 RubyGems
由于创建 redis cluster 需要用到 redis-trib 命令,而这个命令依赖 Ruby 和 RubyGems,因此需要安装一下。
[root@VM_0_15_centos redis-cluster]# yum install ruby [root@VM_0_15_centos redis-cluster]# yum install rubygems [root@VM_0_15_centos redis-cluster]# gem install redis --version 3.3.3
分别启动6个节点
[root@VM_0_15_centos redis-4.0.6]# ./src/redis-server redis-cluster/7000/redis.conf [root@VM_0_15_centos redis-4.0.6]# ./src/redis-server redis-cluster/7001/redis.conf [root@VM_0_15_centos redis-4.0.6]# ./src/redis-server redis-cluster/8000/redis.conf [root@VM_0_15_centos redis-4.0.6]# ./src/redis-server redis-cluster/8001/redis.conf [root@VM_0_15_centos redis-4.0.6]# ./src/redis-server redis-cluster/9000/redis.conf [root@VM_0_15_centos redis-4.0.6]# ./src/redis-server redis-cluster/9001/redis.conf
查看服务运行状态
[root@VM_0_15_centos redis-4.0.6]# ps -ef | grep redis root 20290 1 0 18:33 ? 00:00:02 ./src/redis-server *:8001 [cluster] root 20295 1 0 18:33 ? 00:00:02 ./src/redis-server *:8002 [cluster] root 20300 1 0 18:33 ? 00:00:02 ./src/redis-server *:8003 [cluster] root 20305 1 0 18:33 ? 00:00:02 ./src/redis-server *:8004 [cluster] root 20310 1 0 18:33 ? 00:00:02 ./src/redis-server *:8005 [cluster] root 20312 1 0 18:33 ? 00:00:02 ./src/redis-server *:8006 [cluster] root 22913 15679 0 19:31 pts/2 00:00:00 grep --color=auto redis
可以看到6个节点以及全部成功启动
创建 redis cluster
[root@VM_0_15_centos redis-4.0.6]# ./src/redis-trib.rb create --replicas 1 172.27.0.8:7000 172.27.0.8:7001 172.27.0.8:8000 172.27.0.8:8001 172.27.0.8:9000 172.27.0.8:90001
创建过程中会有部分需要确认的地方,按照提示输入即可,集群创建完毕后观察一下这个集群的节点状态
172.27.0.8:7000> cluster nodes 068ac2afe1ade8b69b83226453fecc2b79cd93ae 172.27.0.8:7001@17001 slave 421ebe9e0a5ac6c811935ecd9dba83ef119dec17 0 1531008204920 4 connected 784c727c83a5952d3714ac211021f909cc4dfee4 172.27.0.8:8001@18001 slave eb5d700e2f030c02fb1f30ba4420d0b4f7170d84 0 1531008203000 5 connected 0537099e7cc7ab595c7aad5f0c96985251b85ec0 172.27.0.8:9001@19001 slave 79262341417df0a11eaf31e72bbf3e26f5f60ebf 0 1531008204419 6 connected 421ebe9e0a5ac6c811935ecd9dba83ef119dec17 172.27.0.8:7000@17000 myself,master - 0 1531008204000 1 connected 0-5460 eb5d700e2f030c02fb1f30ba4420d0b4f7170d84 172.27.0.8:8000@18000 master - 0 1531008203000 2 connected 5461-10922 79262341417df0a11eaf31e72bbf3e26f5f60ebf 172.27.0.8:9000@19000 master - 0 1531008203419 3 connected 10923-16383
如上所示,一个 3主3从的 redis cluster 分布式集群就搭建成功了,7000、8000、9000分别是三个 master 节点,7001、8001和9001为对应的 slaver 节点。
查询集群信息:
./redis-cli -c -h 192.168.101.3 -p 7001 # 其中-c表示以集群方式连接redis,-h指定ip地址,-p指定端口号 cluster nodes # 查询集群结点信息

cluster info # 查询集群状态信息

其实如果你并不想管这么多配置而只是想在最快的速度内创建一个 redis cluster 用作测试或者其他用途, redis 官方在 redis 安装目录的 Utils 目录下提供了一个 create-cluster 的脚本,如下图:
只要执行一下这个脚本就能自动创建一个 cluster:
[图片上传失败...(image-c6eea0-1554881540128)]
进入到这个目录下,执行./create-cluster start,即可立即完成一个 三主三从的 redis-cluster 的搭建:
如下图所示就是直接使用这个脚本创建的 redis cluster:
Tips
-
如果想重新创建集群,需要登录到每个节点,执行 flushdb,然后执行cluster reset,重启节点;
-
如果要批量杀掉Redis进程,可以使用 pkill redis-server命令;
-
如果redis开启了密码认证,则需要在redis.conf中增加属性 : masterauth yourpassword ,并且需要修改/usr/local/share/gems/gems/redis-3.3.3/lib/redis目录下的client.rb文件,将password属性设置为redis.conf中的requirepass的值,不同的操作系统client.rb的位置可能不一样,可以使用 find / -name "client.rb"全盘查找一下;
DEFAULTS = { :url => lambda { ENV["REDIS_URL"] }, :scheme => "redis", :host => "127.0.0.1", :port => 6379, :path => nil, :timeout => 5.0, :password => "yourpassword", :db => 0, :driver => nil, :id => nil, :tcp_keepalive => 0, :reconnect_attempts => 1, :inherit_socket => false }
- Redis开启密码认证后,在集群操作时问题会比较多,因此在非特殊情况下不建议开启密码认证,可以搭配使用防火墙保证 Redis 的安全。
故障测试的方法与上面一样,故不赘述。
Redis主从集群切换数据丢失问题
版权声明:本文为CSDN博主「不清不慎」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37142346/article/details/89435458
数据丢失的情况
- 异步复制同步丢失
- 集群产生脑裂数据丢失
1.异步复制丢失
对于Redis主节点与从节点之间的数据复制,是异步复制的,当客户端发送写请求给master节点的时候,客户端会返回OK,然后同步到各个slave节点中。
如果此时master还没来得及同步给slave节点时发生宕机,那么master内存中的数据会丢失;
要是master中开启持久化设置数据可不可以保证不丢失呢?答案是否定的。在master 发生宕机后,sentinel集群检测到master发生故障,重新选举新的master,如果旧的master在故障恢复后重启,那么此时它需要同步新master的数据,此时新的master的数据是空的(假设这段时间中没有数据写入)。那么旧master中的数据就会被刷新掉,此时数据还是会丢失。
2.集群产生脑裂
首先我们需要理解集群的脑裂现象,这就好比一个人有两个大脑,那么到底受谁来控制呢?在分布式集群中,分布式协作框架zookeeper很好的解决了这个问题,通过控制半数以上的机器来解决。
那么在Redis中,集群脑裂产生数据丢失的现象是怎么样的呢?
假设我们有一个redis集群,正常情况下client会向master发送请求,然后同步到salve,sentinel集群监控着集群,在集群发生故障时进行自动故障转移。

此时,由于某种原因,比如网络原因,集群出现了分区,master与slave节点之间断开了联系,sentinel监控到一段时间没有联系认为master故障,然后重新选举,将slave切换为新的master。但是master可能并没有发生故障,只是网络产生分区,此时client任然在旧的master上写数据,而新的master中没有数据,如果不及时发现问题进行处理可能旧的master中堆积大量数据。在发现问题之后,旧的master降为slave同步新的master数据,那么之前的数据被刷新掉,大量数据丢失。

在了解了上面的两种数据丢失场景后,我们如何保证数据可以不丢失呢?在分布式系统中,衡量一个系统的可用性,我们一般情况下会说4个9,5个9的系统达到了高可用(99.99%,99.999%,据说淘宝是5个9)。对于redis集群,我们不可能保证数据完全不丢失,只能做到使得尽量少的数据丢失。
二、如何保证尽量少的数据丢失?
在redis的配置文件中有两个参数我们可以设置:
min-slaves-to-write 1
min-slaves-max-lag 10
min-slaves-to-write默认情况下是0,min-slaves-max-lag默认情况下是10。
以上面配置为例,这两个参数表示至少有1个salve的与master的同步复制延迟不能超过10s,一旦所有的slave复制和同步的延迟达到了10s,那么此时master就不会接受任何请求。
我们可以减小min-slaves-max-lag参数的值,这样就可以避免在发生故障时大量的数据丢失,一旦发现延迟超过了该值就不会往master中写入数据。
那么对于client,我们可以采取降级措施,将数据暂时写入本地缓存和磁盘中,在一段时间后重新写入master来保证数据不丢失;也可以将数据写入kafka消息队列,隔一段时间去消费kafka中的数据。
通过上面两个参数的设置我们尽可能的减少数据的丢失,具体的值还需要在特定的环境下进行测试设置
问题汇总
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
解决方法是删除生成的配置文件nodes.conf,如果不行则说明现在创建的结点包括了旧集群的结点信息,需要删除redis的持久化文件后再重启redis,比如:appendonly.aof、dump.rdb
查找redis安装目录
如果命令 which 和whereis 都找不到安装目录,可使用以下办法,得到了进程号 xxxx
ps -ef|grep redis ls -l /proc/xxxx/cwd # 查看redis安装目录
总结
Redis 服务的部署方案的选型大家根据自己项目的需求部署即可,一般来说 redis sentinel 就够用了,也是目前用得最多的模式,但是 redis 3.0 之后官方推出的 redis-cluster 虽然本质是用于实现数据分片和分布式存储,但是其也实现了 redis sentinel 的全部功能,有完全的 HA 能力,并且部署起来更简单,因此成为了官方推荐的 HA 方案。我个人也更加推荐 redis cluster 方案。
参考链接
作者:小胖0_0 链接:https://www.jianshu.com/p/21110d3130bc
基于redis的延迟消息队列设计:https://www.cnblogs.com/duanxz/p/9585244.html
Python实现订单超时自动取消 :https://www.sohu.com/a/321524242_194621
《吊打面试官》系列-Redis常见面试题(带答案):https://zhuanlan.zhihu.com/p/91539644
Redis面试题总结 : 作者:叫我不矜持 来源:简书 链接:https://www.jianshu.com/p/65765dd10671
Redis数据持久化之RDB-AOF混合方式 作者:coding乐猿 链接:https://www.jianshu.com/p/446b12e4740f

浙公网安备 33010602011771号