Redis 知识梳理

Redis回顾梳理

️Redis小白的复习梳理
官网: https://redis.io/ 中文地址: http://redis.cn/下载地址: https://redis.io/download

0、简介

0.0 为什么需要Redis

  • 企业需求

​ 高并发、高可用、高性能、海量用户。

  • 关系型数据库(MySQL)瓶颈

​ 性能瓶颈问题:磁盘IO性能低下。

​ 扩展瓶颈:数据关系复杂,扩展性差,不利于大规模集群。

0.1 Redis的优势

内存存储,降低了磁盘IO次数

不存储关系,仅存储数据与数据之间关系,越简单越好

0.2 Redis

Redis (Remote DIctionary Server) 是用 C 语言开发的一个开源的高性能键值 对(key-value)数据库。

  • 数据间没有必然的关联关系。

  • 高性能。

  • 官方提供测试数据,50 个并发执行 100000 个请求,读的速度是 110000 次/s,写的速 度是 81000 次

  • 多种数据结构支持

  • 持久化支持,可以进行数据灾难恢复。

0.3 Redis 应用场景

  1. 为热点数据加速查询,如热点商品、热点新闻、热点资讯、推广类等高访问量信息等。
  2. 任务队列,如秒杀、抢购、购票排队等 。
  3. 即时信息查询,如排行榜、各类网站访问统计 。
  4. 时效性信息控制,如验证码控制、投票控制等 。
  5. 分布式数据共享,如分布式集群架构中的 session 分离 。
  6. 消息队列 。
  7. 分布式锁。

0.4 NoSQL 数据库

Not-Only SQL( 泛指非关系型的数据库),作为关系型数据库的补充。

  • 可扩容、可伸缩。
  • 大数据量下高性能。
  • 灵活的数据模型。
  • 高可用。

作用:应对在海量用户和海量数据的情况下,带来的数据处理问题。

常见的NoSQL数据库有Redis、memcache、HBase、MongoDB。

0.5 安装Redis

  1. Linux网络设置

/etc/sysconfig/network-scripts/ifcfg-ens33,根据实际情况修改。

#IP地址
IPADDR=192.168.153.129
#网关
GATEWAY=192.168.153.2
#DNS
DNS1=8.8.8.8
BOOTPROTO="static"
  1. 在linux安装C语言编译环境
安装gcc
yam install gcc
测试gcc版本
gcc --version
  1. 下载redis-6.26.tar.gc,并转存到 /opt目录

  2. 解压redis-6.26.tar.gc,得到redis-6.26目录

tar -zxvf redis-6.26.tar.gc
  1. 到redis-6.26目录下,分别执行make(编译),make install(安装) 命令
make
make install
  1. 安装 OK , 安装目录在 /usr/local/bin
cd /usr/local/bin/
ls

显示结果

redis-benchmark  redis-check-aof  redis-check-rdb  redis-cli  redis-sentinel  redis-server

0.6 启动 Redis

  1. 拷贝/opt/redis-6.26/redis.conf 到 /etc/redis.conf
  2. 修改/etc/redis.con 后台启动设置 daemonize no 改成 yes。
  3. redis启动
/usr/local/bin/redis-server /etc/redis.conf 
  1. 查看redis是否后台启动
ps -aux |grep redis
或者
netstat -anp | more
  1. 用客户段访问:redis-cli
/usr/local/bin/redis-cli -p 6379
  1. 关闭redis

​ 单实例关闭

/usr/local/bin/redis-cli shutdown
/usr/local/bin/redis-cli -p 6379 shutdown

1、Redis指令

指令文档: http://redis.cn/commands.html

参考中文文档: http://redisdoc.com/

Redis 命令十分丰富,包括的命令组有 Cluster、Connection、Geo、Hashes、HyperLogLog、Keys、Lists、Pub/Sub、Scripting、Server、Sets、Sorted Sets、Strings、Transactions 一共14个 redis 命令组两百多个 redis 命令。

1.0 基础操作

# 设置key,value数据
set key value
# 根据key查询value,如果不存在,返回空(nil)
get keyh
# 退出客户端 (Redis 服务没有关闭)
quit/exit
# 清屏
clear

1.1 对键的操作

# 查看当前库所有key
keys *
#判断某个key是否存在
exists key
#查看key的类型
type key
#删除指定key
del key
#根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作
unlink key
#未指定key设置过期时间,默认单位为秒
expire key time(s)
#查看key还有多少秒过期
ttl key

1.2 对数据库操作

redis 安装后,默认有 16 个库, 0-15

# 命令切换数据库
select n
# 查看当前数据库的 key 的数量
dbsize
# 清空当前库(谨慎使用)
flushdb
# 清空全部库 (谨慎使用)
flushall

2、Redis 数据类型

  • 官方文档 : https://redis.io/commands

  • 参考中文文档: http://redisdoc.com/

redis 自身是一个 Map,其中所有的数据都是采用key : value 的形式存储。key 是字符串,value 是数据,数据支持多种类型/结构。Redis 数据类型 5 种常用 string 、 hash 、list 、set 、sorted_set。

2.1 string 字符串

  • String 是 Redis 最基本的类型,一个 key 对应一个 value。
  • String 类型是二进制安全的, Redis 的 string 可以包含任何数据。比如jpg 图片或者序列化的对象
  • String 类型是 Redis 基本的数据类型,一个 Redis 中字符串value 最多可以是512M。
