reids持久化

持久化分为rdb快照 aof 混合模式

rdb快照
save 60 1000 // 开启方式 这样写表示60秒内 有1000次修改添加操作记录一次快照
rdb快照会把当前redis内存中所有数据全部写到磁盘上 用二进制记录 它的优点就是恢复速度快 缺点持久化数据会很慢 比如redis内存设置的8g那么它会把这
8g都写到磁盘文件中 这样就会很慢 把所有的save都注释或者删了可以关闭rdb快照
rdb有两种模式 save跟bgsave(写时复制(COW)机制)
save是同步的所以在做持久化时 redis的命令都要等持久化完成之后才能执行 否则一直是阻塞状态

bgsave是异步 如果用这个方式 redis在做持久化时会给我们创建一个redis的子进程 这个子进程是主进程的一个备份 利用子进程进行持久化 缺点耗费内存

客户端执行save或者bgsave 手动更新快照文件

aof

appendonly yes // 开启aof

aof三个参数
appendfsync always:每次有新命令追加到 AOF 文件时就执行一次 fsync ,非常慢,也非常安全。
appendfsync everysec:每秒 fsync 一次,足够快,并且在故障时只会丢失 1 秒钟的数据。
appendfsync no:从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择。

开启aof之后 我们在执行修改命令时如果设置的everysec这个参数 它会把这1秒我们执行过的指令存到它的持久化文件中 最多会丢失1秒的数据 缺点恢复数据慢

aof重写

incr readcount当我们多次执行这个命令 以aof的机制会把这多次执行的命令存到持久化文件中造成一些垃圾指令 redis给我做了优化

设置这两个参数可以在aof持久化文件达到64mb时 会把持久化文件中那些垃圾指令给更改 比如incr readcount执行了6次重写的时候会优化成 set readcount 6
auto-aof-rewrite-min-size 64mb //aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
auto-aof-rewrite-percentage 100 //aof文件自上一次重写后文件大小增长了100%则再次触发重写

bgrewriteaof // 在客户端执行这个指令可以手动重写

redis4.0新特性 混合持久化

使用混合持久化必须要开启aof

aof-use-rdb-preamble yes // 开启aof之后在配置文件设置这个值

开启了之后当aof做重写时会把一条一条的指令 重写成二进制形式跟rdb持久化的文件一样 当有了新的指令会在文件后面追加上这时追加的还是指令
触发重写就是rdb不触发重写就是aof

redis主从架构后面有架构图

1、复制一份redis.conf文件

2、将相关配置修改为如下值:
port 6380
pidfile /var/run/redis_6380.pid # 把pid进程号写入pidfile配置的文件
logfile “6380.log”
dir /usr/local/redis-5.0.3/data/6380 # 指定数据存放目录
需要注释掉bind
bind 127.0.0.1(bind绑定的是自己机器网卡的ip,如果有多块网卡可以配多个ip,代表允许客户端通过机器的哪些网卡ip去访问,内网一般可以不配置bind,注释掉即可)

3、配置主从复制
replicaof 192.168.0.60 6379 # 从本机6379的redis实例复制数据,Redis 5.0之前使用slaveof
replica-read-only yes # 配置从节点只读

4、启动从节点
redis-server redis.conf

5、连接从节点
redis-cli -p 6380

6、测试在6379实例上写数据,6380实例是否能及时同步新修改数据

7、可以自己再配置一个6381的从节点

redis主从工作原理

全量复制
当从节点第一次启动时 会建立长连接发送PSYNC命令给主节点 请求复制数据 当主节点接收到PSYNC命令时会创建当前内存的一个快照文件(rdb那种的快照文件)然后把这个快照文件发送给从节点 然后从节点收到快照文件清空当前所有数据然后根据快照恢复数据 如果当发送快照文件时又有了新redis命令 它会把这些新命令放到一个缓存中(repl buffer)然后把缓存中一条一条的命令发送给从节点 发送完就利用长连接有新redis命令有给从节点发一个新redis命令

部分复制或者叫断点续传
如果从节点宕机了 主节点会保存一个近期的指令缓存(repl backlog buffer)跟队列一样执行一个redis命令就保存起来 如果满了就把最先存储的给删除然后存储 当从节点宕机重启之后会重新连接 然后给主节点发送一个偏移量就是用来记录最后一个读取的数据 拿着这个偏移量去缓存中找 找到了就把它后面的数据同步到从节点 如果没找到 就用全量复制

repl backlog buffer // 通过设置redis.conf文件的repl-backlog-size 这个参数默认1mb

主从复制风暴
就是一个主节点有10个从节点 当10个从节点全量复制的时候会让主节点因为压力太大而疯掉 解决办法就是
一个主节点有两个从节点从节点又有两个从节点一次类推

redis依赖

redis.clients jedis 2.9.0

管道(pipeline)

减少网络连接次数 一次性发送多条redis命令

java代码

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 results = pl.syncAndReturnAll();
System.out.println(results);

通道是没有原子性的多条redis命令里面有执行失败的它是不给回滚的 解决这个办法可以使用redis lua脚本
redis lua脚本
替代redis事物、减少网络开销、有原子性

语法格式
EVAL script numkeys key [key …] arg [arg …] 
eval “return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}” 2 key1 key2 first second

java版
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);

redis哨兵架构后面有架构图

