一:持久化
1.RSB快照
1.
在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中。
可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次 数据集。 比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次 数据集:
# save 60 1000
关闭RDB只需要将所有的save保存策略注释掉即可
2.save与bgsave生成文件
还可以手动执行命令生成RDB快照,进入redis客户端执行命令save或bgsave可以生成dump.rdb文件, 每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。
3.写时复制机制
Redis 借助操作系统提供的写时复制技术(Copy-On-Write, COW),在生成快照的同时,依然可以正常 处理写命令。
简单来说,bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。 bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件。此时,如果主线程对这些 数据也都是读操作,那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据,那 么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文 件,而在这个过程中,主线程仍然可以直接修改原来的数据。
4.save与bgsave对比
2. aof
快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失 最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方 式: AOF 持久化,将修改的每一条指令记录进文件appendonly.aof中(先写入os cache,每隔一段时间 fsync到磁盘)
每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文 件的末尾。 这样的话, 当 Redis 重新启动时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目 的。
配置:
测试
重启后,写入:
查看appendonly.aof
这是一种resp协议格式数据,星号后面的数字代表命令有多少个参数,$号后面的数字代表这个参数有几 个字符.
关于过期时间,则使用时间戳的方式
set program true ex 10000
AOF重写
AOF文件里可能有太多没用指令,所以AOF会定期根据内存的最新数据生成aof文件
可以通过下面的配置进行修改:
# auto‐aof‐rewrite‐min‐size 64mb //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大 # auto‐aof‐rewrite‐percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写
当然AOF还可以手动重写,进入redis客户端执行命令bgrewriteaof重写AOF 注意,AOF重写redis会fork出一个子进程去做(与bgsave命令类似),不会对redis正常命令处理有太多 影响
3.rdb与aof
生产环境可以都启用,redis启动时如果既有rdb文件又有aof文件则优先选择aof文件恢复数据,因为aof 一般来说数据更全一点。
4.混合持久化
重启 Redis 时,我们很少使用 RDB来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重 放,但是重放 AOF 日志性能相对 RDB来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很 长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。
如果开启了混合持久化,AOF在重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将 重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一 起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改 名,覆盖原有的AOF文件,完成新旧两个AOF文件的替换。 于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,因此重启效率大幅得到提升。
结构:
配置:
首先保证aof开启,然后
# aof‐use‐rdb‐preamble yes
测试
二:主从架构
1.架构
2.搭建
复制redis.conf成redis_6380.conf
cp redis.conf redis_6380.conf
修改文件
port 6380 pidfile /var/run/redis_6380.pid 日志 logfile "6380.log" 持久化 dbfilename dump_6380.rdb 配置主从关系 replicaof 192.168.19.132 6379 replica-read-only yes
启动
src/redis-server conf/redis_6380.conf
测试:
查询数据是否同步过来,6379修改数据,6380是否同步过来
3.主从复制原理
新的链接:
如果你为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个PSYNC 命令给master请求复制数据。 master收到PSYNC命令后,会在后台进行数据持久化通过bgsave生成最新的rdb快照文件,持久化期 间,master会继续接收客户端的请求,它会把这些可能修改数据集的请求缓存在内存中。当持久化进行完 毕以后,master会把这份rdb文件数据集发送给slave,slave会把接收到的数据进行持久化生成rdb,然后 再加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。 当master与slave之间的连接由于某些原因而断开时,slave能够自动重连Master,如果master收到了多 个slave并发连接请求,它只会进行一次持久化,而不是一个连接一次,然后再把这一份持久化的数据发送 给多个并发连接的slave。
断点续传:
当master和slave断开重连后,一般都会对整份数据进行复制。但从redis2.8版本开始,redis改用可以支 持部分数据复制的命令PSYNC去master同步数据,slave与master能够在网络连接断开重连后只进行部分 数据复制(断点续传)。 master会在其内存中创建一个复制数据用的缓存队列,缓存最近一段时间的数据,master和它所有的 slave都维护了复制的数据下标offset和master的进程id,因此,当网络连接断开后,slave会请求master 继续进行未完成的复制,从所记录的数据下标开始。如果master进程id变化了,或者从节点数据下标 offset太旧,已经不在master的缓存队列里了,那么将会进行一次全量数据的复制。
4.java程序-Jedis连接【单机与主从的代码是一致的】
pom
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
程序:
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; /** * 访问redis单机 */ 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.19.132", 6379, 3000, null); Jedis jedis = null; try { //从redis连接池里拿出一个连接执行命令 jedis = jedisPool.getResource(); //******* jedis普通操作示例 ******** System.out.println(jedis.set("single1", "caojun")); 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("zhuge" + i, "zhuge"); //模拟管道报错 pl.setbit("zhuge", -1, true); } List<Object> results = pl.syncAndReturnAll(); System.out.println(results); //******* 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) " + " return 1 " + " end " + " return 0 "; ; Object obj = jedis.eval(script, Arrays.asList("product_stock_10016"), Arrays.asList("10")); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); } finally { //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。 if (jedis != null) jedis.close(); } } }
三:哨兵架构
1.架构
2.配置
先搭建主从:
搭建哨兵
拷贝一份conf cp sentinel.conf sentinel_26381.conf port 26379 daemonize yes pidfile /var/run/redis-sentinel_26379.pid logfile "26379.log" dir /opt/software/redis-5.0.3/data sentinel monitor mymaster 192.168.19.132 6379 2
启动
src/redis-sentinel conf/sentinel_26379.conf
进入客户端,检查info命令
src/redis-cli -p 26379
查看
sentinel集群都启动完毕后,会将哨兵集群的元数据信息写入所有sentinel的配置文件里去(追加在文件的 最下面),我们查看下如下配置文件sentinel-26379.conf,如下所示:
more conf/sentinel_26379.conf
其中,6381,6380都代表redis主节点的从节点信息
3.java代码-jedis
package com.jun.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.19.132", 26379).toString()); sentinels.add(new HostAndPort("192.168.19.132", 26380).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", "meighui")); System.out.println(jedis.get("sentinel666")); } catch (Exception e) { e.printStackTrace(); } finally { //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。 if (jedis != null) jedis.close(); } } }
3.springboot 的java代码
3.1.pom
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
3.2.代码
package com.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class IndexController { private static final Logger logger = LoggerFactory.getLogger(IndexController.class); @Autowired private StringRedisTemplate stringRedisTemplate; /** * 测试节点挂了哨兵重新选举新的master节点,客户端是否能动态感知到 * * @throws InterruptedException */ @RequestMapping("/test_sentinel") public void testSentinel() throws InterruptedException { int i = 1; while (true){ try { stringRedisTemplate.opsForValue().set("caojun-"+i, i+""); //jedis.set(key,value); System.out.println("设置key:"+ "caojun-" + i); i++; Thread.sleep(1000); }catch (Exception e){ logger.error("错误:", e); } } } }
3.3.配置文件
server: port: 8080 spring: redis: database: 0 timeout: 3000 sentinel: #哨兵模式 master: mymaster #主服务器所在集群名称 nodes: 192.168.19.132:26379,192.168.19.132:26380,192.168.19.132:26381 # cluster: # nodes: 192.168.50.61:8001,192.168.50.62:8002,192.168.50.63:8003,192.168.50.61:8004,192.168.50.62:8005,192.168.50.63:8006 lettuce: pool: max-idle: 50 min-idle: 10 max-active: 100 max-wait: 1000
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)