# 添加键值对
set key value
# 只在键 key 不存在的情况下, 将键 key 的值设置为 value 。
setnx key value
# 查询key对应的value
get key
# 将给定的value追加到原值的末尾
append key value
# 获得值的长度
strlen key
# 设置key值(只有在key不存在时)
setnx key value
# 将value存储的数字值(字符串)增1,只能对数字值操作,如果为空,新增值为1。
incr key
# 将value存储的数字值(字符串)减1,只能对数字值操作,如果为空,新增值为-1。
decr key
# 将value储存的数字值增。自定义步长。
incrby key step
# 将value储存的数字值减。自定义步长。
decrby key step
# 同时设置一个或多个value,如果某个给定键已经存在, 那么 MSET 将使用新值去覆盖旧值
# 是一个原子性(atomic)操作。
# 所有给定键都会在同一时间内被设置, 不会出现某些键被设置了但是另一些键没有被设置的情况。
mset key value key value
# 当且仅当所有给定键都不存在时, 为所有给定键设置值。
msetnx key value key 
# 同时获取多个value值
mget key key 
# 获取值范围(闭区间),索引从0开始。
getrange key start end 
# 覆盖存储字符串的值,从star位置开始,索引从0开始。
setrange key offset value
# 设置value,同时设置过期时间(秒)
setex key seconds
#返回旧值,同时设置一个新值
getset key value

2.2 list 列表

  • list类型保存多个数据,底层是使用双向链表存储结构实现。允许重复。
  • 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
  • 底层是个双向链表,对两端的操作性能高,通过索引下标的操作中间的节点性能较差。
# 从右边插入一个或多个值
rpush key value value 
# 从左边插入一个或多个值
lpush key value value 
# 移除并返回列表 key 的头元素。(从左边吐出一个值) count:删除的数量
lpop key [count]
#从右边吐出一个值
rpop key
# 从key1列表右边吐出一个值,插到key2列表左边。
rpoplplush key1 key2
# 按照索引下标获得元素(从左到右,0左边第一个,-1右边第一个)
lrange key start stop
# 按照索引下标获得元素
lindex key index
# 获取列表长度
llen key
# 在value的前面或后面插入newvalue
linsert key before|after value newvalue
# 将列表 key 下标为 index 的值替换成 value
lset key index value
# 从左边删除 n 个 value(从左到右)
# count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
# count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
# count = 0 : 移除表中所有与 value 相等的值。
lrem key count value

2.3 set 序列

set 提供的功能与 list 类似是一个列表的功能,特殊之处在于set 是可以自动排重的, 即值是不允许重复。

# 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
sadd key membe member
# 取出该集合的所有值。
smembers key 
# 判断集合key是否为含有该value值,有 1,没有0。
sismember key value
# 返回集合 key 的基数(集合中元素的数量)。
scard key
# 删除集合中的某个元素。
srem key value1 value2 
# 随机从该集合中吐出一个值。
spop key
# 随机从该集合中取出 n 个值。不会从集合中删除。
srandmember key n
# 把集合中一个值从一个集合移动到另一个集合
smove source destination value
# 返回两个集合的交集元素。
sinter key1 key2
# 返回两个集合的并集元素。
sunion key1 key2
# 返回两个集合的差集元素(key1 中的,不包含 key2 中的)
sdiff key1 key2

2.4 hash 哈希表

hash 是一个键值对集合,hash 适合用于存储对象, 类似 Java 里面的 Map

# 给key集合中的 field键赋值value
hset key field value 
# 将key 中的域 field 的值设置为 value ,当且仅当域field不存在
hset key field value
# 从key集合field取出 value
hget key1 field
# 批量设置 hash 的值
hmset key1 field1 value1 field2 value2
# 批量取出 hash 的 filed 值
hmget key1 field1 field2
# 查看哈希表 key 中,给定域 field 是否存在
hexists key1 field 
# 列出该hash集合中的所有field 
hkeys key 
# 列出该hash集合中的所有value 
hvals key
# 返回哈希表 key 中,所有的域和值。
hgetall key
# 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
hdel key field field
# 返回哈希表 key 中域的数量。
hlen key
# 返回哈希表 key 中, 与给定域 field 相关联的值的字符串长度
hstrlen key field
# 为哈希表 key 中的域 field 的值加上增量 increment 。 increment为负数则执行减法操作
hincrby key filed increment

2.5 Zset 有序集合

Zset(sorted set)

  • Redis 有序集合 zset 与普通集合 set 非常相似,是一个没有重复元素的字符串集合。
  • 不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。
  • 因为元素是有序的, 所以也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
  • 访问有序集合的中间元素也是非常快的, 你能够使用有序集合作为一个没有重复成员的列表
# 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
# score 值可以是整数值或双精度浮点数。
zadd key score member score member
# 返回有序集 key 中,成员 member 的 score 值。 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 
zscore key member
# 为有序集 key 的成员 member 的 score 值加上增量increment  传递一个负数值 increment ,让 score 减去相应的值
zincrby key increment member
# 返回序集key的基数
zcard key
#返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。
zcount key min max
# 返回有序集 key 中,指定区间内的成员,其中成员的位置按 score 值递增(从小到大)来排序。
zrange key start stop [withscores]
# 返回有序集 key 中,指定区间内的成员,其中成员的位置按 score 值递增(从大到小)来排序
zrevrange key start stop [withscores]
# 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
# (从小到大)
zrangebyscore key min max [withscores] [limit offset count]
# 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
# (从大到小)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
#返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
#score 值最小的成员排名为 0 。
ZRANK key member	
#返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。
#score 值最大的成员排名为 0 
ZREVRANK key member
# 删除该集合的一个或多个元素
zrem key member [member ...]
# 移除有序集 key 中,指定排名(rank)区间内的所有成员。包含 start 和 stop 在内。
ZREMRANGEBYRANK key start stop
# 移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。
ZREMRANGEBYSCORE key min max
# 计算给定的一个或多个有序集的并集,其中给定 key 的数量必须以 numkeys 参数指定,并将该并集(结果集)储存到 destination 。
# 默认情况下,结果集中某个成员的 score 值是所有给定集下该成员 score 值之和 。
# --
# 使用 WEIGHTS 选项,你可以为 每个 给定有序集 分别 指定一个乘法因子(multiplication factor),每个给定有序集的所有成员的 score 值在传递给聚合函数(aggregation function)之前都要先乘以该有序集的因子。
# 如果没有指定 WEIGHTS 选项,乘法因子默认设置为 1 。
# 使用 AGGREGATE 选项,你可以指定并集的结果集sorce的聚合方式。
ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
# 计算给定的一个或多个有序集的交集,其中给定 key 的数量必须以 numkeys 参数指定,并将该交集(结果集)储存到 destination 。
ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]