如果没有哨兵架构 当我们设置的redis主从架构 主节点宕机 那么我们需要手动的重新配置主节点 当有了哨兵架构 我们的客户端连接哨兵然后哨兵给我们连接到主节点 当主节点宕机 哨兵会给我们重新选取主节点

1、复制一份sentinel.conf文件
cp sentinel.conf sentinel-26379.conf

2、将相关配置修改为如下值:
port 26379
daemonize yes
pidfile “/var/run/redis-sentinel-26379.pid”
logfile “26379.log”
dir “/usr/local/redis-5.0.3/data”
sentinel monitor
quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效
sentinel monitor mymaster 192.168.0.60 6379 2 # mymaster这个名字随便取,客户端访问时会用到 2就表示当两个哨兵认为主节废了 就重新选取主节点 可能会出现脑裂问题

3、启动sentinel哨兵实例
src/redis-sentinel sentinel-26379.conf

4、查看sentinel的info信息
src/redis-cli -p 26379
127.0.0.1:26379>info
可以看到Sentinel的info里已经识别出了redis的主从

5、可以自己再配置两个sentinel,端口26380和26381,注意上述配置文件里的对应数字都要修改

springboot整合redis对应关系

StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
StringRedisTemplate的string序列号策略就是 set a 1 保存的时候就是a

RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。

RedisTemplate的JDK序列号此类就是 当我们执行set a 1 使用jdk序列号策略就会把a变成jdk序列的之后的样子保存不是用a保存 读取的时候会反序列化

左边是jedis依赖api 右边是springboot整合redis的依赖api

Redis               		RedisTemplate rt

String结构

set key value        		rt.opsForValue().set("key","value")
get key             		rt.opsForValue().get("key")
del key                 	rt.delete("key")
strlen key                      rt.opsForValue().size("key")
getset key value                rt.opsForValue().getAndSet("key","value")
getrange key start end          rt.opsForValue().get("key",start,end)
append key value      		rt.opsForValue().append("key","value")

Hash结构

hmset key field1 value1 field2 value2...          rt.opsForHash().putAll("key",map) //map是一个集合对象
hset key field value 				  rt.opsForHash().put("key","field","value")
hexists key field				  rt.opsForHash().hasKey("key","field")
hgetall key					  rt.opsForHash().entries("key")  //返回Map对象
hvals key					  rt.opsForHash().values("key") //返回List对象
hkeys key					  rt.opsForHash().keys("key") //返回List对象
hmget key field1 field2... 			  rt.opsForHash().multiGet("key",keyList)
hsetnx key field value				  rt.opsForHash().putIfAbsent("key","field","value"
hdel key field1 field2				  rt.opsForHash().delete("key","field1","field2")
hget key field					  rt.opsForHash().get("key","field")

List结构

lpush list node1 node2 node3...           	  rt.opsForList().leftPush("list","node") 
										  	  rt.opsForList().leftPushAll("list",list) //list是集合对象


rpush list node1 node2 node3...			  rt.opsForList().rightPush("list","node") 
										  rt.opsForList().rightPushAll("list",list) //list是集合对象

lindex key index				  rt.opsForList().index("list", index)

llen key					  rt.opsForList().size("key")

lpop key					  rt.opsForList().leftPop("key")

lpushx list node				  rt.opsForList().leftPushIfPresent("list","node")

rpushx list node				  rt.opsForList().rightPushIfPresent("list","node")

lrange list start end				  rt.opsForList().range("list",start,end)

lrem list count value				  rt.opsForList().remove("list",count,"value")

lset key index value				  rt.opsForList().set("list",index,"value")

Set结构

sadd key member1 member2...			  rt.boundSetOps("key").add("member1","member2",...)
					  rt.opsForSet().add("key", set) //set是一个集合对象


scard key					  rt.opsForSet().size("key")

sidff key1 key2					  rt.opsForSet().difference("key1","key2") //返回一个集合对象

sinter key1 key2     				  rt.opsForSet().intersect("key1","key2")//同上

sunion key1 key2				  rt.opsForSet().union("key1","key2")//同上

sdiffstore des key1 key2			  rt.opsForSet().differenceAndStore("key1","key2","des")

sinter des key1 key2			   	  rt.opsForSet().intersectAndStore("key1","key2","des")

sunionstore des key1 key2			  rt.opsForSet().unionAndStore("key1","key2","des")

sismember key member				  rt.opsForSet().isMember("key","member")

smembers key					  rt.opsForSet().members("key")

spop key					  rt.opsForSet().pop("key")

srandmember key count				  rt.opsForSet().randomMember("key",count)

srem key member1 member2...			  rt.opsForSet().remove("key","member1","member2",...)

save与bgsave区别图

springboot链接ApolloMQTT springboot链接redis哨兵_持久化

rdb与aof区别图

redis关闭重启默认使用aof恢复数据 如果没有开启aof就使用rdb快照恢复数据 如果都没开启那就没法恢复

springboot链接ApolloMQTT springboot链接redis哨兵_缓存_02

主从架构图

springboot链接ApolloMQTT springboot链接redis哨兵_缓存_03

主从全量复制

springboot链接ApolloMQTT springboot链接redis哨兵_redis_04

主从部分复制/断点复制

springboot链接ApolloMQTT springboot链接redis哨兵_redis_05

主从风暴问题解决架构图

springboot链接ApolloMQTT springboot链接redis哨兵_redis_06

哨兵架构图

springboot链接ApolloMQTT springboot链接redis哨兵_java_07