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区别图
rdb与aof区别图
redis关闭重启默认使用aof恢复数据 如果没有开启aof就使用rdb快照恢复数据 如果都没开启那就没法恢复
主从架构图
主从全量复制
主从部分复制/断点复制
主从风暴问题解决架构图
哨兵架构图