1、持久化
1.1、定义
redis的所有数据保存在内存中,对数据的更新将异步的保存到硬盘上
1.2、持久化的实现方式
方式一(快照):
某时某刻数据的一个完成备份
mysql的Dump
redis的RDB
方式二(写日志):
任何操作记录日志,要恢复数据,只要把日志重新走一遍即可
mysql的 Binlog
Hhase的 HLog
Redis的 AOF
2、RDB
2.1、定义
2.2、触发机制(主要三种方式)
方式一:
save(同步)
1、客户端执行save命令 ---> redis服务端 ---> 同步创建RDB二进制文件
2、会造成redis的阻塞(数据量非常大的时候)
3、文件策略: 如果老的RDB存在,会替换老的
4、复杂度 o(n)
方式二:
bgsave(异步,Backgroud saving started)
1、客户端执行save命令 ---> redis服务端 ---> 异步创建RDB二进制文件(fork函数生成一个子进程(fork会阻塞reids),执行createRDB,执行成功,返回给reids消息)
2、此时访问redis,会正常响应客户端
3、文件策略: 跟save相同,如果老的RDB存在,会替换老的
4、复杂度 o(n)
方式三:
自动(通过配置)
配置 seconds changes
save 900 1
save 300 10
save 60 10000
如果60s中改变了1w条数据,自动生成rdb
如果300s中改变了10条数据,自动生成rdb
如果900s中改变了1条数据,自动生成rdb
以上三条符合任意一条,就自动生成rdb,内部使用bgsave
# 配置
save 900 1 # 配置一条
save 300 10 # 配置一条
save 60 10000 # 配置一条
dbfilename dump.rdb # rdb文件的名字,默认为dump.rdb
dir ./ # rdb文件存在当前目录
stop-writes-on-bgsave-error yes # 如果bgsave出现错误,是否停止写入,默认为yes
rdbcompression yes # 采用压缩格式
rdbchecksum yes # 是否对rdb文件进行校验和检验
# 最佳配置
save 900 1
save 300 10
save 60 10000
dbfilename dump-${port}.rdb # 以端口号作为文件名,可能一台机器上很多reids,不会乱
dir /bigdiskpath # 保存路径放到一个大硬盘位置目录
stop-writes-on-bgsave-error yes # 出现错误停止
rdbcompression yes # 压缩
rdbchecksum yes # 校验
2.3、触发机制(不容忽略的方式)
1、全量复制 # 没有执行save和bgsave,没有添加rdb策略,还会生成rdb文件,如果开启主从复制,主会自动生成rdb
2、debug reload # debug级别的重启,不会将内存中的数据清空
3、shutdown save # 关闭会触发rdb的生成
3、AOF
3.1、RDB存在的问题
耗时,耗性能
不可控,可能会丢失数据
3.2、定义
客户端每写入一条命令,都记录一条日志,放到日志文件中,如果出现宕机,可以将数据完全恢复
3.3、AOF的三种策略
日志不是直接写到硬盘上,而是先放在缓冲区,缓冲区根据一些策略,写到硬盘上
具体策略如下:
always: redis –> 写命令刷新的缓冲区 —> 每条命令fsync到硬盘 —> AOF文件
everysec(默认值): redis —> 写命令刷新的缓冲区 —> 每秒把缓冲区fsync到硬盘 –> AOF文件
no: redis —> 写命令刷新的缓冲区 —> 操作系统决定,缓冲区fsync到硬盘 –> AOF文件
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢失数据 | 每秒一次fsync,丢失1秒数据 | 不用管 |
缺点 | IO开销大,一般的sata盘只有几百TPS | 丢1秒数据 | 不可控 |
3.4、AOF 重写
随着命令的逐步写入,并发量的变大, AOF文件会越来越大,通过AOF重写来解决该问题
原生AOF AOF重写
set hello world
set hello java set hello hehe
set hello hehe
incr counter set counter 2
incr counter
rpush mylist a
rpush mylist b rpush mylist a b c
rpush mylist c
本质就是把过期的,无用的,重复的,可以优化的命令,来优化
这样可以减少磁盘占用量,加速恢复速度
3.4.1、实现方式
bgrewriteaof
客户端向服务端发送bgrewriteaof命令,服务端会起一个fork进程,完成AOF重写
3.4.2、AOF重写配置
配置名 | 含义 |
---|---|
auto-aof-rewrite-min-size | AOF文件重写需要尺寸 |
auto-aof-rewrite-percentage | AOF文件增长率 |
统计名 | 含义 |
---|---|
aof_current_size | AOF当前尺寸(单位:字节) |
aof_base_size | AOF上次启动和重写的尺寸(单位:字节) |
自动触发时机(两个条件同时满足):
aof_current_size>auto-aof-rewrite-min-size: 当前尺寸大于重写需要尺寸
(aof_current_size-aof_base_size)/aof_base_size>auto-aof-rewrite-percentage: (增长率)当前尺寸减去上次重写的尺寸,除以上次重写的尺寸如果大于配置中的增长率
3.4.3、重写流程
3.4.4、配置
appendonly yes # 将该选项设置为yes,打开
appendfilename "appendonly-${port}.aof" # 文件保存的名字
appendfsync everysec # 采用第二种策略
dir /bigdiskpath # 存放的路径
no-appendfsync-on-rewrite yes # 在aof重写的时候,是否要做aof的append操作,因为aof重写消耗性能,磁盘消耗,正常aof写磁盘有一定的冲突,这段期间的数据,允许丢失
4、RDB和AOF的选择
4.1、rdb和aof的比较
命令 | rdb | aof |
---|---|---|
启动优先级 | 低 | 高(挂掉重启,会加载aof的数据) |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
轻重 | 重 | 轻 |
4.2、rdb最佳策略
rdb关掉,主从操作时
集中管理: 按天,按小时备份数据
主从配置,从节点打开
4.3、aof最佳策略
开: 缓存和存储,大部分情况都打开,aof重写集中管理
everysec: 通过每秒刷新的策略
4.4、最佳策略
小分片: 每个redis的最大内存为4g
缓存或存储: 根据特性,使用不通策略
时时监控硬盘.内存,负载网络等
有足够内存
5、主从复制
机器故障,容量瓶颈,QPS瓶颈
一主一从,一主多从
做读写分离
做数据副本
扩展数据性能
一个maskter可以有多个slave
一个slave只能有一个master
数据流向是单向的,从master到slave
5.1、原理
1、副本库通过slaveof 127.0.0.1 6379命令,连接主库,并发送SYNC给主库
2、主库收到SYNC,会立即触发BGSAVE,后台保存RDB,发送给副本库
3、副本库接收后会应用RDB快照
4、主库会陆续将中间产生的新的操作,保存并发送给副本库
5、到此,我们主复制集就正常工作了
6、再此以后,主库只要发生新的操作,都会以命令传播的形式自动发送给副本库.
7、所有复制相关信息,从info信息中都可以查到.即使重启任何节点,他的主从关系依然都在.
8、如果发生主从关系断开时,从库数据没有任何损坏,在下次重连之后,从库发送PSYNC给主库
9、主库只会将从库缺失部分的数据同步给从库应用,达到快速恢复主从的目的
5.2、主库是否要开启持久化
如果不开有可能,主库重启操作,造成所有主从数据丢失!
5.3、辅助配置(主从数据一致性配置)
min-slaves-to-write 1
min-slaves-max-lag 3
那么在从服务器的数量少于一个或者三个从服务器的延迟(lag)值都大于或等于3秒时,主服务器将拒绝执行写命令
5.4、主从复制的配置
5.4.1、slave 命令
6380是从,6379是主
在6380上执行(去从库配置,配置主库)
slaveof 127.0.0.1 6379 # 异步
slaveof no one # 取消复制,不会把之前的数据清除
5.4.2、配置文件
slaveof ip port # 配置从节点ip和端口
slave-read-only yes # 从节点只读,因为可读可写,数据会乱
mkdir -p redis1/conf redis1/data redis2/conf redis2/data redis3/conf redis3/data
vim redis.conf
daemonize no
pidfile redis.pid
bind 0.0.0.0
protected-mode no
port 6379
timeout 0
logfile redis.log
dbfilename dump.rdb
dir /data
slaveof 127.0.0.1 6379
slave-read-only yes
cp redis.conf /home/redis2/conf/
docker run -p 6379:6379 --name redis_6379 -v /home/redis1/conf/redis.conf:/etc/redis/redis.conf -v /home/redis1/data:/data -d redis redis-server /etc/redis/redis.conf
docker run -p 6378:6379 --name redis_6378 -v /home/redis2/conf/redis.conf:/etc/redis/redis.conf -v /home/redis2/data:/data -d redis redis-server /etc/redis/redis.conf
docker run -p 6377:6379 --name redis_6377 -v /home/redis3/conf/redis.conf:/etc/redis/redis.conf -v /home/redis3/data:/data -d redis redis-server /etc/redis/redis.conf
info replication
5.4.3、配置总结
方式一:
在从库执行 SLAVEOF 127.0.0.1 6379
断开关系 slaveof no one
方式二: 配置文件(配在从库的配置文件中)
slaveof 127.0.0.1 6379
slave-read-only yes
5.5、故障处理
slave故障
master故障
5.6、主从复制常见问题
1、读写分离
读流量分摊到从节点
可能遇到问题: 复制数据延迟,读到过期数据,从节点故障
2、主从配置不一致
maxmemory不一致: 丢失数据
数据结构优化参数: 主节点做了优化,从节点没有设置优化,会出现一些问题
3、规避全量复制
第一次全量复制,不可避免: 小主节点,低峰(夜间)
节点运行id不匹配: 主节点重启(运行id变化)
复制挤压缓冲区不足: 增大复制缓冲区大小,rel_backlog_size
4、规避复制风暴
单主节点复制风暴,主节点重启,所有从节点复制
6、Redis-Sentinel
6.1、主从复制高可用
主从复制存在的问题:
1、主从复制,主节点发生故障,需要做故障转移,可以手动转移: 让其中一个slave变成master
2、主从复制,只能主写数据,所以写能力和存储能力有限
6.2、架构说明
可以做故障判断,故障转移,通知客户端(其实是一个进程),客户端直接连接sentinel的地址
1、多个sentinel发现并确认master有问题
2、选举出一个sentinel作为领导
3、选取一个slave作为新的master
4、通知其余slave成为新的master的slave
5、通知客户端主从变化
6、等待老的master复活成为新master的slave
6.3、安装配置
1、配置开启主从节点
2、配置开启sentinel监控主节点(sentinel是特殊的redis)
3、应该是多台机器
# 配置开启sentinel监控主节点
mkdir -p redis4/conf redis4/data redis5/conf redis5/data redis6/data redis6/conf
vi sentinel.conf
port 26379
daemonize yes
dir data
protected-mode no
bind 0.0.0.0
logfile "redis_sentinel.log"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
docker run -p 26379:26379 --name redis_26379 -v /home/redis4/conf/sentinel.conf:/etc/redis/sentinel.conf -v /home/redis4/data:/data -d redis redis-sentinel /etc/redis/sentinel.conf
docker run -p 26378:26379 --name redis_26378 -v /home/redis5/conf/sentinel.conf:/etc/redis/sentinel.conf -v /home/redis5/data:/data -d redis redis-sentinel /etc/redis/sentinel.conf
docker run -p 26377:26379 --name redis_26377 -v /home/redis6/conf/sentinel.conf:/etc/redis/sentinel.conf -v /home/redis6/data:/data -d redis redis-sentinel /etc/redis/sentinel.conf
redis-sentinel sentinel.conf
info
配置会重写,自动发现slave
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel monitor <master-name> <ip> <redis-port> <quorum>
告诉sentinel去监听地址为ip:port的一个master,这里的master-name可以自定义,quorum是一个数字,指明当有多少个sentinel认为一个master失效时,master才算真正失效
sentinel auth-pass <master-name> <password>
设置连接master和slave时的密码,注意的是sentinel不能分别为master和slave设置不同的密码,因此master和slave的密码应该设置相同
sentinel down-after-milliseconds <master-name> <milliseconds>
这个配置项指定了需要多少失效时间,一个master才会被这个sentinel主观地认为是不可用的,单位是毫秒,默认为30秒
sentinel parallel-syncs <master-name> <numslaves>
这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用,可以通过将这个值设为1来保证每次只有一个slave处于不能处理命令请求的状态
sentinel failover-timeout <master-name> <milliseconds>
failover-timeout 可以用在以下这些方面:
1、同一个sentinel对同一个master两次failover之间的间隔时间
2、当一个slave从一个错误的master那里同步数据开始计算时间,直到slave被纠正为向正确的master那里同步数据时
3、当想要取消一个正在进行的failover所需要的时间
4、当进行failover时,配置所有slaves指向新的master所需的最大时间,不过即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
操作步骤
1、搭一个一主两从
# 创建三个配置文件:
# 第一个是主配置文件
daemonize yes
pidfile /var/run/redis.pid
port 6379
dir "/opt/soft/redis/data"
logfile “6379.log”
# 第二个是从配置文件
daemonize yes
pidfile /var/run/redis2.pid
port 6378
dir "/opt/soft/redis/data2"
logfile “6378.log”
slaveof 127.0.0.1 6379
slave-read-only yes
# 第三个是从配置文件
daemonize yes
pidfile /var/run/redis3.pid
port 6377
dir "/opt/soft/redis/data3"
logfile “6377.log”
slaveof 127.0.0.1 6379
slave-read-only yes
# 把三个redis服务都启动起来
./src/redis-server redis_6379.conf
./src/redis-server redis_6378.conf
./src/redis-server redis_6377.conf
2、搭建哨兵
# sentinel.conf这个文件
# 把哨兵也当成一个redis服务器
创建三个配置文件分别叫sentinel_26379.conf sentinel_26378.conf sentinel_26377.conf
# 当前路径下创建 data1 data2 data3 个文件夹
# 内容如下(需要修改端口,文件地址日志文件名字)
port 26379
daemonize yes
dir ./data3
protected-mode no
bind 0.0.0.0
logfile "redis_sentinel3.log"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
# 启动三个哨兵
./src/redis-sentinel sentinel_26379.conf
./src/redis-sentinel sentinel_26378.conf
./src/redis-sentinel sentinel_26377.conf
# 登陆哨兵
./src/redis-cli -p 26377
# 输入 info
# 查看哨兵的配置文件被修改了,自动生成的
# 主动停掉主redis 6379,哨兵会自动选择一个从库作为主库
redis-cli -p 6379
shutdown
# 等待原来的主库启动,该主库会变成从库
6.4、客户端连接
import redis
from redis.sentinel import Sentinel
# 连接哨兵服务器(主机名也可以用域名)
# 192.168.1.4:26379
sentinel = Sentinel([('192.168.1.4', 26379),
('192.168.1.4', 26378),
('192.168.1.4', 26377)
],
socket_timeout=5)
print(sentinel)
# 获取主服务器地址
master = sentinel.discover_master('mymaster')
print(master)
# 获取从服务器地址
slave = sentinel.discover_slaves('mymaster')
print(slave)
# 读写分离
# 获取主服务器进行写入
master = sentinel.master_for('mymaster', socket_timeout=0.5)
w_ret = master.set('foo', 'bar')
slave = sentinel.slave_for('mymaster', socket_timeout=0.5)
r_ret = slave.get('foo')
print(r_ret)