1.Redis的过期键的删除策略
(1.1)惰性过期:访问到了才去删除,没访问到就不删除,对CPU非常友好,因为只有在访问到这个键才会去判断是否过期,如果没有访问到是不会去判断的,及大幅度的减少CPU的资源开销,但是对内存非常不友好,极端情况下如果存在大量过期键,因为没有访问到所以一直存在内存中,这会占用内存大量的空间。
(1.2)定时过期:每个key都会有一个定时器,过期的时候定时器会把key给删除掉,这种方案对CPU不友好。
(1.3)定期过期:每隔一段时间就会去扫描一次,扫描一定数量的数据库的expires字典中一定数量的key,然后清除过期的key,这种方案就是一个比较折中的一个方案,通过调整扫描的间隔和每次扫描的耗时来实现一个CPU和内存资源最大平很的一个效果。
2.缓存雪崩,缓存击穿,缓存穿透
(1)缓存雪崩:指的就是在同一时间内大面积缓存中的数据失效,导致所有的访问都直接到达数据库,造成数据库短时间内被大量访问而崩掉。或者缓存重启了。
(1.1)解决方案:缓存的过期时间设置随机,防止同一时间发生大面积失效的情况发生,互斥锁,缓存预热,
(2)缓存穿透:指缓存和数据库中都没有数据,导致所有的请求都直接到数据库中,造成数据库压力剧增从而崩掉,一般是来自恶意攻击
(2.1)解决方案:做一个用户鉴权,以及id校验。或者是从缓存中你取不到的数据,在数据库中也没有取到,这个时候可以在业务层写一个将key-value设置成一个key-null的一个形式然后把缓存的一个有效时间给他设置一下,从而可以防止用户恶意攻击。还有一个就是布隆过滤器,这个布隆过滤器就是一个拦截的作用,你把所有可能存在的数据哈希都放在一个足够大的bitmap中,一个一定不存在的书数据就会被拦截掉了。
(3)缓存击穿:指的就是缓存中没有数据库中有
(3.1)解决方案:就是设置热点数据永远不过期,但是要去维护。或者就是加互斥锁
3.Redis的持久化机制
主要是分三种机制、第一种:RDB、第二种:AOF、第三种:混合持久
(1.1)RDB:
Redis DataBase将某一个时间段的内存快照(Snapshot),以二进制的形式将数据写入磁盘中。比如就是早上八点钟我们上班他就会吧八点钟之前的这个数据进行一个持久化8.01分的数据他是不会进行一个持久化。RDB持久化操作是需要触发的以下是触发的详情。
(1.2)RDB两种触发方式分别是手动触发、自动触发
(1.2.1)手动触发:
-
save命令:使Redis进入阻塞状态,知道RDB持久化操作完成才会响应其他客户端发来的命令,生产环境慎用。
-
bgsave命令:(需要了解的前置知识),Reids的底层是一个单线程但是他会fork出子进程来处理业务,这里就是用的fork , (正文) fork出一个子进程来进行持久化,主进程只在fork过程中会有短暂的阻塞,子进程创建之后,主进程就可以响应客户端的请求了,子进程创建不代表持久化操作已经完成了,由于主进程并没有阻塞他的数据是一直在变的,fork持久化的时候是会出现脏数据的?这里的脏数据是不会出现的因为redis的这个父子进程是一块共享的内存区域,当主进程接收到新的响应的时候,主进程会将内存区域的数据copy一份出来进行一个修改操作,等子进程持久化完成之后,这份拷贝的数据在写回去,这就是写时拷贝。这样就可以保证持久化的数据的时间准确性。
(1.3)自动操作
(1.3.1)save m n:在m秒内,如果有n个键发生改变,则他会自动触发持久化,通过之前说到的bgsave命令执行这里是指触发save m n 只是条件,条件满足就会触发bgsave,如果设置多个规则只需要满足其中一个就会触发的话,在配置文件中有默认的一个配置根据情况来决定。
(1.3.2)flushall:用于清空redis所有的数据库, 一般情况我们在使用redis的时候默认是0号数据库,如果执行这个命令数据库的数据会被清空,同时也会清空RDB文件,还会生成一个dump.rdb里面的内容是空,也相当于是个持久化,但实际上这就是一个清空操作。慎用,慎用,慎用,慎用,慎用,慎用 但是还是有补救措施的,在APF会讲。
(1.3.3)主从同步:全量同步时会自动触发basave命令,生成rdb发送给从节点。(解释) redis中我们做了主从架构的话,我们从服务器是需要去主服务器里面copy数据的因为主服务器接收写命令,如果从服务器去进行全量同步会触发bgsave命令,若果是增量同步是不会触发的。
优点:
1.整个Redis中的数据库质包含一个文件dump.rdb,方便持久化 (解释) 在进行持久化操作的时候会先生成一个dump.rdb文件这个文件名有点区别 等持久化完成之后,会把原来的dump.rdb文件给删除掉,然后将新生成的这个文件给重命名过去,整个系统中只会有一个dump.rdb文件。
2.容灾性好,方便备份
3.性能最大化,因为前面说到了bgsave命令理解了bgsave命令的同学应该知道为什么性能是最好的。因为他是一个主进程和一个子进程两个进程来完成不同的工作一个可以完成持久化一个可以进行响应,主进程不会进行任何IO操作,保证了redis的高性能。
4. 相对于数据量很大的时候,比AOF的启动效率要高(因为RDB文件他就是一个快照文件所以他的一个恢复速度要比AOF要快。比如我们redis挂了这个时候,那我内存中的数据丢了,这个时候就要根据持久化文件来恢复)
缺点
1. 前面也说到了他是某一个时间段进行持久化操作,如果持久化之间的时间中redis发生了故障,那么会导致数据的一个丢失 (这种方式适合在数据要求不严的时候使用)
**2.**前面说到了bgsave会阻塞主进程一下,如果数据量较大的时候,可能就会导致整个服务器暂停一会儿,会占用到CPU。
(2)AOF
介绍: AOF主要就是一个Append Only File 以日志的形式记录服务器所处理的每一个写、删除操作、更新,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录,调操作系统命令进程刷盘
1. 所有的写命令都会追加到AOF缓冲区中。
2. AOF根据缓冲区根据对应的策略向硬盘进行同步操作。
3. 随着AOF的文件越来越大,需要定期对AOF文件进行重写操作,达到压缩的目的。
4. 当Redis重启时,可以加载AOF文件进行数据恢复。
同步策略:
- 每秒同步:异步完成的,效率非常高,但是系统出现宕机现象,那么这一秒钟之内修改的数据都会丢失
- 每修改同步:同步持久化,每次发生的数据变化都会被立即记录到磁盘中,最多丢一条数据。
- **不同步:**由操作系统控制,可能会丢失很多数据
- 优点
1.数据安全
2.通过append模式写文件,即便中途服务器宕机也不会破坏已经存在的内容,可以通过redis-check-aof工具解决数据一致性问题。这里的数据一致性非高并发下产生的数据一致性
3.AOF机制得到rewrite模式,定期对AOF文件进行重写,以达到压缩的目的 - 缺点:
1.AOF的文件要比RDB文件大,因为RDB只是一个快照文件,而AOF他是追加命令所以会有很多命令。而且恢复速度慢。
2.数据量大的时候,比rdb启动效率低,和上面一样
3.运行效率没有RDB高。
混合持久化的实现原理、优缺点
优点:结合 RDB 和 AOF 的优点, 更快的重写和恢复。
缺点:AOF 文件里面的 RDB 部分不再是 AOF 格式,可读性差。
最后给大家总结了一个服务启动加时加载持久化数据的一个流程图
4.Redis主从复制的核心原理
如果能理解以下这张图就可以不用看后续的讲解,理解不了往后学习即可。
在我们redis中最常见的一个架构就是主从架构。Master&Slave Master节点可以进行读写操作,那Slave节点只能进行读那他的数据哪儿来,从Master节点读取过来,所谓的主从复制就是Slave拷贝Master节点的数据。通过以下两点来拷贝
- 全量复制
(1) 主节点通过basave命令fork出子进程进行RDB持久化操作,该过程非常消耗资源。
(**2)**主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都是会带来很大的网络开销 因为这个dump文件是我们的全量数据的一个快照
**(3)**从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令,如果从节点执行bgrewriteof(redis中一种持久化机制),也会带来额外的消耗 - 部分复制(增量复制):
- 简单理解就是我每次复制数据的时候我只复制我上次复制之新产生的数据
(1)复制一个偏移量: 执行复制的双方,当我们的从节点去复制时候首先会复制一份偏移量,主从节点分别会维护一个复制偏移量(offset)
(2)复制积压缓冲区: 主节点内部维护了一个固定长度(这个长度可以指定,也可以临时更改)的先进先出的队列作为复制积压缓冲区,当主从节点offset的差距过大并且超过了缓冲区长度的时候,将无法执行部分复制,只能全量复制,也就是说他会判断你的offset的长度是否超过了缓冲区的长度来区分是执行什么复制。
**(3)服务器运行ID(runid):**每个Redis节点,都会有一个运行ID,运行ID由节点在启动的时候自动生成,主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来。当节点挂掉的重连的时候,就是根据这个运行ID来判断同步的进度: - 如果从节点保存的runID与主节点的runid相同,就说明主从节点之前是同步过的,那么这个主节点会尝试使用部分复制,(因为之前已经是在运行的过程中断掉的所以会有已经复制完成的数据所以会尝试使用部分辅助)能不能复制还是要看这个offset和复制积压缓冲区的情况来决定。
- 如果从节点保存的runid与主节点现在的runid不同,就说明从节点在断线前的Redis节点不是当前的主节点,只能进行一个全量复制。
- 具体的过程原理在上面那张图中。
5.如何保存数据库与缓存的一致性
(1)先操作数据库与先操作缓存
假如有两个并发的请求,一个读一个写,“写请求”先更新数据库成功后再去失效缓存,读请求过来直接查询缓存并且查询缓存成功了,但是“写请求”是有两部操作“读请求”是只有一步操作的,那这个期间“读请求”是会读到脏数据的,所以这种方法也是会产生脏数据
(2)先操作缓存
假如有两个并发请求,还是一读一写,写请求去失效缓存并且成功,“读请求” 去查缓存缓存没有命中(因为是并发,所以读写操作的先后不确定这里就先说读在后)这个时候 “写请求” 去更新数据库并且更新成功了,然后 “读请求” 去查数据库并且查到了数据,然后写入缓存,这个时候是正常的,假如我们 “读请求” 在 “写请求” 之前去查询数据库然后把数据写入缓存中了,这个时候就会产生脏数据,因为 “写请求” 并没有把最新的数据更新到数据库中,所以 “读请求” 还是读到的老数据
(3)失效缓存与更新缓存
(3.1)更新缓存
假如有两个并发的写请求,“写A”和“写B” 流程是这样的,写请求A去更新数据库然后更新成功了,这个时候 写请求B 也去更新了数据库,然后 写请求A 更新缓存 写请求B 也更新了缓存这个时候我们的数据一致性问题就有出来了,到底谁更新的数据是正确的呢,还有很多种可能就不详细说了。
(3.2)失效缓存
还是一样 两个并发写请求分别是AB,写请求A来更新数据库并且成功了,这个时候 写请求B 来更新数据库并且成功了又去失效缓存并且成功了,这个时候 写请求A 去做失效缓存并且也成功了。由于是删除缓存,所以不存在数据库不一致的问题但是还是会存在脏数据的问题
终极方案:将访问操作串行化或者引入MQ事务消息
1.先删缓存,将更新数据库的操作放进有序队列中
2.从缓存查不到的查询操作,都进入有序队列
3.引入失误必然会带来新能上的影响
会面临的问题:
.读请求积压,大量超市,导致数据库的压力增加产生限流、熔断,(可以将队列水平拆分,不同业务在不同队列中,提高并行度)
以上的数据库一致性的问题,因为画的图没有保存所以就没法放上来了。