3、Redis 配置

列举几个常用设置

参考文档 : https://www.cnblogs.com/nhdlb/p/14048083.html#_label0

3.1 NETWORK 网络

3.1.1 bind

默认情况 bind=127.0.0.1 只能接受本机的访问请求。

如果服务器是需要远程访问的,需要将其注释掉。

3.2.2 protected-mode

默认是保护模式

如果服务器是需要远程访问的, 需要将 yes 设置为 no

3.3.3 port

服务默认端口 6379

3.3.4 timeout

一个空闲的客户端维持多少秒会关闭,0 表示关闭该功能, 即永不超时。默认为0。

3.3.5 tcp-keepalive

是对访问客户端的一种心跳检测,每隔 n 秒检测一次, 单位为秒。

如果设置为 0,则不会进行 Keepalive 检测,建议设置成60。

3.3.5.1 为什么需要心跳检测机制

TCP 协议中有长连接和短连接之分。短连接环境下,数据交互完毕后,主动释放连接。

长连接的环境下,进行一次数据交互后,很长一段时间内无数据交互时,客户端可能意外断开,这些 TCP 连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,且有可能导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。所以服务器端要做到快速感知失败,减少无效链接操作,这就有了TCP 的Keepalive(保活探测)机制。

3.2 GENERAL 通用

3.2.1 daemonize

是否为后台进程,设置为 yes

设置为 yes 后, 表示守护进程, 后台启动

3.2.2 pidfile

存放 pid 文件的位置,每个实例会产生一个不同的 pid 文件, 记录redis 的进程号。

3.2.3 loglevel

redis 日志分为 4 个级别,默认的设置为 notice, 开发测试阶段可以用debug(日志内容较多,不建议生产环境使用),生产模式一般选用 notice。

3.2.3.1 redis 日志分为 4 个级别说明
  1. debug:会打印出很多信息,适用于开发和测试阶段。
  2. verbose(冗长的):包含很多不太有用的信息,但比 debug 要清爽一些。
  3. notice:适用于生产模式。
  4. warning : 警告信息。

3.2.4 logfile

ogfile “” 就是说,默认为控制台打印,并没有日志文件生成

3.2.5 databases

databases 16

设定库的数量 默认 16,默认数据库为 0 号

3.3 SERCURITY 安全

3.3.1 requirepass foobared

永久设置密码

命令行设置临时密码 config set requirepass foobared

3.4 LIMITS 限制

3.4.1 maxclients

设置 redis 同时可以与多少个客户端进行连接。默认情况下为 10000 个客户端。

3.4.2 maxmemory

设置redis占用内存最大值

  • 在默认情况下, 对 32 位 实例会限制在 3 GB, 因为 32 位的机器最大只支持4GB的内存,而系统本身就需要一定的内存资源来支持运行,所以32 位机器限制最大3GB的可用内存是非常合理的,这样可以避免因为内存不足而导致Redis 实例崩溃。
  • 在默认情况下, 对于 64 位实例是没有限制
  • 当用户开启了 redis.conf 配置文件的 maxmemory 选项,那么Redis 将限制选项的值不能小于 1 MB
3.4.2.1 对 maxmemory 设置的建议
  1. Redis 的 maxmemory 设置取决于使用情况, 有些网站只需要32MB,有些可能需要12GB。
  2. maxmemory 只能根据具体的生产环境来调试,不要预设一个定值,从小到大测试,基本标准是不干扰正常程序的运行。
  3. Redis 的最大使用内存跟搭配方式有关,如果只是用 Redis 做纯缓存, 64-128M对一般小型网站就足够了
  4. 如果使用 Redis 做数据库的话,设置到物理内存的 1/2 到3/4 左右都可以
  5. 如果使用了快照功能的话,最好用到 50%以下,因为快照复制更新需要双倍内存空间,如果没有使用快照而设置 redis 缓存数据库,可以用到内存的80%左右,只要能保证Java、NGINX 等其它程序可以正常运行就行了

3.4.3 maxmemory-policy

  1. volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键;(最近最少使用)
  2. allkeys-lru:在所有集合 key 中,使用 LRU 算法移除 key
  3. volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键
  4. allkeys-random:在所有集合 key 中,移除随机的 key
  5. volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的key
  6. noeviction:不进行移除。针对写操作,只是返回错误信息

3.4.4 maxmemory-samples

设置样本数量,LRU 算法和最小 TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis 默认会检查这么多个 key 并选择其中LRU 的那个。

一般设置 3 到 7 的数字,数值越小样本越不准确,但性能消耗越小。

4、发布和订阅

  • Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者(pub) 发送消息,订阅者 (sub) 接收消息。Redis 客户端可以订阅任意数量的频道。当给频道发布消息后,消息就会发送给订阅的客户端。
  • Redis的发布和订阅:发布的消息没有持久化。订阅的客户端, 只能收到订阅后发布的消息
发送信息 hello
读取信息 hello
读取信息 hello
读取信息 hello
publisher 发布者
频道-channel1
客户端1
客户端2
客户端3

任务队列

  • 就是"传递消息的队列"。与任务队列进行交互的实体有两类,一类是生产者(producer),另一类则是消费者(consumer)。
  • 生产者将需要处理的任务放入任务队列中,而消费者则不断地从任务队列中读入任务信息并执行。

4.1 发布订阅模式分类

4.1.1 一个发布者,多个订阅者

主要应用:通知、公告 ,可以作为消息队列或者消息管道。

发送
发送
接收
接收
接收
接收
接收
接收
publisher 发布者
message
message
channel1
subscriber
subscriber
subscriber
channel2
subscriber
subscriber
subscriber

4.1.2 多个发布者,一个订阅者

