1、简介
什么是持久化?
利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化。
为什么要持久化
防止数据的意外丢失,确保数据安全性
持久化过程保存什么
- 将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据
- 将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程
1.1 Redis6.0之后为什么引入了多线程?
虽然 Redis 的主要工作(网络 I/O 和执行命令)一直是单线程模型,但是在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上。
所以为了提高网络 I/O 的并行度,Redis 6.0 对于网络 I/O 采用多线程来处理。但是对于命令的执行,Redis 仍然使用单线程来处理,所以大家不要误解 Redis 有多线程同时执行命令。、
redis IO多线程的原理是,当客户端连接到redis服务器时,主线程负责接收连接请求,获取socket,并将其放入一个全局等待读处理队列中然后主线程通过轮询算法将这些连接分配给多个IO线程,每个IO线程有自己的任务链表 。IO线程负责从socket中读取请求数据,解析命令,并将结果放入写出缓冲区中 。主线程在分配完读任务后,会阻塞等待所有IO线程读取完毕,然后通过单线程的方式执行请求命令,并将执行结果放入写出缓冲区中123 。接着主线程会再次分配写任务给IO线程,让它们将数据回写到socket中 。主线程在分配完写任务后,会阻塞等待所有IO线程写入完毕,然后解除绑定,清空等待队列
关于线程数的设置,官方的建议是如果为 4 核的 CPU,建议线程数设置为 2 或 3,如果为 8 核 CPU 建议线程数设置为 6,线程数一定要小于机器核数,线程数并不是越大越好。
因此, Redis 6.0 版本之后,Redis 在启动的时候,默认情况下会额外创建 6 个线程(这里的线程数不包括主线程):
- Redis-server : Redis的主线程,主要负责执行命令;
- bio_close_file、bio_aof_fsync、bio_lazy_free:三个后台线程,分别异步处理关闭文件任务、AOF刷盘任务、释放内存任务;
- io_thd_1、io_thd_2、io_thd_3:三个 I/O 线程,io-threads 默认是 4 ,所以会启动 3(4-1)个 I/O 多线程,用来分担 Redis 网络 I/O 的压力。
2、RDB
2.1RDB启动方式——save
-
命令
save
Copy -
作用
手动执行一次保存操作
RDB配置相关命令
- dbfilename dump.rdb
- 说明:设置本地数据库文件名,默认值为 dump.rdb
- 经验:通常设置为dump-端口号.rdb
- dir
- 说明:设置存储.rdb文件的路径
- 经验:通常设置成存储空间较大的目录中,目录名称data
- rdbcompression yes
- 说明:设置存储至本地数据库时是否压缩数据,默认为 yes,采用 LZF 压缩
- 经验:通常默认为开启状态,如果设置为no,可以节省 CPU 运行时间,但会使存储的文件变大(巨大)
- rdbchecksum yes
- 说明:设置是否进行RDB文件格式校验,该校验过程在写文件和读文件过程均进行
- 经验:通常默认为开启状态,如果设置为no,可以节约读写性过程约10%时间消耗,但是存储一定的数据损坏风险
RDB启动方式——save指令工作原理
注意:save指令的执行会阻塞当前Redis服务器,直到当前RDB过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用。
2.2 RDB启动方式——bgsave
-
命令
bgsave
-
作用
手动启动后台保存操作,但不是立即执行
RDB启动方式 —— bgsave指令工作原理
注意: bgsave命令是针对save阻塞问题做的优化。Redis内部所有涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用,推荐使用bgsave
bgsave的保存操作可以通过redis的日志查看
docker logs myredis
2.3 RDB启动方式 ——save配置
-
配置
save second changes
-
作用
满足限定时间范围内key的变化数量达到指定数量即进行持久化
-
参数
- second:监控时间范围
- changes:监控key的变化量
-
配置位置
在conf文件中进行配置
RDB启动方式 ——save配置原理
注意:
- save配置要根据实际业务情况进行设置,频度过高或过低都会出现性能问题,结果可能是灾难性的
- save配置中对于second与changes设置通常具有互补对应关系(一个大一个小),尽量不要设置成包含性关系
- save配置启动后执行的是bgsave操作
2.4 RDB启动方式对比
2.5 RDB优缺点
- 优点
- RDB是一个紧凑压缩的二进制文件,存储效率较高
- RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景
- RDB恢复数据的速度要比AOF快很多
- 应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复
- 缺点
- RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据
- bgsave指令每次运行要执行fork操作创建子进程,要牺牲掉一些性能
- Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象
3、AOF
3.1 AOF概念
- AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令,以达到恢复数据的目的。与RDB相比可以简单描述为改记录数据为记录数据产生的过程
- AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式
3.2 AOF写数据过程
3.3 AOF写数据三种策略(appendfsync)
- always
- 每次写入操作均同步到AOF文件中,数据零误差,性能较低,不建议使用
- everysec
- 每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高 ,建议使用,也是默认配置
- 在系统突然宕机的情况下丢失1秒内的数据
- no
- 由操作系统控制每次同步到AOF文件的周期,整体过程不可控
AOF功能开启
-
配置
appendonly yes|no
- 作用
- 是否开启AOF持久化功能,默认为不开启状态
- 作用
-
配置
appendfsync always|everysec|no
- 作用
- AOF写数据策略
- 作用
3.4 AOF重写
3.4.1 AOF重写概念:
AOF文件重写是将redis进程内的数据转化为写命令同步到新AOF文件的过程,简单说是将同一个数据的若干条命令执行结果转化为最终结果数据对应的指令进行记录。
3.4.2 作用
- 降低磁盘占用量,提高磁盘利用率
- 提高持久化效率,降低持久化写时间,提高IO性能
- 降低数据恢复用时,提高数据恢复效率
3.4.3 规则
- 进程内已超时的数据不再写入文件
- 忽略无效指令(读命令等可以忽略,操作失败的命令也忽略,多次对同一个key进行操作,只保留最后一次写命令),重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令
- 如del key1、 hdel key2、srem key3、set key4 111、set key4 222等
- 对同一数据的多条写命令合并为一条命令
- 如lpush list1 a、lpush list1 b、 lpush list1 c 可以转化为:lpush list1 a b c
- 为防止数据量过大造成客户端缓冲区溢出,对list、set、hash、zset等类型,每条指令最多写入64个元素
3.4.4 如何使用
1、手动重写,由客户端输入命令行,对AOF文件进行重写
bgrewriteaof
2、自动重写,在配置文件写相应的命令,在达到条件时自动对AOF进行重写
-
自动重写触发条件设置
//触发重写的最小大小 auto-aof-rewrite-min-size size //触发重写须达到的最小百分比 auto-aof-rewrite-percentage percent
-
自动重写触发比对参数( 运行指令info Persistence获取具体信息 )
//当前.aof的文件大小 aof_current_size //基础文件大小 aof_base_size
-
自动重写触发条件
3.4.5 AOF重写工作原理
-
工作原理
3.5 RDB与AOF方案对比
对于不同场景下的RDB和AOF的选择难题:
4、Redis事务
4.1 概念
redis在执行指令过程中,多条连续执行的指令被干扰、打断、插队
会引发跟数据库类似的脏读,看到其他事务操作修改的内容,产生并发问题。
redis事务就是一个命令执行队列,将一系列预定义的命令包装成一个整体。当执行时,一次性按照添加顺序依次执行,中间不会被打断或者干扰。
4.2 redis事务的基本操作
-
开启事务
multi
- 作用
- 作设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中
- 作用
-
取消事务
discard
- 作用
- 终止当前事务的定义,发生在multi之后,exec之前
- 作用
-
执行事务
exec
- 作用
- 设定事务的结束位置,同时执行事务。与multi成对出现,成对使用
- 作用
需要注意:加入事务的命令暂时存进任务队列中,并没有立即执行,只有执行exec命令才开始执行
4.3 事务操作的基本流程
4.4、事务操作的注意事项
定义事务的过程中,命令格式输入错误怎么办?
- 语法错误
- 指命令书写格式有误 例如执行了一条不存在的指令
- 处理结果
- 如果定义的事务中所包含的命令存在语法错误,整体事务中所有命令均不会执行。包括那些语法正确的命令
定义事务的过程中,命令执行出现错误怎么办?
- 运行错误
- 指命令格式正确,但是无法正确的执行。例如对list进行incr操作
- 处理结果
- 能够正确运行的命令会执行,运行错误的命令不会被执行
注意:已经执行完毕的命令对应的数据不会自动回滚,需要程序员自己在代码中实现回滚。
4.5、基于特定条件的事务执行
4.5.1 watch锁
对售罄的货物追加补货,4个业务员都有权限进行补货。补货的操作可能是一系列操作,牵扯到多个连续操作,如何避免重复对一个商品进行补货呢?
-
对 key 添加监视锁,在执行exec前如果key发生了变化,终止事务执行
watch key1, key2....
-
取消对所有key的监视
unwatch
4.5.2 分布式锁
用于解决超卖问题,如何避免最后一件商品不被多人同时购买?
在用户买商品的过程中,如果采用watch的方式,大量请求到达时,会有很多请求的watch监视的库存键被修改,导致后面的减库存事务失效而不执行
watch只能检测到键的变化,而不能保证事务中的逻辑正确性;例如如果事务中的命令是根据库存数量来判断是否可以下单,那么即使watch指令没有发现库存键被修改,也有可能出现多个客户端同时读取同一个库存数量,导致超卖
虽然redis是单线程,但多个客户端对同一数据同时进行操作时,如何避免不被同时修改?
-
使用 setnx 设置一个公共锁
//上锁 setnx lock-key value //释放锁 del lock-key
- 利用setnx命令的返回值特征,有值(被上锁了)则返回设置失败,无值(没被上锁)则返回设置成功
- 对于返回设置成功的,拥有控制权,进行下一步的具体业务操作
- 对于返回值失败的,不具有控制权,排队或等待
- 操作完毕通过del操作释放锁
注意:上述解决方案是一种设计概念,依赖规范保障,具有风险性
4.5.3、死锁的产生及解决方案
依赖分布式锁机制,某个用户操作时对应的客户端宕机,且此时已经获取锁,如何解决?
分析:
由于锁操作由用户控制加锁解锁,必定存在加锁后未解锁的风险
需要解锁操作不仅依赖用户控制,系统级别要给出对应的保底方案
分布式锁加强
-
使用 expire 为锁key添加时间限定,到时不释放,放弃锁
expire lock-key seconds pexpire lock-key milliseconds
-
由于操作通常都是微秒或毫秒级,因此该锁定时间不宜设置过大。具体时间需要业务测试后确认。
- 例如:持有锁的操作最长执行时间127ms,最短执行时间7ms。
- 测试百万次最长执行时间对应命令的最大耗时,测试百万次网络延迟平均耗时
- 锁时间设定推荐:最大耗时120%+平均网络延迟110%
- 如果业务最大耗时<<网络平均延迟,通常为2个数量级,取其中单个耗时较长即可
5、删除策略
设置过有效期的数据,并到达了有效期,称为过期数据
过期的数据真的被删除吗?
根据删除策略来定
5.1 定时删除(应对过期数据)
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
优点:节约内存,到时删除,快速释放不必要的内存
缺点:CPU压力大,影响redis服务响应时间和指令吞吐量
总结:用处理器性能换存储空间
5.2 惰性删除(应对过期数据)
数据到达过期时间,并不做处理,直到下次访问该数据时再进行删除
优点:节约CPU性能,发现必须删除时候才能删除
缺点:内存压力很大,出现长期占用内存的数据
总结:用存储空间换处理器性能(以时间换空间)
5.3 定期删除(应对过期数据)
5.4 定时、惰性、定期删除策略比对
5.5 逐出策略
并不针对于过期数据,而是当新数据进入redis,内存不足的情况。
策略内容:
相关配置:
建议采用检测易失数据
6、高级数据类型
6.1 bitmaps
可以看作一个string类型的二进制字符串,多用于存储状态,毕竟二进制只能来表示两种状态。
比如记录5名同学的性别,男为1,女为0,前三号为男同学,后两名为女同学,则可得字符串为
11100,可如此记录。
基础操作
-
获取指定key对应偏移量上的bit值
getbit key offset
-
设置指定key对应偏移量上的bit值,value只能是1或0
setbit key offset value
扩展操作
业务场景:1、统计每天一部电影是否被点播
2、统计每天有多少部电影被点播
3、统计每周/月/年有多少部电影被点播
4、统计年度哪部电影没有被点播
-
对指定key按位进行交、并、非、异或操作,并将结果保存到destKey中,参数为op
bitop op destKey key1 [key2...]
- and:交
- or:并
- not:非
- xor:异或
- 示例
bitcount 20880808 //统计有多少个1 bitop or 08-09 20880808 20880809 //将20880808的键值与20880809的键值做or操作,并将值存储于08-09键中 bitcount 08-09 //统计08-09键值有多少个1
-
统计指定key中1的数量
bitcount key [start end]
6.2 HyperLogLog
6.2.1 功能
统计独立UV(不重复的数据的数量)
HyperLogLog是用来做基数统计的,运用了LogLog算法
什么是基数:基数是数据集去重后元素的个数
如数据集合{1,3,5,7,5,7,8},则基数集为{1,3,5,7,8},基数为5(即基数集的长度)
6.2.2 基本操作
基本操作
-
添加数据
pfadd key element1, element2...
-
统计数据
pfcount key1 key2....
-
合并数据
pfmerge destkey sourcekey [sourcekey...]
6.2.3 相关说明
1、用于基数统计,不是集合,不保存数据,只记录数量而不是具体数据
2、核心是基数估算算法,最终数值存在误差
3、误差范围:基数估计结果是一个带有0.81%标准错误的近似值
4、耗空间小,每个hyperloglogkey占用12k的内存用于标记计数
6.3 GEO
基本操作
-
添加坐标点
geoadd key longitude latitude member [longitude latitude member ...] georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]
Copy -
获取坐标点
geopos key member [member ...] georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]
Copy -
计算坐标点距离
geodist key member1 member2 [unit] geohash key member [member ...]
7、主从赋值
7.1 主从复制基本介绍
由于单机redis在现在的高并发项目条件下,存在许多风险与问题:
即当前提供服务的redis服务器为主,做主服务器备份的redis服务器为slave服务器,如果主服务器挂掉,那么启动slave服务器,因为slave服务器与主服务器的数据一致,可以保证服务的畅通性。
主从复制简单介绍:
7.2 主从复制的作用
1、读写分离:master写,slave读,提高服务器的读写负载能力
2、负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求变化,改变slave的数量,通过多个节点分担数据读取负载,大大提高Redis服务器并发量和数据吞吐量。
3、故障恢复
4、数据冗余:实现数据的热备份
5、高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案
7.3 主从复制流程
建立连接的流程:
数据同步阶段:全量复制+部分复制
注意:在master进行bgsave操作的过程中,还是会有一些新数据的产生,这些数据放在复制缓冲区中,由6、7、8步来完成。因此需要的同步的数据包括全量复制和部分复制。
数据缓冲区:保存影响master变更的指令,和AOF一样
数据同步需要注意的地方:
对于master需要注意的地方
对于slave需要注意的地方
命令传播阶段
其中第7步得细说,匹配runid和offset,首先匹配runid是看是不是需要部分复制的那个slave,如果不是,直接进行全量复制;再看看offset是否匹配,可能存在缓存池溢出的情况,这个时候也要全量复制
7.4 心跳机制
slave掉线太多,或延迟过高,导致数据复制效率低;通过配置文件,可以指定条件来关闭同步功能
7.5 主从复制整体工作流图
8、哨兵
8.1 哨兵概念
哨兵是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master
监控:不断检查master和slave是否正常运行
master存活检测,master与slave运行情况检测
通知:当被监控的服务器出现问题时,向其他(哨兵间、客户端)发送通知
自动故障转移:断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端的新服务器地址
注意:哨兵也是一台redis服务器,只是不提供数据服务
通常哨兵的配置数量为单数(三个及以上,如果是偶数,它们投票容易打平)
8.2 哨兵工作原理
用于同步各个节点的状态信息,包括:
1、获取各个sentinel的状态(是否在线)
2、获取master的状态(master属性:runid,role等 各个slave的信息)
3、获取slave的状态