欢迎指出错误

目录

文章目录

  • 目录
  • Redis 简介 & 安装
  • 简介
  • 安装
  • Redis 的启动、停止和连接
  • Redis 配置
  • Redis 的单线程+多路IO复用
  • 三种 IO 概念
  • Redis 中的五种数据类型 & 基本操作
  • String
  • Hash
  • List(列表)
  • Set
  • ZSet
  • Redis 的基本指令
  • Redis 事务
  • Multi、Exec、discard。Redis 事务的三条指令。
  • Redis 事务中的错误
  • Redis 监视
  • Redis 的持久化
  • RDB
  • RDB 如果持久化?
  • 修改备份规则
  • 如何恢复 RDB 的备份?
  • AOF
  • AOF 的 Rewite 机制。
  • 修改备份规则
  • 如何恢复 AOF 的备份?
  • 关于 RDB AOF & 持久化的一些建议。
  • Redis 的主从复制
  • Redis 主从复制具体操作
  • 主从复制的的配置修改
  • 启动三个 Redis
  • 设置主从关系
  • 主从复制之薪火相传
  • 主从复制之哨兵(sentinel)模式
  • 配置哨兵模式
  • 启动 sentinel
  • 哨兵原则
  • 主从复制 - 复制原理
  • Redis 集群
  • 什么是集群?
  • Redis 集群实现的原理
  • 安装 ruby
  • 创建集群实例
  • 集群中的故障
  • 解决 Redis 集群中常见的坑
  • Redis 常见问题 & 解决方法
  • 缓存穿透、缓存击穿、缓存雪崩
  • 缓存和数据库双写的一致性
  • SpringBoot2 整合 Redis
  • 参考文章

Redis 简介 & 安装

简介

redis 是一个开源的使用ANSI C语言编写、基于内存亦可持久化的日志型、Key-Value数据库,并提供了对多种编程语言的支持。

redis 是一个高性能的 nosql 数据库,与常规的关系型数据库不同的是。MySQL 主要将数据储存在磁盘中,而 redis 将数据储存在内存中(MySQL 其实也可以将数据储存在内存中)。

redis 中没有用户的概率但是可以设置密码,通常情况下并不设置密码。因为验证过程会损耗资源。

redis 默认有 16 个库,下标为 0 - 15。可以通过配置文件修改库的数量。

redis 可以做数据的持久化

原子性,因为 redis 是单线程的。

安装
wget http://download.redis.io/releases/redis-3.2.5.tar.gz # 下载 Redis 压缩包
tar -zxvf redis-3.2.5.tar.gz # 解压压缩包
cd redis-3.2.5 # 进入解压好的文件
yum install gcc # 安装 gcc 编译工具
make && make install # 编译安装

Redis 的启动、停止和连接

# Redis 的启动依赖配置文件与 Nginx 类似
redis-server # 启动 redis-server
redis-server /xxx/redis.conf # 指定配置文件启动 redis。这两种启动模式都是前台启动,后台启动需要修改配置,见下面 Redis 配置。

redis-cli shutdown # 停止redis

redis-cli -hIP -p端口 # 连接 Redis。Redis 中没有用户的概念但是有密码的概念。
redis-cli --raw # 可以解决 redis 中文乱码问题。

Redis 配置

在已经解压的文件中你可以找到 Redis 的默认配置文件。

配置文件详解太多,你可以去百度。也可以去我的个人网站下载到本地细细品味。yundongis.me

Redis 的单线程+多路IO复用

⚠️ 在 Redis6.0 中已经支持了多线程。

三种 IO 概念
  • 阻塞式 IO

你要上厕所,你敲了敲门,结果厕所里有人。于是你就啥都不做。站在门口干等着。

  • 非阻塞式 IO

你要上厕所,你敲了敲门,结果厕所里有人。可是你实在是憋不住了不定的窍门。翻译成代码如下

while(true){ 
	data = socket.read(); // 你敲门询问是否有人
  
	if(data!= error){  // 判断是否有人
		// 处理内急问题
		break; 
	} 
  
}
  • 单线程+多路IO复用

你要上厕所,你敲了敲门,结果厕所里有人。可是俗话说:人不能被尿憋死。于是你去找其他没人的厕所。但是你很急很急,每一步走到都会压迫膀胱,让你酸爽至极。于是你出 $100 请了一个过路人让他帮你找厕所。找到了就过来通知你。

  • 但是还没有结束,你找到了一个很靠谱的过路人。他给了你三种找厕所的方案。分别是 select、poll、epoll
  • select

过路人帮你一个厕所一个厕所的问:“里面有没有人呀~”。但是一次只能帮你问 1024 次。

  • poll

过路人帮你一个厕所一个厕所的问:“里面有没有人呀~”。不限次数。直到问到为止。

  • epoll

好巧不巧,找的过路人刚好是全国厕所管理员。他早就预料了到了这一切,他用探测仪安满了厕所。只要有人从厕所出来。他就会收到通知然后在通知你。就像每个厕所都有独立的 ID 一样。

Redis 中的五种数据类型 & 基本操作