各应用程序作为 Publisher 向 Channel 中发送消息,Subscriber 端收到消息后执行相应的业务逻辑,比如写数据库。

主要应用:排行榜、投票、计数。

message
message
message
message
message
publisher
publisher
publisher
publisher
channel
subscriber

4.1.3 多个发布者,多个订阅者

可以向不同的 Channel 中发送消息,由不同的 Subscriber 接收。

主要应用:群聊、聊天

message
message
message
message
message
message
message
message
publisher
publisher
publisher
publisher
channel
subscriber
subscriber
subscriber
subscriber

4.2 命令行实现发布和订阅

PUBLISH channel msg

发送信息,将信息 message 发送到指定的频道 channel

SUBSCRIBE channel channel

订阅频道,可以同时订阅多个频道。

UNSUBSCRIBE channel channel

取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道。

PSUBSCRIBE pattern pattern

订阅指定模式,订阅一个或多个符合给定模式的频道。

PUNSUBSCRIBE pattern pattern

退订指定的规则, 如果没有参数则会退订所有规则。

5、 Jedis

Jedis是Redis官方推荐的Java连接开发工具。在线文档

Linux——防火墙开放端口

firewall-cmd --permanent --add-port=6379/tcp
firewall-cmd --reload 

相关指令

  1. 打开端口: firewall-cmd --permanent --add-port=端口号/协议
  2. 关闭端口: firewall-cmd --permanent --remove-port=端口号/协议
  3. 重新载入,才能生效 : firewall-cmd --reload
  4. 查询端口是否开放: firewall-cmd --query-port=端口/协议
  5. 查看防火墙:firewall-cmd --list -all

设置 /etc/redis.conf

1、bind注销

2、requirepass查看密码

protected-mode no 关闭保护模式

3、查看 netstat -anp |more

maven 依赖

<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
    <version>3.2.0version>
dependency>

如果 redis 你设置了密码, 需要执行 jedis.auth(“密码”);进行权限验证。

6、 Spring Boot2 整合 Redis

6.1 maven 配置

 <parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.6.6version>
    <relativePath/>
parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>

    
    <dependency>
        <groupId>org.apache.commonsgroupId>
        <artifactId>commons-pool2artifactId>
        
    dependency>

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
    dependency>

    
    <dependency>
        <groupId>com.fasterxml.jackson.coregroupId>
        <artifactId>jackson-databindartifactId>
        <version>2.13.2.2version>
    dependency>

dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-maven-pluginartifactId>
        plugin>
    plugins>
build>

6.2 application.properties

也可以用application.yml 文件

# 完成 redis 的基本配置
#Redis 服务器地址
spring.redis.host=192.168.153.129
#Redis 服务器连接端口
spring.redis.port=6379
#Redis 如果有密码,需要配置, 没有密码就不要写
spring.redis.password=foobared
#Redis 数据库索引(默认为 0)
spring.redis.database=0
#连接超时时间(毫秒)
spring.redis.timeout=1800000
#连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0

6.3 Redis的配置类

是对要使用的 RedisTemplate bean 对象的配置, 可以理解成是一个常规配置.

与JdbcTemplate设计理念类似

RedisConfig.java

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template =
                new RedisTemplate<>();
        System.out.println("template=>" + template);//这里可以验证..
        RedisSerializer<String> redisSerializer =
                new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer =
                new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.WRAPPER_ARRAY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

7、RDB数据持久化

RDB(Redis DataBase) 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就Snapshot 快照,恢复时将快照文件读到内存。

  1. 整个过程中,主进程是不进行任何 IO 操作的,这就确保了极高的性能。
  2. 如果需要进行大规模数据的恢复, 且对于数据恢复的完整性不是非常敏感,那RDB方式要比 AOF 方式更加的高效。
  3. RDB 的缺点是最后一次持久化后的数据可能丢失。

如果你是正常关闭 Redis , 仍然会进行持久化, 不会造成数据丢失-如果是 Redis 异常终止/宕机, 就可能造成数据丢失。

Fork&Copy-On-Write

  1. Fork 的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
  2. 在 Linux 程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec 系统调用,出于效率考虑,Linux 中引入了"写时复制技术即: copy-on-write" 。 参考
  3. 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

RDB 及其执行流程

Redis 知识梳理_第1张图片

  1. redis 客户端执行 bgsave 命令或者自动触发 bgsave 命令;
  1. 主进程判断当前是否已经存在正在执行的子进程,如果存在,那么主进程直接返回;
  2. 如果不存在正在执行的子进程,那么就 fork 一个新的子进程进行持久化数据,fork 过程 是阻塞的,fork 操作完成后主进程即可执行其他操作;
  3. 子进程先将数据写入到临时的 rdb 文件中,待快照数据写入完成后再原子替换旧的 rdb文件
  4. 同时发送信号给主进程,通知主进程 rdb 持久化完成,主进程更新相关的统计信息

7.1 RDB 配置 /etc/redis.cof

dbfilename(持久化文件名字)

1、在 redis.conf 中配置文件名称, 默认为 dump.rdb

dbfilename dump.rdb

dir (文件存放位置)

2、文件位置默认为 Redis 启动时命令行所在的目录下,可以修改到/root/下或者其他目录。

默认为 dir ./

修改为 dir “/root/”

save (同步频率)

save 传入空字符串, 可禁止save

stop-writes-on-bgsave-error (磁盘满后设置)

当 Redis 无法写入磁盘的话(比如磁盘满了), 直接关掉 Redis 的写操作。推荐yes

rdbcompression (文件压缩设置)

对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis 会采用LZF 算法进行压缩。

如果你不想消耗 CPU 来进行压缩的话,可以设置为关闭此功能, 默认yes

rdbchecksum (检查文件完整性)

在存储快照后, 还可以让 redis 使用 CRC64 算法来进行数据校验,保证文件是完整的

但是这样做会增加大约 10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能, 推荐 yes

其他操作

