1、Redis持久化之RDB
在指定的时间间隔
内将内存中的数据集快照
写入磁盘.
1.1、备份是如何执行的
Redis 会单独创建一个子进程
(fork)来进行持久化,会先将数据写入一个临时文件
中,等持久化过程结束后,用这个临时文件替换
上次持久化好的文件。在整个过程中,主进程不进行任何io操作,确保了极高的性能。
RDB 的缺点是
最后一次持久化后的数据可能丢失
1.2、Fork
-
Fork 的作用是
复制
一个与当前进程一样的进程
,新进程的所有数据(变量,环境变量,程序计数器等)数值都和原来的进程一致,但是是一个全新的进程,并且作为原进程的子进程
. -
Linux 中引入了“
写时复制技术
” -
一般情况下父进程和子进程
共用一段物理内存
,只有进程空间的各段内容要发生变化时,才会将父进程的内容复制一份给子进程。
1.3、RDB持久化流程
1.4、dump.rdb文件
在redis.conf中配置文件名称,默认为dump.rdb
1.5、如何触发RDB快照
配置文件中默认的快照配置
命令save VS bgsave
-
save :save时只管保存,其它不管,全部阻塞。手动保存。不建议。
-
bgsave:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
1.6、优势以及劣势
优势:
- 适合大规模的数据恢复
- 对数据的完整性和一致性要求不高
- 节省磁盘空间
- 恢复速度快
劣势:
- fork时内存中的数据被克隆了一份,造成2倍膨胀性
- fork时写时复制技术,若数据庞大会损耗性能
- 在备份周期一定间隔时间做一次备份,如果redis 意外down掉,会丢失最后一次快照时的所有修改
动态停止RDB:redis-cli config set save ""
#save后给空值,表示禁用保存策略
2、Redis持久化之AOF
以日志
的形式来记录每个写操作
(增量保存),将Redis 执行过的所有写操作记录下来(读操作不记录),只许追加文件但不可以改写文件,redis 启动后会读取该文件重新构建数据。一句话:redis 重启根据日志文件内容将写指令完整执行一次以恢复数据
。
2.1、AOF持久化流程
-
客户端的请求
写命令
会被appand
追加到AOF缓冲区内 -
AOF缓冲区根据AOF持久化策略(always,everysec,no)将操作sync同步到磁盘的AOF文件中
-
AOF文件大小超过重写策略或手动重写时,会对AOF文件
rewrite
重写,压缩AOF文件容量 -
redis服务重启时,会重新
load
加载AOF文件中的写操作达到数据恢复的目的
可以在redis.conf中配置文件名称,默认为 appendonly.aof
AOF文件的保存路径,同RDB的路径一致。
AOF和RDB同时开启,redis听谁的?
AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)
2.2、AOF启动/修复/恢复
-
AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载。
-
正常恢复
- 修改默认的appendonly no,改为yes
- 将有数据的aof文件复制一份保存到对应目录(查看目录:config get dir)
- 恢复:重启redis然后重新加载
-
异常恢复
- 修改默认的appendonly no,改为yes
- 如遇到AOF文件损坏,通过
/usr/local/bin/redis-check-aof--fix appendonly.aof
进行恢复 - 备份被写坏的AOF文件
- 恢复:重启redis,然后重新加载
2.3、AOF同步频率设置
-
appendfsync always
始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好
-
appendfsync everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
-
appendfsync no
redis不主动进行同步,把同步时机交给操作系统。
2.4、优势以及劣势
优势:
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF,可以处理误操作。
劣势:
- 比起RDB占用更多的磁盘空间
- 恢复备份速度要慢
- 每次读写都同步的话,有一定的性能压力
- 存在个别Bug,造成恢复不能
小总结
官方推荐两个都启用。
如果对数据不敏感,可以选单独用RDB。
不建议单独用 AOF,因为可能会出现Bug。
如果只是做纯内存缓存,可以都不用。
3、Redis主从复制
主机数据更新后根据配置和策略,自动更新到备机的master/slaver
机制,master以写为主,slaver以读为主
- 读写分离,性能扩展
- 容灾快速恢复
3.1、怎么玩
- 新建redis6379.conf,填写以下内容
include /myredis/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb
重复操作新建6380、6381
- 启动三台redis服务器
- 查看三台主机运行情况
- 配从(库)不配主(库)
slaveof #成为某个实例的从服务器
在6380和6381上执行: slaveof 127.0.0.1 6379
在主机上写,在从机上可以读取数据
在从机上写数据报错
主机挂掉,重启就行,一切如初
从机重启需重设:slaveof 127.0.0.1 6379
3.2、常用三招
3.2.1、一主二仆
-
切入点问题?slave1、slave2是从头开始复制还是从切入点开始复制?比如从k4进来,那之前的k1,k2,k3是否也可以复制?可以
-
从机是否可以写?set可否? 不可
-
主机shutdown后情况如何?从机是上位还是原地待命?
-
主机又回来了后,主机新增记录,从机还能否顺利复制?
-
其中一台从机down后情况如何?依照原有它能跟上大部队吗?
3.2.2、薪火相传
上一个Slave可以是下一个slave的Master,Slave同样可以接收其他 slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力,去中心化降低风险
用 slaveof <ip><port>
中途变更转向:会清除之前的数据,重新建立拷贝最新的
风险是一旦某个slave宕机,后面的slave都没法备份
主机挂了,从机还是从机,无法写数据了
3.2.3、反客为主
当一个master宕机后,后面的slave可以立刻升为master,其后面的slave不用做任何修改
用slaveof no one
将从机变为主机
3.3、复制原理
- slave启动成功连接到master 后会发送一个sync命令
- master 接到命令启动后台的存盘进程。同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,完成一次完全数据同步
- 全量复制:slave 服务在接收到数据库文件数据后,将其存盘并加入到内存中
- 增量复制:master 继续将新的所有收集到的修改命令依次传给slave ,完成同步
- 只要重新连接master ,一次完全同步(全量复制)将被自动执行
3.4、哨兵模式
反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库
怎么玩:调整为一主二仆模式,6379带着6380、6381
-
自定义的
/myredis
目录下新建文件vi sentinel.conf
(注意文件名字不能错) -
配置文件内容
sentinel monitor mymaster 127.0.0.1 6379 1
mymaster为监控对象起的服务器名称,1为至少有多少个哨兵同意迁移的数量
- 启动哨兵
redis-sentinel /myredis/sentinel.conf
当主机挂掉,从机选举中产生新的主机
那么哪一个从机会被选举为主机呢?根据优先级别:slave-priority
设置从机的优先级,值越小,优先级越高,用于选举主机时使用。默认100
原主机重启后会变为从机
3.5、复制延迟
所有的写操作都是在master上,要同步更新到slaver 从机需要一定的时间,当系统很繁忙时,延迟问题会更加严重,slaver 机器的增加也会使得延迟问题更加严重。
3.6、故障恢复
-
优先级在redis.conf中默认:slave-priority 100,值越小优先级越高
-
偏移量是指获得原主机数据最全的
-
每个redis实例启动后都会随机生成一个40位的runid
4、Redis 集群
4.1、引出问题
容量不够,redis如何进行扩容?
并发写操作, redis如何分摊?
无中心化集群
配置。
删除持久化数据:将rdb,aof文件都删除掉。
4.2、什么是集群
- Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N
- Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求
4.3、搭建集群
-
制作6个实例,6379,6380,6381,6389,6390,6391
-
删掉rdb 文件
-
配置文件修改
include /myredis/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb
cluster-enabled yes #打开集群模式
cluster-config-file nodes-6379.conf #设定节点配置文件名
cluster-node-timeout 15000 #设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换
3.修改好redis6379.conf文件,拷贝6个redis.conf文件
4.使用查找替换修改另外5个文件 例如::%s/6379/6380
5.启动6个redis服务
- 将六个节点合成一个集群
组合之前,请确保所有redis实例启动后,nodes-xxxx.conf文件都生成正常。
-
合体(高版本rb环境已经配好)
cd /opt/redis-6.2.7/src
#src 目录下执行 redis-cli --cluster create --cluster-replicas 1 192.168.181.100:6379 192.168.181.100:6380 192.168.181.100:6381 192.168.181.100:6389 192.168.181.100:6390 192.168.181.100:6391
此处不要用127.0.0.1, 请用真实IP地址(自己电脑)
–replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组
-
登录
普通方式:
可能直接进入
读主机
,存储数据时,会出现MOVED重定向操作集群方式:
采用集群策略连接,设置数据会自动切换到相应的写主机
-
通过
cluster nodes
命令查看集群信息 -
redis cluster 如何分配这六个节点?
一个集群至少要有三个主节点
-
什么是slots?
-
一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个
-
集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:
节点 A 负责处理 0 号至 5460 号插槽。
节点 B 负责处理 5461 号至 10922 号插槽。
节点 C 负责处理 10923 号至 16383 号插槽。
-
-
在集群中录入值
-
redis-cli客户端提供了 –c 参数实现自动重定向
-
不在一个slot下的键值,是不能使用mget,mset等多键操作
-
可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去
-
-
查询集群中的值
CLUSTER GETKEYSINSLOT <slot><count>
返回 count 个 slot 槽中的键 -
故障恢复
-
如果主节点下线,从节点自动升为主节点
-
主节点恢复后变成从机
-
如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,那么 ,整个集群都挂掉
如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,该插槽数据全都不能使用,也无法存储
-
4.4、集群的Jedis开发
即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。
无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据
4.5、集群优劣势
优势:
- 实现扩容
- 分摊压力
- 无中心配置相对简单
劣势:
-
多键操作是不被支持的
-
多键的Redis事务是不被支持的。lua脚本不被支持
5、Redis应用问题
5.1、缓存穿透
问题描述:
key对应的数据在数据源并不存在
,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有
解决办法:
-
对空值缓存
如果一个查询返回的数据为空,我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟
-
设置可访问的名单
使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问
-
采用布隆过滤器
5.2、缓存击穿
问题描述:
key 对应的value值存在,但是key 过期
,若有大量的并发请求
,这些请求发现缓存过期一般会从后端数据库加载数据并回设到缓存,此时高并发的请求可能瞬间搞跨数据库。
解决方案:
- 预先设置热门数据
- 在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长
- 实时调整
- 现场监控哪些数据热门,实时调整key的过期时长
- 使用锁(性能问题)
5.3、缓存雪崩
问题描述:
与缓存击穿类似,区别在于缓存雪崩是针对很多key 缓存
正常访问:
缓存失效访问:
解决方案:
- 构建多级缓存架构
- nginx缓存 + redis缓存 +其他缓存(ehcache等)
- 使用锁或者队列
- 使用加锁或者队列的方式来保证不会有大量的线程一次性访问数据库,从而避免失效时大量的并发请求落到底层的存储系统上。不适合高并发场景。
- 设置过期标志更新缓存
- 记录缓存数据是否过期,如果过期会触发通知另外的线程去后台更新实际key 的缓存
- 将缓存失效时间分散开
5.4、分布式锁
使用redis 实现分布式锁
SET key value nx ex 12
:nx上锁 ex 设置过期时间
- 使用setnx上锁,通过del释放锁
- 锁一直没有释放,设置key过期时间 ,自动释放
问题:上锁之后突然出现异常,无法设置过期时间了
解决:上锁的同时设置过期时间,自动释放锁
set users 10 nx ex 12
设置过期时间:
6、Redis 6 新功能
6.1、ACL
访问控制列表,对用户进行更细粒度的权限控制
命令:
- 使用
acl list
命令展现用户权限列表
- 简单了解,后续补充
6.2、io多线程
IO多线程其实指客户端交互部分的网络IO交互处理模块多线程,而非执行命令多线程。Redis6执行命令依然是单线程