redis持久化主从与哨兵结构(学习总结)_bingtanghulu
目录
5.5 Redis客户端命令对应的RedisTemplate中的方法列表
1. RDB,AOF及混合持久化
如果有不知道怎么配置redis客户端的请看另外一篇博客
1.1 RDB持久化机制
redis默认打开RDB功能,RDB是一种快照形式的redis持久化机制,他的数据都保存在dump.rdb文件中
1.1.1 配置rdb策略
1. 默认配置策略:格式:save 秒数 保存键数,整体连起来就是在多少秒内写多少键值就会把数据保存到dump.rdb文件中。默认配置如下
2. 手动保存
进入redis客户端输入save或者bgsave命令可手动保存数据到dumb.rdb文件,

效果如下:
1.2 aof
aof持久化:默认不开启,这种机制会将每条修改命令不包括读的命令写入appendonly.aof文件中。
1.2.1 开启AOF
修改appendonly yes 参数打开AOP功能
- vi redis.conf #修改配置文件参数appendonly yes
- ps -ef |grep redis #查看redis进程
- kill -9 pid #杀掉进程
- src/redis-server redis.conf # 重启服务端
- src/redis-cli #开启客户端
- set test 666 #写入命令
-
在redis安装目录下能看到appendonly.aof文件代表aop功能开启成功!dump.rdb文件是RDB持久化的文件
1.2.2 配置aof策略
appendfsync always :每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全。appendfsync everysec :每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。appendfsync no :从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择。
推荐appendfsync everysec-每秒 fsync 一次,可以兼顾速度和安全性。
1.3 aof重写
aof定期将一些无用且重复的命令生成一条命令。
1.3.1 配置重写策略
auto ‐ aof ‐ rewrite ‐ min ‐ size 64 mb //aof 文件至少要达到 64M 才会自动重写,文件太小恢复速度本来就很快,重写的意义不大auto ‐ aof ‐ rewrite ‐ percentage 100 //aof 文件自上一次重写后文件大小增长了 100%% 则再次触发重写
1.3.2 手动重写aof
进入redis客户端使用bgrewriteaof命令重写
1.4 RDB 和 AOF ,我应该用哪一个?