save VS bgsave

  • save :save 时只管保存,其它不管,全部阻塞。手动保存, 不建议。
  • bgsave:Redis 会在后台异步进行快照操作, 快照同时还可以响应客户端请求。

flushall

执行 flushall 命令,也会产生 dump.rdb 文件, 数据为空.Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有key)。

lastsave 命令获取最后一次成功执行快照的时间(unix 时间戳 https://tool.lu/timestamp/)

7.2 RDB优势

  • 适合大规模的数据恢复
  • 对数据完整性和一致性要求不高更适合使用
  • 节省磁盘空间
  • 恢复速度快

7.3 劣势

  • 虽然 Redis 在 fork 时使用了写时拷贝技术(Copy-On-Write), 但是如果数据庞大时还是比较消耗性能。
  • 在备份周期在一定间隔时间做一次备份,所以如果 Redis 意外down 掉的话(如果正常关闭 Redis, 仍然会进行 RDB 备份, 不会丢失数据), 就会丢失最后一次快照后的所有修改。

7.4 使用方案

  • Redis 可以充当缓存, 对项目进行优化, 因此重要/敏感的数据建议在Mysql 要保存一份
  • 从设计层面来说, Redis 的内存数据, 都是可以重新获取的(可能来自程序, 也可能来自Mysql)
  • Redis 启动时, 初始化数据是从dump.rdb 来的。
  • 将dump.rdb进行备份,如果有必要可以写shell脚本进行备份。

8、AOF

AOF(Append Only File):以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下来(比如 set/del 操作会记录, 读操作 get 不记录)。

  • 只许追加文件但不可以改写文件。
  • redis 启动之初会读取该文件重新构建数据。
  • redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

文档

选择RDB还是AOF

1 append
2 sync
4 load
3 rewrite
命令写入
AOF缓冲
AOF文件
重启
  1. 客户端的请求写命令会被 append 追加到 AOF 缓冲区内。
  2. AOF 缓冲区根据 AOF 持久化策略[always,everysec,no]将操作sync 同步到磁盘的AOF文件中。
  3. AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件 rewrite 重写,压缩AOF 文件容量。
  4. Redis 服务重启时,会重新 load 加载 AOF 文件中的写操作达到数据恢复的目的。

8.1 开启AOF

在redis.conf中

appendfilename 文件名

AOF的文件的名称默认为appendonly.aof。

appendfilename '‘appendonly.aof’

AOF保存路径于RDB文件保存路径一致 dir,AOF 和 RDB 同时开启,系统默认取 AOF 的数据。

appendonly 启动

是否开启AOF,yes 开启。

appendonly yes

8.2 异常回复

1、如遇到 AOF 文件损坏,通过/usr/local/bin/redis-check-aof --fix appendonly.aof 进行恢复。

2、建议先: 备份被写坏的 AOF 文件。

3、恢复:重启 redis,然后重新加载。

可以修复但是可能会造成数据丢失。

8.3 appendfsync(设置同步频率)

appendfsync always

始终同步,每次 Redis 的写入都会立刻记入日志;性能较差但数据完整性比较好。

appendfsync everysec

每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。

appendfsync no

redis不主动进行同步,把同步时机交给操作系统(30s)

8.4 Rewrite 压缩

  1. AOF 文件越来越大,需要定期对 AOF 文件进行重写达到压缩。
  2. 旧的 AOF 文件含有无效命令会被忽略,保留最新的数据命令, 比如set a a1 ; set a b1; set a c1; 保留最后一条指令就可以了。
  3. AOF 重写降低了文件占用空间,更小的 AOF 文件可以更快的被 redis 加载。

手动触发重写

直接调用 bgrewriteaof 命令

自动触发重写

auto-aof-rewrite-min-size: AOF 文件最小重写大小, 只有当 AOF 文件大小大于该值时候才能重写, 默认配置 64MB 。

auto-aof-rewrite-percentage: 当前 AOF 文件大小和最后一次重写后的大小之间的比率等于或者大于指定的增长百分比,如 100 代表当前 AOF 文件是上次重写的两倍时候才重写。

系统载入时或者上次重写完毕时,Redis 会记录此时 AOF 大小,设为base_size,如果 Redis 的 AOF 当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis 会对 AOF 进行重写。

8.5 优势

  • 备份机制更稳健,丢失数据概率更低。
  • 可读的日志文本,通过操作 AOF 稳健,可以处理误操作。

8.6 劣势

  • 比起 RDB 占用更多的磁盘空间。
  • 恢复备份速度要慢 。
  • 每次读写都同步的话,有一定的性能压力。

9、事务—锁机制

  1. Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。
  2. 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  3. Redis 事务的主要作用就是串联多个命令防止别的命令插队。不具有原子性。

复习 MySQL事务的三特性

名字 解释
原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务的操作要么都发生,要么什么都不发生。
一致性(Consistency) 事务必须使数据库从一个一致性变换到另一个一致性的状态。
隔离性(Isolation) 事务的隔离性使多个用户访问数据库时,数据库为每一个用户(开启事务),
不被其他事务操作的数据所干扰。多个并发事务之间相互隔离。
持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变是永久性的。

Multi、Exec、discard

  1. 从输入Multi 命令开始,输入的命令都会依次进入命令队列中,但不会执行。
  2. 输入 Exec 后,Redis 会将之前的命令队列中的命令依次执行。
  3. 组队的过程中可以通过 discard 来放弃组队。

9.1 Redis 事务三特性

9.1.1 单独的隔离操作

  1. 事务中的所有命令都会序列化、按顺序地执行 。

  2. 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

9.1.2 没有隔离级别的概念

队列中的命令(指令), 在没有提交前都不会实际被执行。

9.1.3 不保证原子性

  1. 事务执行过程中, 如果有指令执行失败,其它的指令仍然会被执行, 没有回滚。

  2. 在组队时,命令语法出错,具有原子性。如果组队成功,有些指令执行失败,那么不具有原子性。

9.2 事务冲突

案例购票

如果没有控制, 会造成超卖现象 。 如果 3 个指令, 都得到执行, 最后剩余的票数是 -2

最后
1 查询到10
2 查询到10
3 查询到10
减6
减5
减1
4
-1
-2
10张票
if 10> 6
then -6
if 10> 5
then -5
if 10> 1
then -1

9.2.1 悲观锁

1 查询得到10
减6
2 block
2
2
2不操作
2
10张票
上锁
if 10> 6
then -6
解锁
余4张票
被阻塞
上锁
if 4> 5
then -6
解锁
余4张票
  1. 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁
  2. 这样别人/其它请求想拿这个数据就会 block 直到它拿到锁。
  3. 悲观锁是锁设计理念, 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.

9.2.2 乐观锁

v1.1
v1.0
1 查询得到10
2 查询得到10
check-and-set
v1.0==v1.0
减6
check-and-set
v1.0!=v1.1
不操作
余4张票
if 4> 5
then -4
余4张票
if 10> 6
then -6
10张票
if 10> 5
then -5

乐观锁在版本修改时具有原子性。

  1. 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁 。
  2. 但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
  3. 乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis 就是利用这种check-and-set机制实现事务的。

9.3 watch\unwatch

9.3.1 watch

  • 基本语法: watch key [key …]
  • 在执行 multi 之前,先执行 watch key1 [key2],可以监视一个(或多个) key ,如果在 事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。(类似乐观锁机制

案例
Redis 知识梳理_第2张图片

9.3.2 unwatch

取消 watch 命令对所有 key 的监视。

如果在执行 watch 命令后,exec 命令或 discard 命令先被执行了的话,那么就不 需要再执行 unwatch 。

9.4 测试工具 httpd-tools

安装httpd-tools
yum install httpd-tools
使用案例
ab -n 1000 -c 100 -p ~/postfile -T application/x-www-form-urlencoded http://192.168.153.1:8080/sr/secKillServlet

解读指令

ab 是并发工具程序

  1. -n 1000 表示一共发出 1000 次 http 请求

  2. -c 100 表示并发时 100 次, 你可以理解 1000 次请求, 会在10 次发送完毕

  3. -p ~/postfile 表示发送请求时, 携带的参数从当前目录的postfile 文件读取(文件需要事先创建

  4. -T application/x-www-form-urlencoded 就是发送数据的编码是基于表单的url 编码

9.5 Redis 连接池

节省每次连接 redis 服务带来的消耗,把连接好的实例反复利用。

9.5.1 链接池参数

  • MaxTotal:控制一个 pool 可分配多少个 jedis 实例,通过 pool.getResource()来获取; 如果赋值为-1,则表示不限制
  • maxIdle:控制一个 pool 最多有多少个状态为 idle(空闲)的 jedis 实例。
  • MaxWaitMillis:表示当获取一个 jedis 实例时,最大的等待毫秒数,如果超过等待时间, 则直接抛 JedisConnectionException。
  • testOnBorrow:获得一个 jedis 实例的时候是否检查连接可用性(ping());如果为 true, 则得到的 jedis 实例均是可用的。

9.5.2 JedisPoolUtil

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author xiaoyu
 * @version 1.0
 * 使用连接池的方式来获取Redis连接
 */
public class JedisPoolUtil {
    
    //解读volatile作用
    //1. 线程的可见性: 当一个线程去修改一个共享变量时, 另外一个线程可以读取这个修改的值
    //2. 顺序的一致性: 禁止指令重排
    private static volatile JedisPool jedisPool = null;


    private JedisPoolUtil() {}

    //保证每次调用返回的 jedisPool是单例-这里使用了双重校验
    public static JedisPool getJedisPoolInstance() {

        if (null == jedisPool) {
            synchronized (JedisPoolUtil.class) {
                if (null == jedisPool) {
                    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
                    //对连接池进行配置
                    jedisPoolConfig.setMaxTotal(200);
                    jedisPoolConfig.setMaxIdle(32);
                    jedisPoolConfig.setMaxWaitMillis(60 * 1000);
                    jedisPoolConfig.setBlockWhenExhausted(true);
                    jedisPoolConfig.setTestOnBorrow(true);
                    jedisPool = new JedisPool(jedisPoolConfig, "192.168.153.129", 6379, 60000,"foobared");
                }
            }
        }
        return jedisPool;
    }

    //释放连接资源
    public static void release(Jedis jedis) {
        if(null != jedis) {
            jedis.close();//如果这个jedis是从连接池获取的,这里jedis.close(),就是将jedis对象/连接,释放到连接池
        }
    }
}

9.6 LUA 脚本

9.6.1 EVAL

EVAL script numkeys key [key ...] arg [arg ...]

  1. 第一个参数是由脚本的 Lua 源代码组成的字符串 。
  2. 第二个参数是脚本正文后面的参数数。脚本访问的所有键名称都必须作为输入键参数显式提供
  3. 从第三个参数开始,表示 Redis 键名称。
  4. 第四个参数是输入参数

推荐阅读:Redis LUA API 、Lua语法教程

例子

127.0.0.1:6379> eval "return 'Hello World!'" 0 
"Hello World!"
127.0.0.1:6379> eval "return ARGV[1]" 0 "Hello World !"
"Hello World !"
127.0.0.1:6379[2]> eval "return  {ARGV[1],KEYS[1]}" 1 "jinyu" "Hello World !"
1) "Hello World !"
2) "jinyu"

9.6.2 redis.call()\redis.pcall()

调用 redis.call() 函数引发的错误将直接返回给执行它的客户端。相反,调用 redis.pcall() 函数时遇到的错误将返回到脚本的执行上下文,以便进行可能的处理。

例子

127.0.0.1:6379[2]> keys *
(empty array)
127.0.0.1:6379[2]> EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 test1 Hello
OK
127.0.0.1:6379[2]> keys *
1) "test1"
127.0.0.1:6379[2]> get test1
"Hello"

