文章目录
- redis简介
- redis - linux安装
- redis - docker安装,并指定配置文件启动
- 常用指令
- redis持久
- redis应用场景实战
- redis性能优化
- redis事务
- redis的发布订阅
- redis主从
- 哨兵机制
- 高可用集群
redis简介
- 介绍
redis是一种基于键值对(key-value)数据库,其中value可以为String 、hash 、list 、set 、zset 等多种数据结构。
可以满足很多应用场景。还提供了键过期,发布订阅,事务,流水线等附加功能。
流水线: redis的流水线功能允许客户端一次将多个命令请求发送给服务器。并将被执行的多个命令请求的结果在一个命令回复中
全部返回给客户端,使用这个功能可以有效地减少客户端在执行多个命令时需要与服务器进行通信的次数。
- 特性
1、速度快,数据放在内存中,官方给出的读写性能 10万/S(与机器的性能有关)
a、数据存放在内存是速度快的主要原因
b、C语言实现,与操作系统距离近
C、使用了单线程架构,预防多线程可能产生的竞争问题
2、键值对的数据结构服务器
3、丰富的功能
4、简单稳定(单线程)
a、三个客户端同时执行命令
执行命令: 发送指令 -> 执行命令 -> 返回结果
执行过程: 所有命令进入,按顺序执行,使用I/O多路复用解决I/O问题
5、持久化 : 有将数据备份到硬盘的机制。 发生断电或机器故障,数据肯会丢失
6、主从复制
7、高可用和分布式 : 哨兵机制实现高可用,保证redis节点故障发现和自动转移
8、客户端语言支持多语言 : java php python c c++ nodejs
- 使用场景
1、缓存 : 合理使用缓存加快数据访问速度,降低后端数据源压力
2、排行榜 : 按照热度排名,按照发布时间排行,主要用到列表和有序集合
3、计数器应用: 视频网站播放数,网站浏览数等
4、社交网络 : 赞、踩、粉丝、下拉刷新
5、消息队列: 发布和订阅
6、其他场景等等
redis - linux安装
- 安装
$ wget http://download.redis.io/releases/redis-4.0.14.tar.gz
$ tar -zxvf redis-4.0.14.tar.gz
$ cd redis-4.0.14
$ make PREFIX=/usr/local/redis install
$ cp redis.conf /usr/local/redis/redis.conf
- 配置
vi /usr/local/redis/redis.conf
#以后端模式启动
将 daemonize no 改为 daemonize yes
#开放远程连接
protected-mode yes 改为 protected-mode no
注释掉bind 127.0.0.1 #注释掉bind所有的ip访问都可以redis.若是想指定多个ip访问,并不是全部的ip访问,可以bind 192.168.1.100 10.0.0.1 进行配置
#设置密码
requirepass 123456
- 启动 、操作、关闭
可执行文件 | 作用 |
redis-server | 启动redis |
redis-cli | redis命令行客户端 |
redis-benchmark | 基准测试工具 |
redis-check-aof | AOF持久化文件检测和修复工具 |
redis-check-dump | RDB持久化文件检测和修复工具 |
redis-sentinel | 启动哨兵 |
redis-trib | cluster集群构建工具 |
#redis-server启动
redis-server #默认启动
redis-server --port 6380 #指定端口启动
redis-server /usr/local/redis/redis.conf #指定配置文件启动
#redis-cli启动
1、交互式
redis-cli -h {host} -p {port} -a {password}
#连接格式, 不写 -h 默认127.0.0.1 , 不写 -p 默认 6379,没有密码-a可以省略
2、命令式
redis-cli -h 127.0.0.1 -p 6379 get age #获取key值为age的value值
3、停止redis服务
redis-cli shutdown
a、使用redis-cli shutdown关闭,断开连接时会持久化文件生产,相对安全
b、还可以用kill关闭,此方式不会做持久化,还会造成缓冲区非法关闭,可能会造成AOF和数据丢失
4、手动持久化(redis-cli 登录命令行客户端后)
shutdown nosave|save
- 版本选择
1、版本号第二位为奇数,为非稳定版本(2.7 , 2.9 , 3.1)
2、版本号第二位为偶数,为稳定版本(2.8 , 3.0 , 3.2)
3、奇数版本是下一个稳定版本的开发版本,如2.9是3.0的开发版本
redis - docker安装,并指定配置文件启动
- 安装
#下载redis镜像
docker pull redis:4.0.14
#下载对于版本的redis.conf,并修改配置-开放远程连接
protected-mode yes 改为 protected-mode no
注释掉bind 127.0.0.1
#创建并运行redis容器
docker run --privileged=true -p 6379:6379 --name redis -v /var/lib/docker/volumes/redis/_data:/data -d redis:latest redis-server /data/redis.conf
- 参数说明
#创建redis容器参数说明
--privileged=true
> 解决无法访问目录,权限拒绝问题。该问题常出现在centos7中,因为centos7中的安全模块selinux把权限禁掉了,一般的解决方案有以下两种:
> 1.临时关闭selinux,直接在centos服务器上执行以下命令即可。执行完成以后建议重新docker run。
> setenforce 0
> 2.给容器加权限,在运行docker run时给该容器加上以下参数即可:
> --privileged=true
-p 6379:6379 >端口映射
--name redis >指定容器名称
-v /var/lib/docker/volumes/redis/_data:/data
> 挂载目录
> redis.conf文件要放在挂载的/var/lib/docker/volumes/redis/_data目录下
-d >后台启动
redis:latest >指定镜像
redis-server /data/redis.conf >指定配置文件启动redis
常用指令
- 全局操作指令
#查看所有键
keys *
#查看键总数
dbsize
#检测键是否存在 存在返回1 , 不存在返回 0
exists key
#删除键 返回删除键个数,删除不存在的键返回 0
del key
#设置键的过期时间(单位秒)
expire key seconds
#查看key的过期时间(返回 -1 表示不过期, 返回 -2 表示键不存在)
ttl key
#查看键的数据类型 (键不存在返回 none)
type key
#切换数据库(默认支持16个数据库)
select 1
#清空当前库所有数据
flushdb
#清空所有库所有数据
flushall
- string 字符串操作指令
字符串类型:实际上可以是字符串(包括XML JSON),还有数字(整形浮点数),二进制(图片音频视频),最大不能超过512MB
#存储设值 (存储成功返回1 , 失败返回 0)
set key ex seconds #ex过期时间单位秒
set key px milliseconds #px过期时间单位毫秒
#获取 (存在返回value值,不存在返回nil)
get key
#批量设值
mset key value [key value ...]
#批量获取
mget key [key ...]
#计数
incr key #必须为整数自加1,非整数返回错误,无key键从0自增
incrby key 2 #自加给定数值
decr key #自减1
decrby kay 2 #自减给定数值
incrbyfloat key 1.1 #自增浮点数给定数值
#追加指令 (在原有value值上添加给定的值)
append key value
#获取字符串长度 (每个中文占3个字节)
strlen key
#截取字符串(返回给定下标范围内的字符串)
getrange key 2 4
#查看编码类型
object encoding key
#value值只含整数数字的返回 int
#value值小于等于39字节长度的返回 embstr
#value值大于39字节长度的返回 raw
get/set与mget/mset的区别(图一 get set请求,图二 mget mset 请求 )
- hash 哈希操作指令
是一个string 类型的field 和value 的映射表,hash 特适合用于存储对象。
#设值
hset key field value
#取值
hget key field
#删值
hdel key field
#计算个数(key键下有几个字段)
hlen key
#批量设值
hmset key filed value [filed value ...]
#批量取值
hmget key filed [filed ...]
#判断field是否存在
hexists key filed
#获取所有field
hkeys key
#获取所有value
hvals key
#获取所有的filed 和value
hgetall key
#为某个字段增加给定整数
hincrby key filed 2
#为某个字段增加给定浮点数
hincrbyfloat key filed 2.1
#内部编码 (ziplist 压缩列表 , hashtable 哈希表 )
object encoding key
#当filed个数少且没有大的value时,内部编码为ziplist
#当value大于64字节,内部编码由ziplist变成hashtable
- list 列表操作指令
用来存储多个有序的字符串,一个列表最多可存2 的32 次方减1 个元素
因为有序,可以通过索引下标获取元素或某个范围内元素列表,列表元素可以重复
#依次向尾部添加数据
rpush key value [value ...]
#依次向头部添加数据
lpush key value [value ...]
#向指定元素前面或者后面添加元素(before向前面添加, after向后面添加, pivot 已经存在的元素 )
linsert key before|after pivot value
#查询指定下标范围元素(索引下标: 从左到右为0 到N-1,-1 代表最后一位元素)
lrange key start stop
#返回指定下标元素(index 可以为负值, -1 代表最后一位元素)
lindex key index
#返回列表长度
llen key
#删除第一个元素
lpop key
#删除最后一个元素
rpop key
#删除指定个数的指定元素
#1、count为指定的个数, 当count为 0 时删除所有匹配的元素
#2、从左向右依次删除
lrem key count value
#保留指定下标范围的元素
#如: ltrim key 1 3 保留下标为1到3的元素,其他全删除
ltrim key start stop
#替换指定下标的value值
lset key index value
#移出并获取列表的第一个元素
#如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
#timeout指定超时时间 单位秒
blpop key [key ...] timeout
#移出并获取列表的最后一个元素
#如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
#timeout指定超时时间 单位秒
brpop key [key ...] timeout
#内部编码
object encoding key
3.2版本以前是 ziplist 和 linkedlist两种
3.2版本以后只有quicklist一种编码
quicklist,它结合了ziplist和linkedlist 两者的优势.编码效率更高
- set 集合操作指令
保存多元素,与列表不一样的是不允许有重复元素,且集合是无序,一个集合最多可存2 的32 次方减1 个元素,除了支持增删改查,还支持集合交集、并集、差集
#添加元素(相同的元素,则重复无效)
sadd key value [value ...]
#获取所有value值(返回结果无序)
smembers key
#删除指定元素
srem key value
#获取元素个数
scard key
#判断元素是否在集合存在,存在返回1,不存在0
sismember key value
#随机返回指定个数元素
srandmember key 2
#随机移除并返回指定个数元素
spop key 2
#求集合交集
sinter key [key ...]
#求集合并集
sunion key [key ...]
#求集合差集
sdiff key [key ...]
#求交集并将结果保存到一个新的集合中(destination 新集合key值)
sinterstore destination key [key ...]
#求并集并将结果保存到一个新的集合中(destination 新集合key值)
sunionstore destination key [key ...]
#求差集并将结果保存到一个新的集合中(destination 新集合key值)
sdiffstore destination key [key ...]
#内部编码
object encoding key
#intset 当元素个数小于512且都为整数
#hashtable 当超过512个或不为整数
使用场景
标签,社交,查询有共同兴趣爱好的人,智能推荐
使用方式:
给用户添加标签:
sadd user:1:fav basball fball pq
sadd user:2:fav basball fball
............
或给标签添加用户
sadd basball:users user:1 user:3
sadd fball:users user:1 user:2 user:3
........
计算出共同感兴趣的人:
sinter user:1:fav user2:fav
规则:sadd (常用于标签) spop/srandmember(随机,比如抽奖)
sadd+sinter (用于社交,查询共同爱好的人,匹配)
- zset 有序集合操作指令
常用于排行榜,如视频网站需要对用户上传视频做排行榜,或点赞数
#添加元素 (NX 表示键必须不存在,用于添加. XX 表示键必须存在,用于修改.)
zadd key [NX|XX] [CH] [INCR] score member [score member ...]
zadd key score member [score member......]
zadd user:zan 200 james //james的点赞数1, 返回操作成功的条数1
zadd user:zan 200 james 120 mike 100 lee // 返回3
zadd test:1 nx 100 james //键test:1必须不存在,主用于添加
zadd test:1 xx incr 200 james //键test:1必须存在,主用于修改,此时为300
zadd test:1 xx ch incr -299 james //返回操作结果1,300-299=1
zrange test:1 0 -1 withscores //查看点赞(分数)与成员名
zcard test:1 //计算成员个数, 返回1
排名场景:
zadd user:3 200 james 120 mike 100 lee //先插入数据
zrange user:3 0 -1 withscores //查看分数与成员
zrank user:3 james //返回名次:第3名返回2,从0开始到2,共3名
zrevrank user:3 james //返回0, 反排序,点赞数越高,排名越前
redis持久
- redis持久化机制
redis 支持 RDB 和 AOF 两种持久化机制,持久化可以避免因进程退出而造成数据丢失;
- RDB持久化
RDB持久化把当前进程数据生成快照(.rdb)文件保存到硬盘的过程,有手动触发和自动触发
手动触发有save和bgsave两命令
save命令:阻塞当前Redis,直到RDB持久化过程完成为止,
若内存实例比较大会造成长时间阻塞,线上环境不建议用它
bgsave命令:redis进程执行fork操作创建子进程,由子线程完成持久化,
阻塞时间很短(微秒级),是save的优化,在执行redis-cli shutdown关闭redis服务时
如果没有开启AOF持久化,自动执行bgsave;
bgsave 运行流程
RDB文件的操作
命令:config set dir /usr/local //设置rdb文件保存路径
备份:bgsave //将dump.rdb保存到usr/local下
恢复:将dump.rdb放到redis安装目录与redis.conf同级目录,重启redis即可
优点: 1,压缩后的二进制文,适用于备份、全量复制,用于灾难恢复
2,加载RDB恢复数据远快于AOF方式
缺点: 1,无法做到实时持久化,每次都要创建子进程,频繁操作成本过高
2,保存后的二进制文件,存在老版本不兼容新版本rdb文件的问题
- AOF持久化
针对RDB不适合实时持久化,redis提供了AOF持久化方式来解决
- 开启AOF
redis.conf设置: appendonly yes (默认不开启,为no)
默认文件名: appendfilename "appendonly.aof"
AOF配置详解
appendonly yes //启用aof持久化方式
# appendfsync always //每收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec //每秒强制写入磁盘一次,性能和持久化方面做了折中,推荐
no-appendfsync-on-rewrite yes //正在导出rdb快照的过程中,要不要停止同步aof
auto-aof-rewrite-percentage 100 //aof文件大小比起上次重写时的大小,增长率100%时,重写
auto-aof-rewrite-min-size 64mb //aof文件,至少超过64M时,重写
AOF持久化流程
流程说明:
1,所有的写入命令(set hset)会append追加到aof_buf缓冲区中
2,AOF缓冲区向硬盘做sync同步
3,随着AOF文件越来越大,需定期对AOF文件rewrite重写,达到压缩
4,当redis服务重启,可load加载AOF文件进行恢复
AOF如何恢复?
1. 设置appendonly yes;
2. 将appendonly.aof放到dir参数指定的目录;
3. 启动Redis,Redis会自动加载appendonly.aof文件
- redis重启时AOF和RDB的加载顺序
redis应用场景实战
redis性能优化
- redis慢查询
#开启慢查询方式一
config set slowlog-log-slower-than 10000 //10毫秒
config rewrite //将配置持久化保存到redis.conf
#开启慢查询方式二
#修改redis.conf即可(slowlog-log-slower-than 默认配置 10毫秒,线上可以根据redis并发量来调整,对高并发建议为1毫秒)
slowlog-log-slower-than 10000
#慢查询记录的是存在队列中的,slow-max-len 存在的记录最大条数
#比如slow-max-len=10 ,当有第11条慢查询命令插入时,队列的第一条命令就会出列
#线上可加大slow-max-len的值,记录慢查询存长命令时redis会做截断,不会占用打了内存,线上可配置1000以上
#慢查询命令
slowlog get //获取队列里慢查询的命令
slowlog len //获取慢查询列表当前的长度
slowlog reset //对慢查询列表清理
- redis性能测试工具
#100个并发连接 , 10000个请求,检测服务器性能
redis-benchmark -h 192.168.227.134 -p 6379 -c 100 -n 10000
#测试存储大小为100字节的数据包的性能
redis-benchmark -h 192.168.227.134 -q -d 100
#只测试 指定指令的性能 set, get
redis-benchmark -h 192.168.227.134 -q -n 10000 -t get,set
#只测试某些数值的性能
redis-benchmark -h 192.168.227.134 -p 6379 -n 100000 -q script load "redis.call('set','foo','bar')"
- 使用PIPELINE
pipeline就是把一组命令进行打包,然后一次性通过网络发送到Redis。同时将执行的结果批量的返回回来
Redis是采用基于C/S模式的请求/响应协议的TCP服务器。
性能问题一:redis客户端发送多条请求,后面的请求需要等待前面的请求处理完后,才能进行处理,而且每个请求都存在往返时间RRT(Round Trip Time),即使redis性能极高,当数据量足够大,也会极大影响性能。
性能问题二:我们可以通过scan命令来解决,如何来设置count又是一个问题,设置不好,同样会有大量请求存在,即使设置到1w(推荐最大值),如果扫描的数据量太大,这个问题同样不能避免。每个请求都会经历三次握手、四次挥手,在处理大量连接时,处理完后,挥手会产生大量time-wait,如果该服务器提供其他服务,可能对其他服务造成影响。
- 例如
Jedis jedis = new Jedis("192.168.227.134", 6379);
jedis.set("addr", "chonginq");
jedis.del("addr");
#这段代码我们设置了addr的值,同时又删除了键addr。如果Redis和我们的Java程序在同一台服务器,那么可能不明显,
#如果我们的Java程序和Redis属于跨机房,那么这个命令就会通过网络发送两次。如果很多这种短小的命令通过网络传输势必就会造成网络延迟。于是就提出了pipeline的概念。
pipeline的使用
Jedis jedis = new Jedis("192.168.227.134", 6379);
try {
Pipeline pipelined = jedis.pipelined();
pipelined.set("addr", "chongqing");
pipelined.del("addr");
//pipelined.sync();//没有返回值
List<Object> list= pipelined.syncAndReturnAll(); //程序会阻塞,等到所有命令执行完之后返回一个List集合。
for (Object object : list) {
System.out.println(object);
}
} finally {
jedis.close();
}
集群下优化RedisPipeline操作
在redis集群上使用上面的方法操作,会导致执行时间变成长,也会带来额外的消耗。那么集群下如何使用Pipeline操作呢?Redis 集群中内置了 16384 个哈希槽,redisCluster把所有的物理节点映射到[0-16383]slot上,由cluster 负责维护。比如A,B,C三个节点,那么对应槽点的范围值可能是分别是0-5400,5401-10800,10801-16383。我们需要根据key用CRC16算法计算出该key的槽点值,获取到相应的节点,然后把同一节点的数据通过pipeline获取。则可以极大提升效率。
- 键的迁移
- 键的遍历
redis事务
redis的发布订阅
redis主从
- 使用docker安装redis主从
# 前置准备
#下载redis镜像
docker pull redis:4.0.14
#下载对于版本的redis.conf,并修改配置-开放远程连接
protected-mode yes 改为 protected-mode no
注释掉bind 127.0.0.1
#方式一:
新增redis6380.conf, 加入slaveof 192.168.227.134 6379, 在6379启动完后再启6380,完成配置
docker run --privileged=true -p 6379:6379 --name redis6379 -v /var/lib/docker/volumes/redis/_data:/data -d redis:latest redis-server /data/redis6379.conf
docker run --privileged=true -p 6380:6379 --name redis6380 -v /var/lib/docker/volumes/redis/_data:/data -d redis:latest redis-server /data/redis6380.conf
#方式二:
启动redis-server时,加入 --slaveof 192.168.227.134 6379
docker run --privileged=true -p 6380:6379 --name redis6380 -v /var/lib/docker/volumes/redis/_data:/data -d redis:latest redis-server --slaveof 192.168.227.134 6379
#方式三:
进入从节点redis-cli控制台后执行 slaveof 192.168.227.134 6379
6380:> slaveof 192.168.227.134 6379
#其他常用操作
#查看状态:
info replication
#断开主从复制:
在slave节点,执行6380:>slaveof no one
#断开后再变成主从复制:
在slave节点,执行6380:> slaveof 192.168.42.111 6379
#数据较重要的节点,主从复制时使用密码验证:
requirepass
#传输延迟:主从一般部署在不同机器上,复制时存在网络延时问题,
redis提供repl-disable-tcp-nodelay 参数决定是否关闭TCP_NODELAY,默认为关闭
参数关闭时:无论大小都会及时发布到从节点,占带宽,适用于主从网络好的
场景,
参数启用时:主节点合并所有数据成TCP包节省带宽,默认为40毫秒发一次,
取决于内核,主从的同步延迟40 毫秒,适用于网络环境复杂或带宽紧张,如跨机
房
- docker主从复制原理与常用部署
一主一从:
用于主节点故障转移从节点,当主节点的“写”命令并发高且需要持久化,可以只在从节点开启AOF(主节点不需要),这样即保证了数据的安
全性,也避免持久化对主节点的影响
一主多从:
针对“读”较多的场景,“读”由多个从节点来分担,但节点越多,主节点同步到多节点的次数也越多,影响带宽,也加重主节点的稳定
树状主从:
一主多从的缺点(主节点推送次数多压力大)可用些方案解决,主节点只推送一次数据到从节点1,再由从节点2 推送到11,减轻主节点推送的压力。
复制原理
执行slave master port 后,
与主节点连接,同步主节点的数据,6380:>info replication:查看主从及同步信息
数据同步:
redis 2.8 版本以上使用psync 命令完成同步,过程分“全量”与“部分”复制
全量复制:一般用于初次复制场景(第一次建立SLAVE 后全量)
部分复制:网络出现问题,从节占再次连主时,主节点补发缺少的数据,每次数据增加同步
心跳: 主从有长连接心跳, 主节点默认每10S 向从节点发ping 命令,repl-ping-slave-period 控制发送频率
哨兵机制
- 使用docker部署哨兵
#前置条件,部署好redis主从复制
docker run --privileged=true --network=host --name redis6379 -v /var/lib/docker/volumes/redis/:/data -d redis:4.0.14 redis-server /data/redis6379.conf
docker run --privileged=true --network=host --name redis6380 -v /var/lib/docker/volumes/redis/:/data -d redis:4.0.14 redis-server /data/redis6380.conf
# > --network=host 表示容器IP和宿主机的IP相同, 解决当主节点出现故障时,由redis sentinel自动完成故障发现和转移分配成容器IP,造成的外部访问不通的问题
#配置sentinel.conf(3 个节点)
sentinel monitor mymaster 190.168.1.111 6379 2 #监听主节点6379 (IP 必须是主节点所在宿主机的IP)
sentinel auth-pass mymaster 12345678 #连接主节点时的密码
#启动redis哨兵
docker run --privileged=true -p 26379:26379 --name redis26379 -v /var/lib/docker/volumes/redis/sentinel/:/data -d redis:4.0.14 redis-sentinel /data/sentinel26379.conf
docker run --privileged=true -p 26380:26379 --name redis26380 -v /var/lib/docker/volumes/redis/sentinel/:/data -d redis:4.0.14 redis-sentinel /data/sentinel26380.conf
docker run --privileged=true -p 26381:26379 --name redis26381 -v /var/lib/docker/volumes/redis/sentinel/:/data -d redis:4.0.14 redis-sentinel /data/sentinel26381.conf
#启动哨兵后sentinel.conf配置文件会自动生成以下的配置
#Generated by CONFIG REWRITE
sentinel known-slave mymaster 190.168.1.111 6380
- spring boot集成redis哨兵模式
#在application.properties文件中进行以下配置即可
####redis的配置信息###
spring.redis.sentinel.master=mymaster
spring.redis.sentinel.nodes=192.168.227.134:26379,192.168.227.134:26380,192.168.227.134:26381
#采用哪个数据库
spring.redis.database=0
# 连接池最大连接数,默认8个,(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)(必须大于0,否则将会出现哨兵连接不上的问题)
spring.redis.timeout=30000
- 哨兵机制的原理
哨兵机制(sentinel)的高可用:
A,原理:当主节点出现故障时,由redis sentinel 自动完成故障发现和转移,并通知应用方,实现高可用性。
哨兵有三个定时监控任务完成对各节点的发现和监控:
任务1,每个哨兵节点每10 秒会向主节点和从节点发送info 命令获取最拓扑结构图,哨
兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当
有新的从节点加入时可以马上感知到
任务2,每个哨兵节点每隔2 秒会向redis 数据节点的指定频道上发送该哨兵节点对于主
节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵
节点的信息及对主节点的判断,其实就是通过消息publish 和subscribe 来完成的;
任务3,每隔1 秒每个哨兵会向主节点、从节点及其余哨兵节点发送一次ping 命令做一次心跳检测,
这个也是哨兵用来判断节点是否正常的重要依据
任务1-图例
任务2-图例
任务3-图例
- 部署建议
a,sentinel 节点应部署在多台物理机(线上环境)
b,至少三个且奇数个sentinel 节点
c,通过以上我们知道,3 个sentinel 可同时监控一个主节点或多个主节点
监听N 个主节点较多时,如果sentinel 出现异常,会对多个主节点有影响,同时还会造成sentinel 节点产生过多的网络连接,
一般线上建议还是, 3 个sentinel 监听一个主节点
- sentinel 哨兵的API
命令:redis-cli -p 26379 //进入哨兵的命令模式,使用redis-cli 进入
26379>sentinel masters 或sentinel master mymaster //查看redis 主节点相关信息
26379>sentinel slaves mymaster //查看从节点状态与相关信息
26379>sentinel sentinels mymaster //查sentinel 节点集合信息(不包括当前26379)
26379>sentinel failover mymaster //对主节点强制故障转移,没和其它节点协商
- 哨兵其它的配置
sentinel monitor mymaster 192.168.1.10 6379 2
#监控主节点的IP 地址端口,sentinel监控的master的名字叫做mymaster.
#2代表,当集群中有2 个sentinel认为master 死了时,才能真正认为该master已经不可用了
sentinel auth-pass mymaster 12345678 #sentinel 连主节点的密码
sentinel config-epoch mymaster 2 #故障转移时最多可以有2 从节点同时对新主节点进行数据同步
sentinel leader-epoch mymaster 2
sentinel failover-timeout mymasterA 180000 #故障转移超时时间180s,
#a,如果转移超时失败,下次转移时时间为之前的2 倍;
#b,从节点变主节点时,从节点执行slaveof no one 命令一直失败的话,当时间超过180S 时,则故障转移失败
#c,从节点复制新主节点时间超过180S 转移失败
sentinel down-after-milliseconds mymasterA 300000 #sentinel节点定期向主节点
#ping 命令,当超过了300S 时间后没有回复,可能就认定为此主节点出现故障了……
sentinel parallel-syncs mymasterA 1
#故障转移后,1 代表每个从节点按顺序排队一个一个复制主节点数据,
#如果为3,指3个从节点同时并发复制主节点数据,不会影响阻塞,但存在网络和IO 开销
- RedisSentinel 如何监控2 个redis 主节点呢?
#配置两个以下配置就行了
sentinel monitor mymasterA 192.168.1.20 6379 2
sentinel monitor mymasterB 192.168.1.21 6379 2
高可用集群
- 为什么要用集群
RedisCluster 是redis 的分布式解决方案,在3.0 版本后推出的方案,有效地解决了Redis 分布式的需求,当遇到单机内存、
并发等瓶颈时,可使用此方案来解决这些问题
- 集群的部署
1、 Redis集群至少需要3个节点,因为投票容错机制要求超过半数节点认为某个节点挂了该节点才是挂了,所以2个节点无法构成集群。
2、要保证集群的高可用,需要每个节点都有从节点,也就是备份节点,所以Redis集群至少需要6台服务器
- #修改配置
》分别修改6379、 6380、 6381、 6389、 6390、 6391配置文件
#以6379为例:
port 6379 //节点端口
cluster-enabled yes //开启集群模式
cluster-node-timeout 15000 //节点超时时间
cluster-config-file //nodes-6379.conf 集群内部配置文件
#其它节点的配置和这个一致,改端口即可 (从节点可以看需要 开启aof)
#场景并启动节点容器
docker run --privileged=true --network=host --name redis6379 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6379.conf
docker run --privileged=true --network=host --name redis6380 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6380.conf
docker run --privileged=true --network=host --name redis6381 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6381.conf
docker run --privileged=true --network=host --name redis6389 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6389.conf
docker run --privileged=true --network=host --name redis6390 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6390.conf
docker run --privileged=true --network=host --name redis6391 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6391.conf
#手动方式配置-部署
#1 进入任意一个节点 建立与其他节点之间的连接
redis-cli
6379>cluster meet 192.168.227.134 6380
6379>cluster meet 192.168.227.134 6381
6379>cluster meet 192.168.227.134 6389
6379>cluster meet 192.168.227.134 6390
6379>cluster meet 192.168.227.134 6391
#2 分配槽点
redis-cli -h 192.168.227.134 -p 6379 CLUSTER ADDSLOTS {0..5460}
redis-cli -h 192.168.227.134 -p 6380 CLUSTER ADDSLOTS {5461..10922}
redis-cli -h 192.168.227.134 -p 6381 CLUSTER ADDSLOTS {10923..16383}
#3 添加主从关系(给每个从节点指定它的主节点是哪个)
redis-cli -h 192.168.227.134 -p 6389 CLUSTER REPLICATE 789d4432d7a95ad017196237a560a38ea080bc7e
redis-cli -h 192.168.227.134 -p 6390 CLUSTER REPLICATE 84bcd87deeacb4bfb1ca63d3b5caefa6fa522478
redis-cli -h 192.168.227.134 -p 6391 CLUSTER REPLICATE 0137800a0902d741441ac8685f9a52ed174d5d1a
#查看节点
cluster nodes
#查看集群状态
cluster info
#自动方式配置 - 部署
redis的安装包中提供了自动配置槽点和主从关系的工具(src文件夹下的 redis-trib.rb )
但使用 redis-trib.rb 需要 ruby和gem 的支持,所以需要先进行 ruby 和 gem安装
#安装ruby 和 gem
yum install ruby rubygems -y
wget https://rubygems.org/downloads/redis-3.3.0.gem
gem install -l redis-3.2.2.gem
#执行redis-trib.rb
cd ./redis-4.0.14/src
./redis-trib.rb create --replicas 1 192.168.227.134:6379 192.168.227.134:6380 192.168.227.134:6381 192.168.227.134:6389 192.168.227.134:6390 192.168.227.134:6391
#执行redis-trib.rb过程中需要手动输入一次 yes
# 192.168.227.134:6379 192.168.227.134:6380 192.168.227.134:6381 为主节点
# 192.168.227.134:6389 192.168.227.134:6390 192.168.227.134:6391 分别为对应的从节点
- 集群健康检测
redis-trib.rb check 192.168.42.111:6379 (注:redis 先去注释掉requirepass,不然连不上)
- #如果出现以下错误说明 6379,6380,6381 的有部分槽位被打开了,分别进入这几个节点,执行 cluster setslot 5798 stable 命令即可
如:
6380:>cluster setslot 1180 stable
cluster setslot 2998 stable
cluster setslot 11212 stable
#健康检测正常图例 - 密码的设置
集群正常启动后,在每个redis.conf 里加上
masterauth “12345678”
requiredpass “12345678”
当主节点下线时,从节点会变成主节点,用户和密码是很有必要的,设置成一致
- 一主多从的集群部署
./redis-trib.rb create --replicas 2
192.168.42.111:6379 192.168.42.111:6380 192.168.42.111:6381
192.168.42.111:6479 192.168.42.111:6480 192.168.42.111:6481
192.168.42.111:6579 192.168.42.111:6580 192.168.42.111:6581
- redis-trib.rb具有的功能
1、create:创建集群
2、check:检查集群
3、info:查看集群信息
4、fix:修复集群
5、reshard:在线迁移slot
6、rebalance:平衡集群节点slot数量
7、add-node:将新节点加入集群
8、del-node:从集群中删除节点
9、set-timeout:设置集群节点间心跳连接的超时时间
10、call:在集群全部节点上执行命令
11、import:将外部redis数据导入集群
- 客户端连接集群
#redis-cli命令行后面加 -c
redis-cli -h 192.168.42.111 -p 6379 -c
- spirng boot 集成redis集群
#》在application.properties配置文件中进行以下配置即可
spring.redis.cluster.nodes=192.168.227.134:6379,192.168.227.134:6380,192.168.227.134:6381,192.168.227.134:6389,192.168.227.134:6390,192.168.227.134:6391
#采用哪个数据库
spring.redis.database=0
# 连接池最大连接数,默认8个,(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
- 分布式数据库概念
1,分布式数据库把整个数据按分区规则映射到多个节点,即把数据划分到多个节点上,每个节点负责整体数据的一个子集
2,比如我们库有900 条用户数据,有3 个redis 节点,将900 条分成3 份,分别存入到3 个redis 节点分区规则:常见的分区规则哈希分区和顺序分区,redis 集群使用了哈希分区
rediscluster 采用了哈希分区的“虚拟槽分区”方式
哈希分区方式 : (节点取余、一致性哈希分区 、虚拟槽分区)虚拟槽分区(槽:slot) :
RedisCluster 采用此分区,所有的键根据哈希函数(CRC16[key]&16383)映射到0-16383 槽内,共16384 个槽位,每个节点维护部分槽及槽所映射的键值数据哈希函数: Hash()=CRC16[key]&16383 按位与
redis 用虚拟槽分区原因:1,解耦数据与节点关系,节点自身维护槽映射关系,分布式存储redisCluster 的缺陷:
a,键的批量操作支持有限,比如mset, mget,如果多个键映射在不同的槽,就不支持了
b,键事务支持有限,当多个key 分布在不同节点时无法使用事务,同一节点是支持事务
c,键是数据分区的最小粒度,不能将一个很大的键值对映射到不同的节点
d,不支持多数据库,只有0,select 0
e,复制结构只支持单层结构,不支持树型结构。
- 集群扩容
#1.同目录下新增6382.conf、6392.conf 并启动两个新redis 节点
docker run --privileged=true --network=host --name redis6382 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6382.conf
docker run --privileged=true --network=host --name redis6392 -v /var/lib/docker/volumes/clusterconf/:/data -d redis:4.0.14 redis-server /data/6392.conf
#2.新增主节点 (6379 是原存在的主节点, 6382 是新的主节点)
./redis-trib.rb add-node 192.168.227.134:6382 192.168.227.134:6379
#3.添加从节点
./redis-trib.rb add-node --slave --master-id 9681f59e8567ecb5ddcf3f9d110aa179fd7feda9 192.168.227.134:6392 192.168.227.134:6379
#--slave,表示添加的是从节点
#--master-id 9681f59e8567ecb5ddcf3f9d110aa179fd7feda9表示主节点6382的master_id (可以通过检测命令查看获得 ./redis-trib.rb check 192.168.227.134:6379)
#192.168.227.134:6392,新从节点
#192.168.227.134:6379 集群原存在的旧节点
#4.为新主节点重新分配solt(执行过程中需要手动输入一些参数)
./redis-trib.rb reshard 192.168.227.134:6382
# How many slots do you want to move (from 1 to 16384)? 1000 //设置slot数1000
# What is the receiving node ID? 9681f59e8567ecb5ddcf3f9d110aa179fd7feda9 //新节点node id
# Source node #1:all //表示全部节点重新洗牌
#新增完毕!
- 集群减缩节点
#删除从节点(或 没有槽点、数据的主节点)-(有两个参数ip:port 和 节点的id)
./redis-trib.rb del-node 192.168.227.134:6392 3816fcc5bcb51c3f41f6963de626b60044f36f1c
#删除主节点
./redis-trib.rb reshard 192.168.227.134:6382
./redis-trib.rb del-node 192.168.227.134:6382 9681f59e8567ecb5ddcf3f9d110aa179fd7feda9
#执行./redis-trib.rb reshard 192.168.227.134:6382 时需要手动输入一些参数
# How many slots do you want to move (from 1 to 16384)? 1000 //移动多个槽点(输入6382该节点的全部槽点数 - 可以通过 ./redis-trib.rb check 192.168.227.134:6379 检测命令查询获得)
# What is the receiving node ID? 9681f59e8567ecb5ddcf3f9d110aa179fd7feda9 //移动槽点到那个节点服务上
# Source node #1:3816fcc5bcb51c3f41f6963de626b60044f36f1c //源节点ID(从那个节点上拿需要移动到目标节点的槽点 - 即:需要删除的节点ID)
# Source node #2:done //只删除一个,输入done完成
流程说明:
A,确定下线节点是否存在槽slot,如果有,需要先把槽迁移到其他节点,保证整个
集群槽节点映射的完整性;
B,当下线的节点没有槽或本身是从节点时,就可以通知集群内其它节点(或者叫忘
记节点),当下线节点被忘记后正常关闭。
- 请求路由重定向
我们知道,在redis 集群模式下,redis 接收的任何键相关命令首先是计算这个键CRC值,通过CRC 找到对应的槽位,再根据槽找到所对应的redis 节点,如果该节点是本身,则直接处理键命令;如果不是,则回复键重定向到其它节点,这个过程叫做MOVED 重定向
- 故障转移
redis 集群实现了高可用,当集群内少量节点出现故障时,通过故障转移可以保证集群正常对外提供服务。
当集群里某个节点出现了问题,redis 集群内的节点通过ping pong 消息发现节点是否健康,是否有故障,其实主要环节也包括了主观下线和客观下线;
主观下线:指某个节点认为另一个节点不可用,即下线状态,当然这个状态不是最终的故障判定,只能代表这个节点自身的意见,也有可能存在误判;
下线流程:
A,节点a 发送ping 消息给节点b ,如果通信正常将接收到pong 消息,节点a 更新最近一次与节点b 的通信时间;
B,如果节点a 与节点b 通信出现问题则断开连接,下次会进行重连,如果一直通信失败,则它们的最后通信时间将无法更新;
C,节点a 内的定时任务检测到与节点b 最后通信时间超过cluster_note-timeout 时,更新本地对节点b 的状态为主观下线(pfail)
客观下线:指真正的下线,集群内多个节点都认为该节点不可用,达成共识,将它下线.如果下线的节点为主节点,还要对它进行故障转移;
A,假如节点a 标记节点b 为主观下线,一段时间后节点a 通过消息把节点b 的状态发到其它节点,当节点c 接受到消息并解析出消息体时,会发现节点b 的pfail 状态时,会触发客观下线流程。
B,当下线为主节点时,此时redis 集群为统计持有槽的主节点投票数是否达到一半,当下线报告统计数大于一半时,被标记为客观下线状态。
- 故障恢复
故障主节点下线后,如果下线节点的是主节点,则需要在它的从节点中选一个替换它,保证集群的高可用;转移过程如下:
1、 资格检查:检查该从节点是否有资格替换故障主节点,如果此从节点与主节点断开过通信,那么当前从节点不具体故障转移资格
2,准备选举时间:当从节点符合故障转移资格后,更新触发故障选举时间,只有到达该时间后才能执行后续流程;
3,发起选举:当到达故障选举时间时,进行选举;
4,选举投票:只有持有槽的主节点才有票,会处理故障选举消息,投票过程其实是一个领导者选举(选举从节点为领导者)的过程,每个主节点只能投一张票给从节点,当从节点收集到足够的选票(大于N/2+1)后,触发替换主节点操作,撤销原故障主节点的槽,委派给自己,并广播自己的委派消息,通知集群内所有节点。