1.5 混合持久化
混合持久化:重写这一刻之前的内存做内存快照,并将rdb快照和增量aof修改命令放到一起存入aof文件,重写一个新的aof文件,重写完成后覆盖原来的appendonly.aof文件。redis重启以后加载dump.rdb文件,并加载增量aof文件完成重写。结合了aof数据安全和rdb效率的优势。
1.5.1 开启混合持久化
aof‐use‐rdb‐preamble yes #开启混合持久化
1.6 Redis数据备份策略
策略如下,脚本需要自己写,如果有写过的linux大佬,麻烦给个链接,谢谢!
1. 写crontab定时调度脚本,每小时都copy一份rdb或aof的备份到一个目录中去,仅仅保留最近48小时的备份2. 每天都保留一份当日的数据备份到一个目录中去,可以保留最近1个月的备份3. 每次copy备份的时候,都把太旧的备份给删了4. 每天晚上将当前机器上的备份复制一份到其他机器上,以防机器损坏
2. redis主从架构原理
2.1 redis主从架构图
当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送给多个并发连接的slave。架构图如下:
2.2 搭建redis主从架构
1 、复制一份 redis . conf 文件mkdir config #创建文件夹cp redis.conf /usr/local/redis-5.0.3/config/redis-6380.conf #复制一份文件2 、将相关配置修改为如下值:port 6380pidfile / var / run / redis_6380 . pid # 把 pid 进程号写入 pidfile 配置的文件logfile "6380.log" 7 dir / usr / local / redis ‐ 5.0.3 / data / 6380 # 指定数据存放目录# 需要注释掉 bind# bind 127.0.0.1 ( bind 绑定的是自己机器网卡的 ip ,如果有多块网卡可以配多个 ip ,代表允许客户端通过机器的哪些网卡 ip 去访问,内网一般可以不配置 bind ,注释掉即可)3 、配置主从复制replicaof 192.168.1.7 6379 # 从本机 6379 的 redis 实例复制数据, Redis 5.0 之前使用 slaveofreplica ‐ read ‐ only yes # 配置从节点只读4 、启动从节点src/redis ‐ server ./config/redis-6380 . conf5 、连接从节点src/redis ‐ cli ‐ p 63806 、测试在 6379 实例上写数据, 6380 实例是否能及时同步新修改数据7 、可以自己再配置一个 6381 的从节点
如果能显示如下所示代表链接成功:
2.2.1 主从复制全量流程图
2.2.2 部分复制流程图
2.2.3 平常redis主从架构
主从复制风暴:为了缓解主节点往多个从节点复制数据的压力,可以做如下架构:
4.redis管道与lua脚本
4.1 管道
客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完后再一次性读取服务的响 应,这样可以极大的降低多条命令执行的网络传输开销,管道执行多条命令的网络开销实际上只相当于一 次命令执行的网络开销。需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓
存起所有命令的处理结果
。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。 pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达“所有command都一起成功”的语义,管道中前面命令失败,后面命令不会有影响,继续执行。
4.2 lua脚本
Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。使用脚本的好处如下:
1、
减少网络开销
:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器
上完成。使用脚本,减少了网络往返时延。
这点跟管道类似
。
2、
原子操作
:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。
管道不是原子的,不过
redis的批量操作命令(类似mset)是原子的。
3、
替代redis的事务功能
:redis自带的事务功能很鸡肋,而redis的lua脚本几乎实现了常规的事务功能,
官方推荐如果要使用redis的事务功能可以用redis lua替代。
4.2.1 redis使用lua脚本
从Redis2.6.0版本开始,通过内置的Lua解释器,可以使用EVAL命令对Lua脚本进行求值。EVAL命令的格式如下:
EVAL script numkeys key [key ...] arg [arg ...]
EVAL script numkeys key [key ...] arg [arg ...]
4.3 jedis操作
4.3.1 引入jedis依赖
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
4.3.2 模拟管道报错和操作字符串
- package com.tuling.jedis;
-
- import redis.clients.jedis.Jedis;
- import redis.clients.jedis.JedisPool;
- import redis.clients.jedis.JedisPoolConfig;
- import redis.clients.jedis.Pipeline;
-
- import java.io.IOException;
- import java.util.Arrays;
- import java.util.List;
-
- public class JedisSingleTest {
- public static void main(String[] args) throws IOException {
-
- JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
- jedisPoolConfig.setMaxTotal(20);
- jedisPoolConfig.setMaxIdle(10);
- jedisPoolConfig.setMinIdle(5);
-
- // timeout,这里既是连接超时又是读写超时,从Jedis 2.8开始有区分connectionTimeout和soTimeout的构造函数
- JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.1.7", 6379, 3000, null);
-
- Jedis jedis = null;
- try {
- //从redis连接池里拿出一个连接执行命令
- jedis = jedisPool.getResource();
-
- //******* jedis普通操作示例 ********
- /*System.out.println(jedis.set("single1", "genghongbo"));
- System.out.println(jedis.get("single1"));*/
-
- //******* 管道示例 ********
- //管道的命令执行方式:cat redis.txt | redis-cli -h 127.0.0.1 -a password - p 6379 --pipe
- Pipeline pl = jedis.pipelined();
- for (int i = 0; i < 10; i++) {
- pl.incr("pipelineKey");
- pl.set("test" + i, "zhuge");
- //模拟管道报错
- pl.setbit("test", -1, true);
- }
- List<Object> results = pl.syncAndReturnAll();
- System.out.println(results);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
- if (jedis != null)
- jedis.close();
- }
- }
- }
4.3.3 模拟lua脚本
- //******* lua脚本示例 ********
- //模拟一个商品减库存的原子操作
- //lua脚本命令执行方式:redis-cli --eval /tmp/test.lua , 10
- jedis.set("product_stock_10016", "15"); //初始化商品10016的库存
- String script = " local count = redis.call('get', KEYS[1]) " +
- " local a = tonumber(count) " +
- " local b = tonumber(ARGV[1]) " +
- " if a >= b then " +
- " redis.call('set', KEYS[1], a-b) " +
- //模拟语法报错回滚操作
- " bb == 0 " +
- " return 1 " +
- " end " +
- " return 0 ";
- Object obj = jedis.eval(script, Arrays.asList("product_stock_10016"), Arrays.asList("10"));
- System.out.println(obj);
5. redis哨兵高可用架构
5.1 redis哨兵架构图
5.2 redis哨兵搭建
1 、复制一份 sentinel . conf 文件cp sentinel . conf sentinel ‐ 26379. conf2 、将相关配置修改为如下值:port 26379daemonize yespidfile "/var/run/redis‐sentinel‐26379.pid"logfile "26379.log"dir "/usr/local/redis‐5.0.3/data"# sentinel monitor < master ‐ redis ‐ name > < master ‐ redis ‐ ip > < master ‐ redis ‐ port > < quorum ># quorum 是一个数字,指明当有多少个 sentinel 认为一个 master 失效时 ( 值一般为: sentinel 总数 / 2 +1 ) , master 才算真正失效 12 sentinel monitor mymaster 192.168.0.60 6379 2 # mymaster 这个名字随便取,客户端访问时会用到3 、启动 sentinel 哨兵实例src / redis ‐ sentinel sentinel ‐ 26379. conf4 、查看 sentinel 的 info 信息src / redis ‐ cli ‐ p 26379127.0.0.1 : 26379 > info可以看到 Sentinel 的 info 里已经识别出了 redis 的主从5 、可以自己再配置两个 sentinel ,端口 26380 和 26381 ,注意上述配置文件里的对应数字都要修改
如下图所示即可。
5.2.1 查看哨兵集群元信息
进入sential-26379.conf文件查找
哨兵挂掉后自动从从节点选举出主节点信息更改端口号为新的主节点端口号
sentinel monitor mymaster 192.168.0.60 6380 2
5.3 jedis连接哨兵
5.3.1 引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
5.3.2 springboot项目核心配置
server :port : 8080spring :redis :database : 0timeout : 3000sentinel : # 哨兵模式master : mymaster # 主服务器所在集群名称nodes : 192.168.0.60 : 26379 , 192.168.0.60 : 26380 , 192.168.0.60 : 26381lettuce : 12 pool :max ‐ idle : 50min ‐ idle : 10max ‐ active : 100max ‐ wait : 1000
5.3.3 jedis连接代码
- package com.tuling.jedis;
-
- import redis.clients.jedis.HostAndPort;
- import redis.clients.jedis.Jedis;
- import redis.clients.jedis.JedisPoolConfig;
- import redis.clients.jedis.JedisSentinelPool;
-
- import java.io.IOException;
- import java.util.HashSet;
- import java.util.Set;
-
- /**
- * 访问redis哨兵集群
- *
- */
- public class JedisSentinelTest {
- public static void main(String[] args) throws IOException {
-
- JedisPoolConfig config = new JedisPoolConfig();
- config.setMaxTotal(20);
- config.setMaxIdle(10);
- config.setMinIdle(5);
-
- String masterName = "mymaster";
- Set<String> sentinels = new HashSet<String>();
- sentinels.add(new HostAndPort("192.168.1.7",26379).toString());
- // sentinels.add(new HostAndPort("192.168.1.7",26380).toString());
- // sentinels.add(new HostAndPort("192.168.1.7",26381).toString());
- //JedisSentinelPool其实本质跟JedisPool类似,都是与redis主节点建立的连接池
- //JedisSentinelPool并不是说与sentinel建立的连接池,而是通过sentinel发现redis主节点并与其建立连接
- JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, config, 3000, null);
- Jedis jedis = null;
- try {
- jedis = jedisSentinelPool.getResource();
- System.out.println(jedis.set("sentinel666", "test"));
- System.out.println(jedis.get("sentinel666"));
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。
- if (jedis != null)
- jedis.close();
- }
- }
- }
5.4 StringRedisTemplate与RedisTemplate详解
private ValueOperations < K , V > valueOps ;private HashOperations < K , V > hashOps ;private ListOperations < K , V > listOps ;private SetOperations < K , V > setOps ; 5 private ZSetOperations < K , V > zSetOps ;
RedisTemplate中定义了对5种数据结构操作
redisTemplate . opsForValue (); // 操作字符串redisTemplate . opsForHash (); // 操作 hashredisTemplate . opsForList (); // 操作 listredisTemplate . opsForSet (); // 操作 setredisTemplate . opsForZSet (); // 操作有序 set
StringRedisTemplate继承自RedisTemplate,也一样拥有上面这些操作。
5.5 Redis客户端命令对应的RedisTemplate中的方法列表