9.6.3 脚本缓存

通过调用 SCRIPT LOAD 命令并提供其源代码,将脚本加载到服务器的缓存中。服务器不执行脚本,而只是编译并将其加载到服务器的缓存中。加载后,可以使用从服务器返回的 SHA1 摘要执行缓存的脚本。

Redis 脚本缓存始终是易失性的。它不被视为数据库的一部分,也不会持久保存。缓存可以在服务器重新启动时清除,在故障切换期间当复制副本担任主角色时清除,或者由 显式 SCRIPT FLUSH 清除。这意味着缓存的脚本是临时的,缓存的内容可能随时丢失。

常用命令

  1. SCRIPT LOAD script

    此命令在 Redis 脚本缓存中注册指定的脚本。在我们想要确保不会 EVALSHA 失败的所有上下文中,它是一个有用的命令(例如,在管道中或从 [ / 事务](/topics MULTI / EXEC transactions)调用时),而无需执行脚本。

  2. SCRIPT EXISTS

    给定一个或多个 SHA1 摘要作为参数,此命令返回一个包含 1 和 0 的数组。1 表示特定 SHA1 被识别为脚本缓存中已存在的脚本。0 的意思是具有此 SHA1 的脚本之前未加载(或者至少自最近一次调用 以来 SCRIPT FLUSH 从未加载过)。

  3. EVALSHA script numkeys key [key ...] arg [arg ...]