⚠️ 虽然说有五种数据类型。但是他都是以 key-value 形式储存的。这里的五种数据类型值的是 Map 中的 value 不同。如:“aaa” -> “aaa” 。“bbb” -> (“yyy” -> “xxx”)。

如果你是 Java 程序员可以理解为。Map<"Key","java.lang.Object"> map。

redis3和redis5哪一个刚好_redis

String

就是普通字符串类型。一个 key 对应一个 value。String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象。

String 是 Redis 中最常用的数据类型。

常用于

  1. 缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到 redis 中,redis 作为缓存层,mysql 做持久化层,降低 mysql 的读写压力。
  2. 计数器:redis 是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。
  3. session:常见方案 spring session + redis 实现 session 共享,自定义 Token 等。

使用方法(完整使用方法见 https://www.redis.net.cn/order/)

get <key> # 查询对应键值
set <key> <value> # 添加键值对
append <key> <value> # 将给定的<value>追加到原值的末尾
strlen <key> # 获得值的长度
setnx <key> <value> # 只有在 key 不存在时设置 key 的值。

incr <key> # 将 key 中储存的数字值增加 1,只能对数字操作。如果不存在 <key> 就会创建。一个值为 1 的数据。
decr <key> # 将 <key> 值减 1,如果不存在 <key> ,就创建一个 -1 的数据
incrby/decrby <key> <步长> # 将 <key> 中储存的值增/减 <步长>,没有就创建。

mset <k1> <v1> <k2> <v2> # 同时设置多个键值对
mget <k1> <k2> # 同时获取一个或多个 <value>
msetnx <k1> <v1> <k2> <v2> # 同时设置多个键值对,当且仅当所有 key 都不存在才会成功。

getrange <key> <起始位置> <结束位置> # 获得值的范围,类似 java 中的substring
setrange <key> <起始位置> <value> # 用 <value> 覆写 <key> 所储存的字符串值,从 <起始位置> 开始。如果 key 不存在会创建。<起始位置> 之前数据使用 `\x00` 代替

setex <key> <过期时间> <value> # 设置键值的同时,设置过期时间,单位秒。
getset <key> <value> # 以新换旧,设置了新值同时获得旧值。(会返回旧值)
Hash

Redis hash 是一个键值对集合

Redis hash 是一个 String 类型的 field 和 value 的映射表,hash 特别适合用于储存对象

redis3和redis5哪一个刚好_spring_02

常用于

  1. 缓存: 能直观,相比string更节省空间,的维护缓存信息,如用户信息,视频信息等。

使用方法:(完整使用方法见 https://www.redis.net.cn/order/)

所有的 hash 命令都是 h 开头的。

hset <key> <field> <value> # 给 <key> 集合中的 <field> 键赋值 <value>
hget <key> <field> # 从 <key> 集合 <field> 键取出 value。
hmset <key> <f1> <v1> <f2> <v2> # 批量设置 hash 的值。

hexists key <field> # 查看哈希 key 中,给定域 <field> 是否存在。
hkeys <key> # 列出该 hash 集合的所有的 field。
hvals <key> # 列出该 hash 集合的所有 value
hincrby <key> <field> <increment> # 为哈希表 key 中的域 field 的值加上增量 increment。
hsetnx <key> <field> <value> # 将哈希表 key 中的域 field 的值设置为 value,当且仅当域 field 不存在。
List(列表)

单键多值

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以从头部(左边)或者尾部(右边)插入数据

它的底层是个双向链表,对两端的操作性能很高。通过索引下标的操作中间节点性能会较差。

redis3和redis5哪一个刚好_redis_03

常用于

  1. timeline:例如微博的时间轴,有人发布微博,用lpush加入时间轴,展示新的列表信息。

使用方法(完整使用方法见 https://www.redis.net.cn/order/)

命令都以 l 开头,如 lpush、lrange

lpush/rpush <key> <v1> <v2> # 从 左边/右边 插入一个或者多个值。
lpop/rpop <key> # 从 左边/右边 吐出一个值,如果值没有键就消毁。会返回销毁的值。
rpoplpush <k1> <k2> # 从 <k1> 列表右边吐出一个值,插到 <k2> 的右边

lrange <key> <起始位置> <结束位置> # 按照索引下标获得元素(从左到右)。0 至 -1 打印全部
lindex <key> <下标> # 按照下标获得元素(从左到右)
llen <key> # 获得列表长度

linsert <ket> before <value> <new value> # 在 <value> 的后面插入 <new value> 。如果 <value> 存在相同值只会插入第一个查询到的(从左到右)。
lrem <key> <n> <value> # 从左边删除 <n> 个 <value> (从左到右)。<value> 为相同值
Set

Redis ser 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的,当你需要储存一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员变量是否在一个 set 集合内的重要接口,这个也是 list 不能提供的。

Redis 的 set 是 String 类型的无序集合,它底层其实是一个 value 为 null 的 hash 表,所以添加,删除,查找的复杂度都是O(1)。

redis3和redis5哪一个刚好_分布式_04

常用于

  1. 标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。
  2. 点赞,或点踩,收藏等,可以放到set中实现
  3. 秒杀,一个人不能重复参加秒杀活动。

基本操作(完整使用方法见 https://www.redis.net.cn/order/)

命令都是以 s 开头的 sset 、srem、scard、smembers、sismember

sadd <key> <v1> <v2> # 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 merber 元素将被忽略。会返回成功的个数
smembers <key> # 取出该集合的所有值
sismenmber <key> <value> # 判断集合 <key> 是否为含有该 <value> 值,有返回 1,没有返回0。

scard <key> # 返回该集合的元素个数
srem <key> <v1> <v2> ... # 删除 <key> 集合中的某个元素
spop <key> # 随机从该集合中吐出一个值,会移除该元素。
srandmember <key> <n> # 随机从该集合中取出 <n> 个值,不会从集合中删除。

sinter <k1> <k2> # 返回两个集合的交集元素
sunion <k1> <k2> # 返回两个集合的并集元素
sdiff <k1> <k2> # 返回两个集合的差集元素
ZSet

Redis 有序集合 zset 与普通集合 set 非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分(score)。这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员,集合的成员是唯一的,但是评分可以是重复的。

因为元素是有序的,所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。

⚠️ (有序集合中的元素不可以重复,但是score 分数 可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相同)。

redis3和redis5哪一个刚好_redis_05

常用于

  1. 排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。

基本使用(完整使用方法见 https://www.redis.net.cn/order/)

有序集合的命令都是 以 z 开头 zadd 、 zrange、 zscore

zadd <key> <s1> <v1> <s2> <v2>... # 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
zrange <key> <start> <stop> [WITHSCORES] # 返回有序集 key 中,下标在 <start> <stop> 之间的元素,带 WITHSCORES,可以让分数一起和值返回到结果集。
zrangbyscore <key> <min> <max> [WITHSCORES] [limit offset count] # 返回有序集 key 中,所以 score 值介于 min 和 max 之间(包括等于 min 或者 max )的成员。有序集成员按 score 值递增(从小到大)次序排序。
zrevrangebyscore <key> <max> <min> [WITHSCORES] [limit offset count] # 同上,改为从大到小排序。

zincrby <key> <increment> <value> # 为元素的 score 加上增量
zrem <key> <value> # 删除该集合下,指定 <value> 的元素
zcount <key> <min> <max> # 统计该集合,分数区间内的元素个数
zrank <key> <value> # 返回该值在集合中的排名,从 0 开始。

Redis 的基本指令

更多指令查看 http://www.redis.net.cn/order

keys * # 查看当前库的所有键
exists <key> # 判断某个键是否存在
type <key> # 查看键的类型
del <key> # 删除某个键

expire <key> <scconds> # 设置键值的过期时间,单位秒
ttl <key> # 查看还有多少秒过期,-1 表示永久不过期,-2 表示已过期

dbsize # 查看当前数据库的 key 的数量
Flushdb # 清空当前库
Flushall # 通杀全部库

Redis 事务

Redis 中的事务并不具备关系型数据库的特点。Redis 中的事务更像是将几条命令集合起来,一起操作,如果某一条数据出现问题也不会回滚。

Multi、Exec、discard。Redis 事务的三条指令。
  • Multi说明事务开始,接下来可以编写事务内的操作。
  • Exec提交事务。
  • discard中断事务。
Redis 事务中的错误
  • Redis 的报告错误,会取消整个事务。Redis 的报告错误更像是一种编译错误。例如:你将 set 写成了 sett,就会出现这个错误。
  • Redis 的执行错误。不会取消整个事务,只会在错误代码代码地方报错。例如:set number a 命令出现了一个字符串,接下来你又使用 incr number 增加 1 。但是 number 并不是 数字类型。就会出现执行错误。但是!这个事务不会回滚也不会中断。只会在 incr number 操作中报错。然后接着继续执行。
Redis 监视

watch <key> [key...] 命令可以对每个 key 进行监视(在 multi 之前)。如果在事务执行时与其监视时的值不一致那么事务将会被打断。(其他事务中的操作也不会执行)

127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> set a a 
OK
127.0.0.1:6379> set b b
OK
127.0.0.1:6379> set c c
OK
127.0.0.1:6379> WATCH b
OK
127.0.0.1:6379> MULTI 
OK
127.0.0.1:6379> set a aa
QUEUED
127.0.0.1:6379> set b bb
QUEUED
127.0.0.1:6379> set c cc
QUEUED
127.0.0.1:6379> EXEC # 在执行 Exec 之前使用其他客户端将 b 修改为了 'bbb'
(nil)
127.0.0.1:6379> mget a b c
1) "a"
2) "bbb"
3) "c"

使用 unwatch 可以停止监视。当然在执行 exec discard 之后也会停止监视。

Redis 的持久化

什么是持久化?Redis 是将数据存储在内存中。就意味着只要机器断电,那么就会出现数据丢失的情况。但是Redis 同时也提供了两种持久化的方式,将数据写入磁盘中。

RDB

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的 Snapshot 快照,它恢复时是将快照文件直接读到内存里。

RDB 如果持久化?

Redis 会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用临时文件替换上次持久化好的文件。RDB 的缺点是最后一次持久化后的数据可能丢失。 Linux 中引入了 “写时复制技术”。即当我需要写的时候,才会进行复制。避免了多进程中对效率的影响

修改备份规则

你可在 redis.conf 中修改 RDB 保存位置 & 保存策略。

当然你也可以在 redis-cli 中对 redis 执行手动保存操作。只需要输入命令 save 。save 命令会导致 Redis 全部阻塞,直至操作完成。

dbfilename dump.rdb # 设置备份文件名
dir ./ # 设置备份文件的保存位置

# 保存策略(条件)。
save 900 1 # 900 秒内发生 1 次修改就会触发持久化
save 300 10 # 300 秒内发生 10 次修改就会触发持久化
save 60 10000 # 60 秒内发生 10000 次修改就会触发持久化
# 回想上方 **最后一次持久化后的数据可能丢失** 。有可能出现在一次持久化后,10 秒内又进行了 10 次更改。但是不满足策略没有进行持久化。可是 Redis 异常关闭了。那么那 10 次更改就会丢失。
# ⚠️ 正常的 `shutdown` 关闭 Redis 也会触发持久化。

stop-wirtes-on-bgsave-error yes # 当 Redis 无法写入磁盘的话,直接关掉 Redis 的写操作。
rdbcompression yes # 进行 rdb 保存时,将文件压缩
rdbchecksum yes # 在储存快照后,还可以让 Redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约10% 的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
如何恢复 RDB 的备份?

只要将备份文件放到,Redis.conf 中设置的目录中备份数据会直接加载。

AOF

以日志的形式来记录每个写操作,将 Redis 执行过的所有写指令记录下来。只许追加文件但不可以改写文件(RDB 是重写原备份文件)。

AOF 的 Rewite 机制。

因为 AOF 采用了问价追加的方式记入日志,那么文件必然会越来越大。再这种情况下新增了重写机制。当 AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩。只保留可以恢复数据的最小指令集。当人你也可以使用 BGREWRITEAOF 来开启手动压缩。

  • Redis 是怎样实现重写的?AOF 文件持续增大时,会 fork 出一条新的进程来将遍历 Redis 库中的现有数据,将数据反编译成写操作语句记入再一个临时文件夹中。操作完成后再替换原有的 AOF 文件。
# 主要压缩了不必要的写操作。如:
set a a
set a aa
set a aaa
# 那么最后 a 的值一定为 aaa。但是 AOF 会记录三条记录。其中前两条是不必要的,冗余的。Redis 的重写也主要是将这两条数据抹去。但是不会读取原 AOF,以节省资源。
  • 什么时候会重写?当系统载入时或者上次重写完毕时,Redis 会记录此时 AOF 大小为 base_size。如果满足以下公式就会发生重写。当前大小 >= base_size. + base_size * auto-aof-rewrite-percentage。且 base_size >= auto-aof-rewrite-min-size
# Redis.conf 配置中设置。 AOF Rewrite
auto-aof-rewrite-percentage 100 # 设置百分比
auto-aof-rewrite-min-size 64mb # 设置重写大小的阈值
修改备份规则

AOF 默认是不开启的。

appendonly no # 是否开启 AOF 。
appendfilename "appendonly.aof" # AOF 默认的文件名。
dir ./ # 对 RDB 和 AOF 同时生效。建议换成绝对路径

# AOF 的同步规则。
# appendfsync always # 始终同步,每次 Redis 的写入都会立刻记入日志
appendfsync everysec # 默认 每秒同步,每秒记入日志一次,如果当机。本秒的数据可能丢失
# appendfsync no     # 把同步时机交给操作系统。

# AOF Rewrite,避免文件过大。
auto-aof-rewrite-percentage 100 # 设置百分比
auto-aof-rewrite-min-size 64mb # 设置重写大小的阈值
如何恢复 AOF 的备份?

和 RDB 一样,放入指定的文件夹中即可。Redis 在启动时会重新执行一边 AOF 中的写操作。

关于 RDB AOF & 持久化的一些建议。
  • RDB 更加节省空间,因为 RDB 储存的是数据而 AOF 储存的是指令。
  • RDB 的恢复速度更快。原因同上。
  • 虽然 RDB 使用了 fork 。但是面对庞大数据时,还是比较消耗性能的。
  • RDB 最后一次持久化后的数据可能丢失。

前提条件。持久化规则:30 秒内进行 60 次更改就触发持久化。

有可能出现在一次持久化后,10 秒内又进行了 10 次更改。但是不满足策略没有进行持久化。可是 Redis 异常关闭了。那么那 10 次更改就会丢失。

  • 如果 AOF 和 RDB 同时开启,那么 RDB 将不生效。Redis 会采用 AOF。
  • AOF 备份机制更稳健,丢失数据概率更低。
  • AOF 的备份可读,可以处理误操作。
  • AOF 开启每次读写都同步的话,有一定的性能压力。
  • 如果只是保存临时数据完全可以不启用缓存。如:保存用户 Token,保存有时效的验证码。

Redis 的主从复制

  • 是什么?主从复制就是主机数据更新后根据配置和策略,自动同步到备机的 master/slaver 机制,master 以写为主,slave 以读为主
  • 用途
  • 读写分离,性能扩展
  • 容灾快速恢复
Redis 主从复制具体操作

Redis 的理想状态下,使用 一主二仆 模式即一个主机两个从机。其中主机可以读写(一般主机只写不读),从机只能读。

主从复制的的配置修改

案例需要:三台主机,但是我手上没有,只能通过修改端口模拟了。

⚠️ 如果要外网访问需要将 redis.conf 文件中的 bind 注释掉。

⚠️ 新增的配置文件修改了的参数会覆盖原有的默认配置文件中的参数。

⚠️ 以下配置文件只是一个参考,你可以参考上方的 [Redis 配置](#Redis 配置) 编写自己的配置文件。当然三个主机的配置信息也可以不同。

⚠️ 将之前开启的 redis 服务关闭。

我新建了三个配置,其中 6379 端口为主服务器,6380、6381 端口为从服务器。

  • 使用 vim 新增加配置文件 redis_6379.conf
# 引入默认的 Redis.conf 配置文件。⚠️ 如果要外网访问需要将 include 引入的 redis.conf 文件中的 bind 注释掉。
include /root/redis/redis-3.2.5/redis.conf
# 让外部网络可以访问到 redis。no 为关闭保护 
protected-mode no
# Redis 是否后台执行
daemonize yes
# 设置 pid 文件。关于 pid 文件详情可以百度。
pidfile /var/run/redis_6379.pid
# 设置端口
port 6379
# 指定 Log 文件保存地址
logfile "/root/redis/logs/redis_6379.log"
# 设置 RDB 持久化的文件。防止名字重复
dbfilename dump_6379.rdb
# 设置 RDB 文件保存地址
dir /root/redis/rdbs/

# 设置主从关系。PS:主从关系可以通过配置设置,也可以通过命令设置。为了方便演示其他功能,我采用的是命令的方式。如果你觉得命令麻烦完全可以打开这个设置,⚠️ 但是下方 ·设置主从关系· 这一步就不用执行 ·redis-cli -h xxx -p xx· 了
# slaveof 127.0.0.1 6379

# 关闭 AOF 持久化
appendonly no
  • 使用 vim 新增加配置文件 redis_6380.conf
# 引入默认的 Redis.conf 配置文件。⚠️ 如果要外网访问需要将 redis.conf 文件中的 bind 注释掉。
include /root/redis/redis-3.2.5/redis.conf
# 让外部网络可以访问到 redis。no 为关闭保护 
protected-mode no
# Redis 是否后台执行
daemonize yes
# 设置 pid 文件。关于 pid 文件详情可以百度。
pidfile /var/run/redis_6380.pid
# 设置端口
port 6380
# 指定 Log 文件保存地址
logfile "/root/redis/logs/redis_6380.log"
# 设置 RDB 持久化的文件。防止名字重复
dbfilename dump_6380.rdb
# 设置 RDB 文件保存地址
dir /root/redis/rdbs/


# 设置主从关系。PS:主从关系可以通过配置设置,也可以通过命令设置。为了方便演示其他功能,我采用的是命令的方式。如果你觉得命令麻烦完全可以打开这个设置,⚠️ 但是下方 ·设置主从关系· 这一步就不用执行 ·redis-cli -h xxx -p xx· 了
# slaveof 127.0.0.1 6379


# 关闭 AOF 持久化
appendonly no
  • 使用 vim 新增加配置文件 redis_6381.conf
# 引入默认的 Redis.conf 配置文件。⚠️ 如果要外网访问需要将 redis.conf 文件中的 bind 注释掉。
include /root/redis/redis-3.2.5/redis.conf
# 让外部网络可以访问到 redis。no 为关闭保护 
protected-mode no
# Redis 是否后台执行
daemonize yes
# 设置 pid 文件。关于 pid 文件详情可以百度。
pidfile /var/run/redis_6381.pid
# 设置端口
port 6381
# 指定 Log 文件保存地址
logfile "/root/redis/logs/redis_6381.log"
# 设置 RDB 持久化的文件。防止名字重复
dbfilename dump_6381.rdb
# 设置 RDB 文件保存地址
dir /root/redis/rdbs/

# 设置主从关系。PS:主从关系可以通过配置设置,也可以通过命令设置。为了方便演示其他功能,我采用的是命令的方式。如果你觉得命令麻烦完全可以打开这个设置,⚠️ 但是下方 ·2.设置主从关系· 这一步就不用执行 ·redis-cli -h xxx -p xx· 了
# slaveof 127.0.0.1 6379


# 关闭 AOF 持久化
appendonly no
启动三个 Redis
redis-server redis_6379.conf
redis-server redis_6380.conf
redis-server redis_6380.conf

# 使用 ps 查看是否启动成功。
ps -ef | grep redis-server # 启动成功的截图放在了下面。

redis3和redis5哪一个刚好_redis_06

如果启动失败,可以去配置中设置的 log 文件查看失败的原因。

设置主从关系

以下操作都建立在通过 redis-cli -h xxx -p xx 登陆成功后。

  1. 查看当前 redis 的主从关系。
# 可以使用 ·info replication· 命令查看当前登陆 redis 的主从关系。
127.0.0.1:6379> info replication
# Replication
role:master # <- 这行表明了它是主服务器,未设置主从关系的 redis 默认都是主服务器。
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
  1. 设置主从关系

slaveof <ip> <port> 这个命令可以让当前登陆的 redis 成为某个实例的从服务器。

127.0.0.1:6381> SLAVEOF 127.0.0.1 6379 # 设置 6379 端口的实例为当前服务的主。⚠️ 如果配置中设置了就不需要了。
OK
127.0.0.1:6381> info replication # 再次查看状态。
# Replication
role:slave # 状态已经改成 slave
master_host:127.0.0.1 # 主服务器的 IP
master_port:6379 # 主服务器的 端口
master_link_status:up # 主服务器的状态 up 为正常运行,down 为主服务停止运行。
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_repl_offset:15
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
  1. 查看 6379 端口主服务器的状态
127.0.0.1:6379> info replication
# Replication
role:master # 说明这是一个主服务器
connected_slaves:2 # 说明他有两个从服务器,详情如下。
slave0:ip=127.0.0.1,port=6381,state=online,offset=435,lag=1
slave1:ip=127.0.0.1,port=6380,state=online,offset=435,lag=1
master_repl_offset:435
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:434
127.0.0.1:6379>

这时,6379 端口的主服务器可以进行读写,6380、6381 端口只能进行读。同时 6379 的全部数据都会同步到 6380、6381 中。

关于主从服务器之间的说明。

  1. 从服务器在连接上主服务器后会拥有与主服务器同样的数据,与数据插入的时间无关
  2. 主服务器 down 掉后,从服务器会进入等待状态。等待主服务上线。
  3. 从服务器 down 掉后,主服务器就损失一个从服务器。从服务器上线后需要重新编写主从关系。
  4. 从机不可写数据。
主从复制之薪火相传

Redis 主从复制的薪火相传相传比较好理解。见上图你因为就明白了。就是将服务器们串行化,当主服务器 6379 宕机时。顺位的从服务器 6380 可以使用,命令 slaveof no one 放弃自己的从服务的定位,转为主服务器。

更具上图也可以得知他们的主从关系。6379 是 6380 的主服务器,6380 是 6381 的主服务器。

主从复制之哨兵(sentinel)模式

能后台监视主机是否故障,如果发生了故障根据投票数自动将从服务器转换为主服务器。哨兵模式的本质就是开启一个哨兵的服务器监视启动的 Redis 服务器。如果你细心的话可以在解压的 redis 文件中看到 sentinel.conf 。里面是哨兵相关的配置。这里我就不多说了,一般使用默认即可。如果你想了解请移步 yundongis.me 。下载中文版,查看。

配置哨兵模式

哨兵模式基于 一主二仆 模式,前面我已经讲过哨兵模式 redis 已经给了默认的配置,一般情况下是不需要进行修改的。具体各个配置参数你也可以百度一下。这里我只说两样配置。

vim sentinel.conf 修改配置,我建议你先 cp 出一份备份。以防万一。

sentinel monitor mymaster 127.0.0.1 6379 2 # redis 默认配置
# ·mymaster· 是你给主服务起的名称可以随意。·127.0.0.1· 是你监视的主服务器 IP。·6379· 是主服务器端口。·2· 代表需要两台从服务器同意才可以转移主服务器,所以使用哨兵模式最好有三台从服务器。

daemonize yes # sentinel 默认是前台启动的。加上这个配置可让其后台运行。
# 偷偷告诉你 sentinel 其实就是 redis 那么有多少 redis 配置可以用在 sentinel? 你可自己试试。
启动 sentinel
redis-sentinel sentinel.conf 
# 记住要指定你修改好的 sentinel.conf 。
哨兵原则

当主服务器宕机后,新主上位遵循以下规则。

  1. 选优先级靠前的Redis.conf 中 slave-priority 参数可以设置,优先级。数值越小优先级越高。
  2. 选择偏移量较大的就是那个从服务器,连接主服务器时间较长(最先备份数据的)。
  3. 选择 runid 最小的从服务器。每个 redis 实例启动后都会随机生成一个 40 位的 runid。

主服务恢复连接后,将转变为从服务器。

主从复制 - 复制原理
  1. 每次从机连通后,都会给主机发送 sync 指令。告诉主机,我是你的从机。麻烦把数据给我下。
  2. 主机收到信息后立即进行持久化操作。发送 RDB 文件给从机。
  3. 从机收到 RDB 文件后,进行加载。
  4. 之后每次主机的写操作,都会立刻发送给从机,从机执行相同命令。

Redis 集群

什么是集群?

Redis 集群实现了对 Redis 的水平扩容,即启动 N 个 redis 节点,将整个数据库分布存储在这 N 个节点中,每个节点储存总数据的 1/N 。

Redis 集群通过分区(partition)来提供一定程度的可用性。即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。

Redis 集群实现的原理

假设在 redis 集群下有三个 redis 可写实例(主服务器)。分别是 R1、R2、R3。Redis 集群要做的就是将 key 为 k1、k2、k3 的数据分别储存在 R1、R2、R3 中。首先,在安装 Redis 集群时 Redis 会将各个实例库分配好对应插槽,例如一共有 300 个插槽。 R1 对应 0 ~ 100 的插槽,R2 对应 101 ~ 200,R3 对应 201 ~ 300。 在储存 key 时使用 CRC16(key) 算法,计算 key 的值然后放入对应的插槽(实例库)。

如果你了解 Hash 算法,可以想象成是一种 Hash 的实践。可写实例对应 Hash 值,数据对应 Hash 下的链表。

安装 ruby

Redis 的集群依赖 ruby 脚本。

yum install ruby
yum install rubygems
创建集群实例
  • 创建 6 个 redis.conf 配置具体配置信息可以参考上方的主从复制。但是要新增以下三条。
# 打开集群模式
cluster-enabled yes
# 设定节点配置文件。这里的节点文件会自动生成,自需要配置好路径即可。
cluster-config-file "/root/tools/redis/confs/nodes-6379.conf" 
# 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。
cluster-node-timeout 15000

redis3和redis5哪一个刚好_redis_07

  • 启动 6 个 Redis
redis-server 6379.conf
redis-server 6380.conf
redis-server 6381.conf
redis-server 6389.conf
redis-server 6390.conf
redis-server 6391.conf
# 启动完成后就会生成 nodes 节点文件。路径就是你上面配置的。

redis3和redis5哪一个刚好_spring_08

  • 合并 6 个节点文件

打开你解压的 redis 目录下,cd 到 src 文件夹下面。

⚠️ 执行命令之前一定保证以下三个必要条件

  1. 必须使用真实 IP 地址,我这里因为使用的是公有云服务器所以设置的是公网 IP 。如果你使用的内网可以使用 192.168.xx.xx 内网地址。
  2. 必须将 redis 端口加 10000。开放,如:我有 6 台 redis 实例端口分别是:6379、6380、6381、6389、6390、6391。那么就要在防火墙中打开,6379、6380、6381、6389、6390、6391、16379、16380、16381、16389、16390、16391。或者直接将防火墙关闭。
  3. 保证各个 redis 中没有数据。如果存在数据使用。 flushall 和 cluster reset ,命令进行清除。
# 这一步其实就是将 6 个节点放到一个集群中。
  # ⚠️ 一个集群至少要有三个主节点
  # ⚠️ 下方 --replicas 1 参数表示。我们希望集群每个主节点都创建一个从节点
  # ⚠️ 生产环境中。尽量保证每个 Redis 都运行在不同 IP 地址上

./redis-trib.rb create --replicas 1 118.25.181.91:6379 118.25.181.91:6380 118.25.181.91:6381   118.25.181.91:6389 118.25.181.91:6390 118.25.181.91:6391

如果报错可以参考下方的 [解决 Redis 集群中常见的坑](#解决 Redis 集群中常见的坑) 。

正常截图执行成功截图如下:Redis 会为集群自动创建主从复制。主从关系

redis3和redis5哪一个刚好_redis_09

  • 集群操作命令
  • 注意事项
# ⚠️ 集群中不能使用 mset mget 等。一次可以操作多条 key 的命令。因为 key 可能不再一个实例中!
  • 基本操作指令
# 连接 redis 集群。<port> 为可写实例(主服务器)端口。 
[root@VM_0_8_centos ~] redis-cli -c -p <port>

# 查看集群信息
127.0.0.1:6379> cluster nodes

# 使用 {} 将 key 分配到一个集群中
127.0.0.1:6379> set k1{ks} k1
127.0.0.1:6379> set k2{ks} k2
127.0.0.1:6379> set k2{ks} k2 # 那么 k1 k2 k3 就会在一个实例中了。

# 计算 key 的插槽值
cluster keyslot <key>

# 返回插槽目前包含的键值对应数量
CLUSTER COUNTKEYSINSLOT <slot>

# 返回 count 个 slot 插槽中的 key
CLUSTER GETKEYSINSLOT <slot> <count>
集群中的故障
  • 如果主节点下线?从节点能否自动升为主节点?主节点下线后,从节点会自动升级为主节点。
  • 主节点恢复后,主从关系如何?主节点下线后又上线,会转变为从服务器。
  • 如果所有每一段插槽的主从节点都当掉,redis 服务是否还能继续?如果每一段的插槽主从服务器都当掉,Redis 集群会宕机。redis.conf 中 cluster-require-full-coverage 参数可以设置是否 16384 个插槽都正常的时候才能对外提供服务。
解决 Redis 集群中常见的坑
  1. 执行 ./redis-trib.rb 出错。报错信息如下:

/usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require’: cannot load such file – redis (LoadError)

from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require’

from /usr/local/bin/redis-trib.rb:25:in `’

  1. 解决方法:
# 使用 gem 安装 redis 库
gem install redis

# ⚠️ 但是!!! 这个命令连接的库是国外的,所以执行会非常的慢!以下是修改 gem 源地址。
gem sources --remove https://rubygems.org/ # 删除默认源代码镜像
gem sources -a https://mirrors.ustc.edu.cn/rubygems/  #添加科大源
  1. 执行 gem install redis 命令时出错。报错信息如下:

Fetching: redis-4.1.2.gem (100%) ERROR: Error installing redis: redis requires Ruby version >= 2.3.0.

  1. 解决方法
# 安装 curl 
[root@VM_0_8_centos ~] yum install curl 

# 安装RVM
[root@VM_0_8_centos ~] curl -L get.rvm.io | bash -s stable
  1. 执行 curl -L get.rvm.io | bash -s stable 命令受阻。信息如下:
# 执行 curl -L get.rvm.io | bash -s stable 命令时,可能会出现连接失败。不用管它,一直重试就行。

[root@VM_0_8_centos ~] curl -L get.rvm.io | bash -s stable

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                Dload  Upload   Total   Spent    Left  Speed

100 194 100 194 0 0 89 0 0:00:02 0:00:02 --:–:-- 89 100 24535 100 24535 0 0 4108 0 0:00:05 0:00:05 --:–:-- 8873 Downloading https://github.com/rvm/rvm/archive/1.29.9.tar.gz Downloading https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc gpg: 已创建目录‘/root/.gnupg’ gpg: 新的配置文件‘/root/.gnupg/gpg.conf’已建立 gpg: 警告:在‘/root/.gnupg/gpg.conf’里的选项于此次运行期间未被使用 gpg: 钥匙环‘/root/.gnupg/pubring.gpg’已建立 gpg: 于 2019年07月10日 星期三 16时31分02秒 CST 创建的签名,使用 RSA,钥匙号 39499BDB gpg: 无法检查签名:没有公钥 GPG signature verification failed for ‘/usr/local/rvm/archives/rvm-1.29.9.tgz’ - ‘https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc’! Try to install GPG v2 and then fetch the public key:

gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB

or if it fails:

command curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -
   command curl -sSL https://rvm.io/pkuczynski.asc | gpg2 --import -

In case of further problems with validation please refer to https://rvm.io/rvm/security

- 两种解决方法

  ```bash
  # 执行命令
  [root@VM_0_8_centos ~] gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
  ```

  或者

  ```bash
  # 执行命令
  [root@VM_0_8_centos ~] command curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -
  [root@VM_0_8_centos ~] command curl -sSL https://rvm.io/pkuczynski.asc | gpg2 --import -
  ```

```bash
# 查看配置文件
[root@VM_0_8_centos ~] find / -name rvm -print
/usr/local/rvm
/usr/local/rvm/src/rvm
/usr/local/rvm/src/rvm/bin/rvm
/usr/local/rvm/src/rvm/lib/rvm
/usr/local/rvm/src/rvm/scripts/rvm
/usr/local/rvm/bin/rvm
/usr/local/rvm/lib/rvm
/usr/local/rvm/scripts/rvm

# 激活RVM,使配置生效
[root@VM_0_8_centos ~] source /usr/local/rvm/scripts/rvm

# 下载 RVM 依赖 
[root@VM_0_8_centos ~] rvm requirements

# 查看rvm库中已知的ruby版本
[root@VM_0_8_centos ~] rvm list known

# 安装 2.3.8 版的 ruby
[root@VM_0_8_centos ~] rvm install ruby-2.3.8

# 使用 2.3.8 版本
[root@VM_0_8_centos ~] rvm use 2.3.8 

# 设为默认    
[root@VM_0_8_centos ~] rvm use 2.3.8 --default  

# 删除一个已知版本
[root@VM_0_8_centos ~] rvm remove 2.0.0         

# 查看当前版本
[root@VM_0_8_centos ~] ruby -v                  

# ⚠️ 执行到这一步,我估计你到已经忘记自己是为了解决什么问题。让我来告诉你:
# 你该执行:
gem install redis 

&&

./redis-trib.rb create --replicas 1 118.25.181.91:6379 118.25.181.91:6380 118.25.181.91:6381 118.25.181.91:6389 118.25.181.91:6390 118.25.181.91:6391

Redis 常见问题 & 解决方法

缓存穿透、缓存击穿、缓存雪崩
  • 缓存穿透
  • 描述缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为 “-1” 的数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
  • 解决方案:
  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id <= 0 的直接拦截;
  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将 key-value 对写为 key-null ,缓存有效时间可以设置短点,如 30 秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个 id 暴力攻击
  • 缓存击穿
  • 描述:缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
  • 解决方案:
  1. 将热点缓存失效时间设置为 -1。
  • 缓存雪崩
  • 描述:缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至 down 机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
  • 解决方案:
  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
  2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
  3. 设置热点数据永远不过期。
缓存和数据库双写的一致性

就是一条数据更新时,先修改 redis 中的值,还是 mysql 中的值的问题。

如下三种策略你可以想想有什么不足。(如果两条请求一起来时会发生什么?)

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存

终极解决办法:

  1. mysql 加上监视器
  2. 代码串联化。假设:A 修改了数据 D1 。那么就可以使用 D1 的 ID 当作锁。直至完成双写操作完成在解锁。

SpringBoot2 整合 Redis

https://www.jianshu.com/p/9be5d9c13bec

源码在 Github 中

参考文章

Redis(一)、Redis五种数据结构

实例解读什么是Redis缓存穿透、缓存雪崩和缓存击穿

缓存穿透、缓存击穿、缓存雪崩区别和解决方案