Redis进阶
- 1.Redis持久化
- 1.1 什么是Redis持久化?为什么需要持久化?
- 1.2 Redis持久化方式——RDB(Redis DataBase)
- 1.2.1 什么是RDB?
- 1.2.2 备份文件位置
- 1.2.3 触发RDB的方式
- 1.2.3.1 自动触发
- 1.2.3.2 手动触发
- 1.2.3.3 其他触发方式
- 1.2.4 RDB优缺点
- 1.3 Redis持久化方式——AOF(Append Only File)
- 1.3.1 什么是AOF
- 1.3.2 备份的位置(Redis7)
- 1.3.3 AOF工作原理
- 1.3.4 AOF优缺点
- 1.4 Redis持久化方式——RDB+AOF
- 2. Redis主从复制(Replication)
- 2.1 什么是主从复制?为什么要主从复制?
- 2.2 主从复制配置
- 2.3 主从复制过程
- 2.4 主从复制的其他说明
- 3. Redis哨兵(Sentinel)
- 3.1 什么是哨兵
- 3.2 哨兵集群配置和启动
- 3.3 哨兵选举流程
- 3.4 哨兵集群的优缺点
- 4. 集群(Cluster)
- 4.1 什么是集群
- 4.2 集群算法——分片、槽位slot
- 4.3 其他说明
- 5.Redis单线程与多线程
- 5.1 Redis是单线程还是多线程
- 5.2 Redis为什么快
- 5.3 主线程和IO多线程的协作流程
- 6.布隆过滤器(BloomFilter)
- 6.1 什么是布隆过滤器
- 6.2 布隆过滤器原理
- 6.3 优缺点和使用场景
- 7. 缓存雪崩、穿透、击穿
- 7.1 缓存雪崩
- 7.2 缓存穿透
- 7.3 缓存击穿
1.Redis持久化
1.1 什么是Redis持久化?为什么需要持久化?
什么是Redis持久化:将内存中的数据写到磁盘中。
为什么Redis需要持久化:Redis缓存是基于内存的,当服务器宕机或发生意外的时候,数据可能会丢失,所以需要将内存中的数据以一定的方式存储到磁盘中,方便数据恢复。
1.2 Redis持久化方式——RDB(Redis DataBase)
1.2.1 什么是RDB?
在一定的间隔时间内将内存中的数据写入到磁盘中,专业术语称之为snapshots(快照)。类似照相机每间隔一定时间拍摄一张照片。RDB是Redis默认的持久化方式。在redis.conf中可以找到关于间隔时间的说明:见1.2.3中自动触发部分。
1.2.2 备份文件位置
可以在Redis.conf文件中找到下列配置,dbfilename是备份文件名,dir是保存备份文件的文件夹。可以根据需求进行修改。
1.2.3 触发RDB的方式
1.2.3.1 自动触发
在Redis.conf中可以找到相关的配置说明。当达到条件的时候fork出子进程来进行备份,类似手动触发的bgsave。
# Unless specified otherwise, by default Redis will save the DB:
# After 3600 seconds (an hour) if at least 1 change was performed
# After 300 seconds (5 minutes) if at least 100 changes were performed
# After 60 seconds if at least 10000 changes were performed
save 3600 1 300 100 60 10000
1.2.3.2 手动触发
例如某时某刻向Redis中写入了某数据,需要立即保存到磁盘,就可以手动触发保存命令。保存命令分为SAVE和BGSAVE,区别如下:
- save :同步生成快照,会造成Redis阻塞,一般不建议使用。
- bgsave:异步生成快照。会fork出子进程来执行持久化,主进程可以继续执行任务。
redis> save
OK
redis> bgsave
Background saving started
1.2.3.3 其他触发方式
- 执行shutdown 自动触发
- 主从复制时master 自定触发
1.2.4 RDB优缺点
- 优点:相比于AOF 更适合大规模数据更快的恢复。因为所有的数据都备份在磁盘,而AOF只是保存了执行的命令,恢复数据的时候要耗时加载。
- 缺点:(1)可能造成数据丢失:RDB是一定条件下才触发保存,当服务器意外宕机并且最近的一次数据并没有达到触发条件就会丢失最近的数据。 (2)fork子进程备份可能会耗时造成卡顿:虽然备份是fork子进程进行的,但是主进程fork子进程的时候会暂时阻塞,当要备份的数据很大的时候,fork可能会很耗时,造成卡顿。
1.3 Redis持久化方式——AOF(Append Only File)
1.3.1 什么是AOF
将服务器的的所有写操作记录进日志,数据恢复时根据所有写操作命令重建数据。AOF默认关闭,如果需要开启,可以在Redis.conf中开启:将appendonly 设置为yes。
1.3.2 备份的位置(Redis7)
备份的文件夹名称配置如下:
备份的文件名称配置如下:
即:有关AOF的所有备份文件都会保存在appendonlydir文件夹中,当配置保存文件的前缀是appendonly.aof时,会生成3种文件:base文件,increase文件,mainfest文件(跟踪管理这些文件。)
1.3.3 AOF工作原理
AOF工作流程如下:
缓冲区写回策略:AOF缓冲区命令写到磁盘的方式有3种
- always:同步写回,有一个写一个。
- everysec(默认):每一秒将缓冲区写一次到磁盘。
- no:操作系统自己调动。
AOF重写机制:随着AOF命令写的越来越多,导致AOF文件会变得越来越大,在一定条件下需要重写,简而言之对AOF文件瘦身。
当当前文件大于64mb,并且是上一次文件大小的1倍时触发重写。
注意:比较大小是用百分比表示的。
1.3.4 AOF优缺点
- 优点:相比于RDB,更不容易丢失数据,因为每个写操作都存入磁盘了。
- 缺点:(1)相同数据集AOF文件大小大于RDB,恢复速度慢。(2)运行效率低于RDB(可能是同步策略导致效率低,因为每秒钟需要同步一次。)
1.4 Redis持久化方式——RDB+AOF
Redis支持RDB和AOF共同持久化,开启方式如下:
aof-use-rdb-preamble yes
- 开启混合持化后:先使用RDB进行快照存储,然后使用AOF持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。这样的话,重启服务的时候会从RDB和AOF两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能。简单来说:混合持久化方式产生的文件一部分是RDB格式,一部分是AOF格式。
注:当不开启AOF时,只产生RDB备份,开启AOF时会产生RDB和AOF文件,混合持化时,产生的是AOF文件,但是其内容是部分RDB+部分AOF文件。二者共存的时候只加载AOF,因为AOF文件保存的更完整。
- RDB VS AOF
2. Redis主从复制(Replication)
2.1 什么是主从复制?为什么要主从复制?
- 主从复制:有多台服务器,分为master和slave服务器,一台master服务器可以挂载多台slave服务器,在master服务器中进行写操作,slave服务器进行读操作,减轻单台服务器带来的读写压力。当数据写入到master服务器中的时候,slave服务器同步master服务器数据的过程称之为主从复制。
- 为什么要主从复制:(1)读写分离 (2)容灾恢复 (3)数据备份 (4)扩容支持高并发
2.2 主从复制配置
info replication 查看节点之间的主从关系
replicaof masterip masterport 一般在配置文件中配置要连接的mater信息,也可以在命令行配置。配置文件中配置后重启后依然生效
slaveof masterip masterport 命令行配置要连接的master信息
slaveof no one 转为master不再挂载于任何master
假设3台主机, 1 master with 2 slaves。在master中写入了数据,在slaves中可以直接读写
2.3 主从复制过程
(1)首次连接全量复制
- 当slave连接master的时候会发送sync命令。
- master接收到sync后暂停当前所有的写操作,并将写操作缓存起来,并生成RDB快照。
- master将RDB快照发送给slave,slave收到快照后,清空自己的数据库,将接受到的数据写入自己的内存。
- master将刚才缓存的写操作发给slave,slave根据操作载入缓存。
(2)后续增量复制
master每执行一个写命令就会向slave服务器发送相同的写命令,slave服务器接收并执行收到的写命令。
(3)断点续传
master在内存缓冲区中给每个slave服务器都维护了一份同步备份日志backlog,同时master 和 slave都维护了一个复制偏移量(replication offset)和 master线程ID(master run id),每个slave服务器在跟master服务器进行同步时都会携带master run id 和 最后一次同步的复制偏移量offset,通过offset可以知道主从之间的数据不一致的情况。当连接断开时,slave服务器会重新连接上master服务器,然后请求继续复制。假如主从服务器的两个master run id相同,并且指定的偏移量offset在同步备份日志中还有效,复制就会从上次中断的点开始继续。
2.4 主从复制的其他说明
- slave只能读而不能写。
- master宕机后,slave只能等待而不能上位。
- master 宕机后重启后仍然是master
- 缺点:master宕机后没有slave上位,可能造成生产事故。
3. Redis哨兵(Sentinel)
3.1 什么是哨兵
俗称“无人值守运维”,当master宕机后,哨兵从slave中自动选举出新的master,避免master宕机后造成生产事故。
哨兵一般是集群,至少得3个哨兵,因为当master宕机后哨兵得投票选举出新的master。
哨兵一般只需要监视master,当master宕机后会选举出新master。当slave宕机后会通知哪个slave宕机了,需要手动恢复。
- 哨兵的作用
3.2 哨兵集群配置和启动
假设有服务器A B C ,可以在同一台服务器上或者不同的服务器上对sentinel.conf进行配置,
服务器可以是新的D服务器或者A B C服务器之一。
案例中设置3个哨兵,选择在3台服务器中分别配置sentinel.conf来监视master——192.168.178.128。(剩余的2台服务器ip分别是129、130)
- 在sentinel.conf中进行相关配置
daemonize yes
protected-mode no
#sentinel monitor <master-name> <ip> <redis-port> <quorum>
#master-name 自己起
#quorm 集群中至少有quorm个哨兵认为master宕机了才客观认为master宕机了
sentinel monitor mymaster 192.168.178.128 6379 2
#sentinel auth-pass <master-name> <password> 要监视的master 名称 和 密码
sentinel auth-pass mymaster 123456
- 启动哨兵sentinel
redis-sentinel sentinel.conf --sentinel
因为哨兵配置在3台服务器中,所以需要再3台服务器中分别启动。
- 模拟master宕机
手动shutdown master服务器 之前 master和slaves info replication 如下:可以看到2台slave服务器都挂载在128的master服务器下,可以通过ip和replid看出来。
手动shutdown master服务器 之后,并在 稍后 重启之前的master,发现: 哨兵选举出新的master(129服务器),并且原来的master重启后变成了slave。
3.3 哨兵选举流程
- 先确定master是否客观下线
主观下线:即单个sentinel主观认为某个服务下线(有可能是接收不到订阅,之间的网络不通等等原因)。衡量指标是服务器在[sentinel down-after-milliseconds]给定的毫秒数之内没有回应PING命令或者返回一个错误消息。
#sentinel.conf
# default 30s
sentinel down-after-milliseconds <master-name> <milliseconds>
主观下线:单个哨兵得主观下线可能存在误判,例如只是网络不通畅就造成了ping不通。所以需要至少个哨兵认为master下线了才能认为master客观下线了。
#sentinel.conf
sentinel monitor <master-name> <ip> <redis-port> <quorum>
-
各个哨兵选举领导者哨兵(Raft算法)
-
领导者哨兵进行故障转移
通知调用的客户端master发生了变化
通知其余的原slave节点,去复制Sentinel选举出来的新的master节点
即使原来的master重启了,也沦为slave
3.4 哨兵集群的优缺点
- 优点:对节点进行监控,来完成自动的故障发现与转移
- 缺点:(1)在主从切换的瞬间存在访问瞬断的情况,等待时间比较长,客户端无法访问。(2)只有一个主节点对外提供服务,没法支持很高的并发。
4. 集群(Cluster)
4.1 什么是集群
简单来说,由多台服务器组成的集合。其中由多台master和多台slave,一台master可以挂载多台slave。客户端只需要连接其中一个节点服务器即可,实现了高并发和高可用。(相比于单纯的主从复制和哨兵模式,集群并发量更高,并且容灾性更好)
4.2 集群算法——分片、槽位slot
分片:即某台服务器
槽位:存在于某台服务器上的存储空间
集群算法:当要读取或者写入某个key的时候,将其经过CRC16(循环冗余校验码 16即校验结果占16bit)哈希函数的映射后,再与16384(2的14次方)取模后,得到0-16383之间的值。一般情况下,多个分片均分16384个槽位。则将key对应到某个分片下对应的槽位中。
4.3 其他说明
- 哈希冲突:不难理解,只要key的确定的,得到的哈希映射结果是固定的,就会分配到固定的分片固定的槽位。不同的key也可能被分配到相同的分片下的相同槽位,这就属于哈希冲突,这是无法避免的,一种解决方案是:创建一个链表结构来存储多个键值对。
- 集群方便扩容和缩容:当扩容的时候,分片增加,重新分配槽位。比如扩容前有3个分片,槽位分别是0-5460、5461-10922、10923-16383,扩容后有4个分片,槽位分别是0-4095、4096-8191、8192-12287、12288-16383。扩容后会根据新的槽位分配迁移部分数据。也就是1的部分迁移到2,2的部分迁移到3…3的部分迁移到4。这并不影响key的查找和写入,因为具体的key映射结果是固定的,区别只是存储在那个分片。
- 集群有多个master,每个master有多个slave。当某个master宕机后,内部的故障转移机制会选举出新的master,并不会重新分配槽位。
- 槽位数是16384的原因:(1)正常的心跳包会携带一个节点的完整配置,它会以幂等的方式更新旧的配置,这意味着心跳包会附带当前节点的负责的哈希槽的信息。假设哈希槽采用 16384 ,则占空间 2kb(16384/8)。假设哈希槽采用 65536, 则占空间 8k(65536/8),这是令人难以接受的内存占用。(2)Redis Cluster 不太可能扩展到超过 1000 个主节点。
也就是说,65536 个固然可以确保每个主节点有足够的哈希槽,但其占用的空间太大。而且,Redis Cluster 的主节点通常不会扩展太多,16384 个哈希槽完全足够用。 - 集群在扩容和缩容期间如何保证提供服务:Redis Cluster 提供了重定向机制,两种不同的类型: (1)ASK 重定向 :可以看做是临时重定向,后续查询仍然发送到旧节点。(2) MOVED 重定向 :可以看做是永久重定向,后续查询发送到新节点。
5.Redis单线程与多线程
5.1 Redis是单线程还是多线程
这个问题不能简单的一概而论需要分Redis版本和操作类型来讨论:
Redis6之前:都是单线程,对数据的读写和IO多路复用都是单线程。
Redis6之后:引入了IO多线程(默认关闭)。之前是单线程IO多路复用同时处理多个客户端的请求。后来请过分析发现限制Redis性能的原因有:CPU、内存、IO。其中前两者一般管够,所以瓶颈就在IO,因此在Redis6引入了IO多线程,叠加多路复用,简直是快上加快。但是对数据的读写都是单线程,因此不会涉及到线程不安全的问题,也不会涉及到加锁的问题。
5.2 Redis为什么快
- 基于内存操作:运算相比于在磁盘速度更快。
- 高效数据结构:例如哈希结构对查找和插入很友好,时间复杂度只有O(1)。
- 单线程:避免了多线程切换上下文和资源竞争激烈。
- IO多路复用:多路复用能同时监听处理多个客户端的请求,尤其引入多线程IO后快上加快。
5.3 主线程和IO多线程的协作流程
6.布隆过滤器(BloomFilter)
6.1 什么是布隆过滤器
布隆过滤器由初始值为0的bit数组和多个哈希函数构成,一般用于查询某个key是否存在于集合中。
6.2 布隆过滤器原理
-
添加key
初始化bit数组为0,将要存储的key经过多个哈希函数映射,对数组长度取模得到数组下标,多个哈希函数得到多个函数下标,将这些下标置1即完成了添加。
-
查询key
注意:查询的前提是数据已经存在了bit数组中了。
将要查询的key经过多个哈希函数的映射取模后得到下标,当下标处有0的时候,肯定不存在。当下标全是1的时候,可能存在误判。
**误判原因:哈希冲突。**不同的key经过哈希映射取模后可能得到的是同样的下标。例如bit数组中存储的是康师傅,但是要查询的是康帅傅,二者经过哈希函数映射和取模后下标一样,这时就出现了误判。
6.3 优缺点和使用场景
- 优点:高效查询和插入。
- 缺点:不能删除,因为哈希冲突会存在误判。
- 应用场景:缓存穿透。垃圾邮件识别等
7. 缓存雪崩、穿透、击穿
7.1 缓存雪崩
- 什么是缓存雪崩
缓存雪崩一方面指服务器宕机,另一方面指在短时间内大量的缓存过期,大量请求短时间内落到数据库上,犹如雪崩。 - 如何解决或避免
(1)集群缓存:cluster或者主从复制+哨兵
(2)限流:对客户端的访问限制
(3)多级缓存:本地缓存+redis缓存
(4)设置缓存永不过期
7.2 缓存穿透
- 什么是缓存穿透
redis缓存中和数据库中都没有数据,大量的请求不断的打到数据库上造成压力。 - 如何解决或避免
(1)缓存无效的key:如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间
(2)布隆过滤器
7.3 缓存击穿
- 什么是缓存击穿
大量热key过期,此时缓存并不存在但是数据库中存在,大量的请求会落到数据库上。 - 如何解决或避免
(1)对热key失效时间差异化。比如秒杀场景下设置数据过期时间晚于秒杀结束时间。
(2)加锁策略:对多个用户请求同一个key,第一个用户请求后加锁,防止其他用户再请求。