127.0.0.1:6379[2]> script load "return 'Hello World !'"
"7b39be95cdcf24151596a4f42203ab6f6bf09a0b"
127.0.0.1:6379[2]> EVALSHA 7b39be95cdcf24151596a4f42203ab6f6bf09a0b 0
"Hello World !"
127.0.0.1:6379[2]> script flush
OK
127.0.0.1:6379[2]> EVALSHA 7b39be95cdcf24151596a4f42203ab6f6bf09a0b 0
(error) NOSCRIPT No matching script. Please use EVAL.

10、主从复制

  • 好处: 读写分离, 提升效率 (理解: 读写分离后, 将读和写操作分布到不同的Reids, 减少单个 Redis 的压力, 提升效率)
  • 好处: 容灾快速恢复。 (理解: 如果某个 slaver , 不能正常工作, 可以切换到另一个slaver)
  • 主从复制, 要求是 1 主多从, 不能有多个 Master。( 理解: 如果有多个主服务器Master, 那么 slaver 不能确定和哪个 Master 进行同步, 出现数据紊乱)
  • 要解决主服务器的高可用性, 可以使用 Redis 集群。
复制
复制
复制
应用程序
主Master
从-Slave
从-Slave
从-Slave
  • Slave 启动成功连接到 master 后会发送一个 sync 命令
  • Master 接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后, master 将传送整个数据文件到 slave,以完成一次完全同步
  • slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中, 即 全量复制。
  • Master 数据变化了, 会将新的收集到的修改命令依次传给 slave, 完成同步, 即 增量复制
  • 但是只要是重新连接 master,一次完全同步(全量复制)将被自动执行

10.1 搭建一主多从

1、创建目录/jyuRedis, 并拷贝 redis.conf 到 /jyuRedis/redis.conf。

mkdir /jyuRedis
cp /etc/redis.conf /jyuRedis/redis.conf

2、设置以守护线程的方式启动 /jyuRedis/redis.conf,关闭AOF备份只开启RDB备份 。

daemonize yes
appendonly no

3、在/jyuRedis目录下创建三个配置文件(redis6379.conf, redis6380.conf, redis6381.conf)。

include /jyuRedis/redis.conf
port 6380
pidfile /var/run/redis_6380.pid
dbfilename dump6380.rdb

3、连接redis ,命令info replication 查看主从复制相关信息。

redis-server /jyuRedis/redis6379.conf 
redis-server /jyuRedis/redis6380.conf
redis-server /jyuRedis/redis6381.conf 

4、使用salveof 设置 主机 (保证主机于从机网络连接)salveof master_ip master_port

slaveof 127.0.0.1 6379

10.2 薪火相传

上一个 Slave 可以是下一个 slave 的 Master,Slave 同样可以接收其他 slaves 的连接和同步请求,那么该 slave 作为了链条中下一个的 master, 可以有效减轻 master 的写压力,去中心化降低风险。slaveof

Redis \n主服务器
Redis\n从服务器
Redis\n从服务器

风险是一旦某个 slave 宕机,后面的 slave 都没法同步。

主机挂了,从机还是从机,无法写数据了。

10.3 反客为主

1、在薪火相传的结构下, 当一个 master 宕机后, 指向 Master 的 slave 可以升为 master, 其后面的 slave 不用做任何修改.
2、用 slaveof no one 将从机变为主机 (说明: 后面可以使用哨兵模式, 自动完成切换.)

slaveof no one

10.4 哨兵模式

监听到出现故障
切换 为主服务器
主服务器
从服务器
从服务器

10.4.1 启动哨兵监控

  • 创建配置文件 sentinel.conf (/jyuRedis/sentinel.conf)
sentinel monitor redis_master 127.0.0.1 6380 1

sentinel monitor 是关键字, redis_master 是对监控的服务所起的名字(自由起名)。127.0.0.1 是IP地址(参考实际情况更改),6380是监听的端口。1是投票数量。

  • 启动
/usr/local/bin/redis-sentinel /jyuRedis/sentinel.conf

10.4.2 使用细节

Redis 知识梳理_第3张图片

  • 优先级在 redis.conf 中默认:replica-priority 100,值越小优先级越高
  • 偏移量是指获得原主机数据的量, 数据量最全的优先级高
  • 每个 redis 实例启动后都会随机生成一个 40 位的 runid, 值越小优先级越高

11、集群

11.1 实际需求和问题

  • 容量不够,redis 如何进行扩容?

  • 并发写操作, redis 如何分摊?

  • 主从模式,薪火相传模式,主机宕机,会导致 ip 地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。

11.1.2 传统解决方案:代理主机

