Redis持久化机制
- RDB快照(Redis DataBase)
在指定时间间隔执行数据集的时间点快照,将当前Redis的数据生成一个快照"dump.rdb"二进制文件
如下配置
save 60 1000 // 在60秒内有1000个key被改动,则触发RDB快照,生成文件
RDB缺点
RDB并不是时时刻刻都在执行的,因为RDB记录的是整个Redis的数据,执行RDB时会很消耗性能。
如果在执行完RDB之后的真空期发生了Redis服务器的宕机,就会导致,这期间的数据丢失
- AOF(append-only file)
将修改key的每一条指令存进 "appendonly.aof"文件中
比如 在执行 “set k1 v1” 时,AOF会记录数据
*3
$3
set
$2
k1
$2
v1
这是一种 resp 协议格式数据,每当Redis执行一个改变数据的命令时,这个命令就会被记录在AOF文件的末尾
当Redis服务重启时,会一个个执行AOF里的命令,达到还原数据的目的
- AOF缺点
如果Redis的数据量大,执行的命令过多,那么服务重启时,会花费很长的时间
Redis 发布订阅
什么是发布订阅,发布订阅(pub/sub)是一种消息通信模式:发送者发送消息,订阅者接收消息
Redis 的subscribe
命令可以让客户端订阅任意数量的频道
Redis的List双端链表也可以实现消息队列机制,但是不能支持一对多的消息分发,并且如果消息的生产速度大于订阅者的接收速度,那么就会造成消息堆积。
- 基于channel的发布/订阅
127.0.0.1:6379> SUBSCRIBE qq # 订阅名为"qq"的频道,可同时订阅多个
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "qq"
3) (integer) 1
我是Windows,没法开启另一个客户端,所以用java代替发布者
publicstatic void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.publish("qq", "hello"); // 往”qq“的频道发送"hello"消息
}
执行java代码后,订阅者收到,来自发布者的消息
127.0.0.1:6379> SUBSCRIBE qq
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "qq"
3) (integer) 1
1) "message"
2) "qq"
3) "hello"
- 基于pattern的发布/订阅
127.0.0.1:6379> PSUBSCRIBE q? # 订阅”q“?是个占位符
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "q?"
3) (integer) 1
publicstatic void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.publish("qq", "hello"); // 往”qq“的频道发送"hello"消息
}
127.0.0.1:6379> PSUBSCRIBE q?
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "q?"
3) (integer) 1
1) "pmessage"
2) "q?" # 信息匹配的模式:q?
3) "qq" # 信息本身的目标频道:qq信息本身的目标频道:qq
4) "hello"
主从复制
主从复制,是指将一台Redis服务器的数据复制到另一台Redis服务器,前者为主服务器,后者为从服务器,且数据的复制是单向的,只能主–>从,主服务器主要进行写操作,从服务器进行读操作
- 主从复制好处
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:如果主服务器宕机了,哨兵模式可以将一个从服务器升级为主服务器
- 负载均衡:redis 80%都是读操作,读写分离可以减缓redis服务器压力。
- 缺点:
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave服务器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重
info replication
命令查看当前服务器信息
127.0.0.1:6379> info replication
# Replication
role:master # 主服务器
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
主从复制只需要配置从服务器,从服务器配置文件更改:
slaveof <masterip> <masterport> # 配置主服务器ip,端口号
masterauth <master-password> # 主机的密码
slave-read-only yes # 从服务器,只读
复制原理
- 全量复制
- 从机 启动成功连接到 主机后会发送一个sync同步命令
- 主机接到命令,执行bgsave命令生成RDB快照文件记录所有写命令
- 在后台进程执行完毕之后,主机将向所有从机发送快照文件
- 从机收到并处理RDB快照,将数据载入缓存。
- 增量复制
从机在初始化完全量复制并正常工作后,主机会将发生的写命令都发给从机,从机接受并处理命令的过程
哨兵模式(Sentinel)
在主从复制模式下,如果主机宕机了,我们需要手动配置将一台从机升级为主机,而启动哨兵模式,可以帮我们自动完成这项操作
- 首先在redis目录下添加Sentinel.conf配置文件,添加配置
###普通配置
#哨兵端口号
port 6385
# 保护模式关闭,这样其他服务起就可以访问此台redis
protected-mode no
# 哨兵模式是后台启动
daemonize yes
###核心配置
# 最后一个参数:哨兵的数量。1表示,当只要有1个哨兵发现master断线了,就会标记master为down
#sentinel monitor [被监控的主机名称] [主机ip] [主机端口]
sentinel monitor mymaster 127.0.0.1 6379 1
# master中redis的密码
sentinel auth-pass mymaster 123456
# 哨兵从master节点宕机后,等待多少时间(毫秒),认定master不可用,默认30s。
sentinel down-after-milliseconds mymaster 10000
linux命令redis-sentinel sentinel.conf
启动哨兵
windows命令redis-server sentinel.conf --sentinel
启动哨兵
之后启动主机和从机,哨兵模式就搭建好了
如果主机宕机超过一定时间(默认30s)后,这时就会从从机中随机选择一个服务器,升级为主机。之后如果主机重新启动后,这台主机会自动降将为从机。
缓存穿透
正常的环境下,用户请求数据会优先访问Redis缓存,如果缓存中没有,再去数据库中找,数据库有,就会写到缓存中并返回给用户,但是如果数据库中也没有,并且这时大量请求这个没有的数据,那么会给数据库带来很大的压力,甚至崩溃。这就是缓存穿透
- 解决方案
- 缓存空对象,在数据库哪怕没找到对象,也生成一个空的值存入缓存中,并且设置一个较短的过期时间。
- 使用布隆过滤器
- 一般这种请求都是遭到攻击引起的,所以可以过滤过于频繁查询无效数据的请求
缓存击穿
缓存中的某个key过于频繁的被搜索,是一种非常“热点”的数据,这时这个key刚好过期,在过期的一瞬间,所有的请求都会去请求数据库,造成数据库的压力,这就叫缓存击穿。
- 解决方案:
- 将“热点”key设为永不过期,但什么是“热点数据”?
- 加分布式锁,redis可以用setnx命令来实现分布式锁,为防止出现死锁,要在互斥锁上加过期时间
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
} else {
return value;
}
}
缓存雪崩
当Redis服务器宕机重启,或者是缓存中大量的key一起失效,这时大量请求就会直接访问数据库,造成缓存雪崩
- 解决方案
- 将key的过期时间分散开来,避免同时过期大量的key
- 缓存集群,主从复制