proxy
代理从机:Slave
代理主机:nMaster
A
A主机
Master
A从机
Slave
B
B主机
Master
B从机
Slave
C
C主机
Master
C从机
Slave
  1. 客户端请求先到代理服务器 。
  2. 由代理服务器进行请求转发到对应的业务处理服务器。
  3. 为了高可用性, 代理服务、A 服务、B 服务、C 服务都需要搭建主从结构(至少是一主一从), 这样就需求搭建至少 8 台服务器 。
  4. 这种方案的缺点是: 成本高,维护困难, 如果是一主多从, 成本就会更高。

11.1.3 无中心化集群

A
B
C
A主机
Master
A从机
Slave
B主机
Master
B从机
Slave
C主机
Master
C从机
Slave
  1. 各个 Redis 服务仍然采用主从结构 。
  2. 各个 Redis 服务是连通的, 任何一台服务器, 都可以作为请求入口。
  3. 各个 Redis 服务器因为是连通的, 可以进行请求转发 。
  4. 这种方式, 就无中心化集群配置, 可以看到,只需要 6 台服务器即可搞定。
  5. 无中心化集群配置, 还会根据 key 值, 计算 slot , 把数据分散到不同的主机, 从而缓解单个主机的存取压力。
  6. Redis 推荐使用无中心化集群配置 。
  7. 在实际生成环境 各个 Redis 服务器, 应当部署到不同的机器(防止机器宕机, 主从复制失效) 。

回顾linux 关于vim 文本替换指令

:[addr]s/源字符串/目的字符串/[option]

  • [addr]: 表示检索范围,省略时表示当前行。

    • 如:“1,20” :表示从第1行到20行;
    • “%” :表示整个文件,同“1,$”;
    • “. ,$” :从当前行到文件尾;
  • [option] :表示操作类型

    • 如:g 表示全局替换;
    • c 表示进行确认
    • p 表示替代结果逐行显示(Ctrl + L恢复屏幕)

省略option时仅对每行第一个匹配串进行替换;

如果在源字符串和目的字符串中出现特殊字符,需要用”\”转义

11.2 集群的搭建

  • 在每一个节点的配置文件中,修改配置。
include /jyuRedis/redis.conf
port 6380
pidfile /var/run/redis_6380.pid
dbfilename dump6380.rdb

# 打开集群模式
cluster-enabled yes
# 设定节点配置文件名
cluster-config-file nodes-6379.conf 
# 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换
cluster-node-timeout 15000 
  • 启动每个节点的redis-server。
redis-server redis6379.conf 
  • 将所有节点合成一个集群

进入安装redis时的源文件目录,/opt/redis-6.2.6/src/。根据实际情况修改。主要是使用redis-cli,同时还需要一个ruby环境,redis6有ruby环境。

 redis-cli --cluster create --cluster-replicas 1 192.168.153.129:6379 192.168.153.129:6380 192.168.153.129:6381 192.168.153.129:6389 192.168.153.129:6390 192.168.153.129:6391

1、一个集群至少要有三个主节点

2、选项 --cluster-replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。

3、分配原则:尽量保证主服务器和从服务器各自运行在不同的IP 地址(机器), 防止机器故障导致主从机制失效, 高可用性得不到保障

  • 集群方式登录
redis-cli -c -p 6379
# 查看集群中节点主从关系
cluster nodes

11.3 使用集群

11.3.1 slot 介绍

  1. 一个 Redis 集群包含 16384 个插槽(hash slot),编号从0-16383, Reids 中的每个键都属于这 16384 个插槽的其中一个

  2. 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽,其中CRC16(key) 语句用于计算键 key 的 CRC16 校验和

  3. 集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点,其中:例如

    • 节点 A 负责处理 0 号至 5460 号插槽。

    • 节点 B 负责处理 5461 号至 10922 号插槽。

    • 节点 C 负责处理 10923 号至 16383 号插槽

11.3.2 在集群中录入值

  1. 在 redis 每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是该客户端对应服务器的插槽,redis 会告知应前往的 redis 实例地址和端口。

  2. redis-cli 客户端提供了 –c 参数实现自动重定向。 如 redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。

  3. 不在一个 slot 下的键值,是不能使用 mget,mset 等多键操作

    因为k1,k2,k3可能被分不到不同的slot,因此会报错,所以可以通过{}来定义组的概念,从而使 key 中{}内相同内容的键值对放到一个slot 中去。mset k1{name} v1 k2{name} v2 k3{name} v3

    不在一个 slot 下的键值,是不能使用 mget,mset。

11.3.3 查询集群中的值

  1. 指令: CLUSTER KEYSLOT key 返回 key 对应的 slot 值 。
  2. 指令: CLUSTER COUNTKEYSINSLOT slot返回 slot 有多少个key。
  3. 指令: CLUSTER GETKEYSINSLOT slot count返回 count 个slot 槽中的键。

11.3.4 Redis 故障恢复

  1. 如果主节点下线,从节点自动升为主节点。原主节点恢复后,原主节点变为从机。

  2. 如果某一段插槽的所有主从节点都宕掉,Redis 服务是否还能继续, 要根据不同的配置而言

    • cluster-require-full-coverage 为 yes ,那么整个集 群都挂掉
    • cluster-require-full-coverage 为 no , 那么只是该插槽 数据不能使用,也无法存储

11.4 优点与缺点

11.4.1 优点

1、实现扩容 2、分摊压力 3、无中心配置相对简单

11.4.2 缺点

1、多键操作是不被支持的 2、多键的 Redis 事务是不被支持的。lua 脚本不被支持 3、由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而其它方案想要迁移至 redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

11.5 Jedis使用集群

Set<HostAndPort> hostAndPorts = new HashSet<>();
hostAndPorts.add(new HostAndPort("192.168.153.129",6379));
JedisCluster jedisCluster = new JedisCluster(hostAndPorts);

你可能感兴趣的:(复习之旅,redis,笔记,vim,缓存,数据库)