Redis知识

目录

AOF日志

三种写回策略

AOF重写机制

AOF后台重写

RDB快照

RDB和AOF混合持久化

Redis过期删除和内存淘汰策略:

过期删除策略:

内存淘汰策略(解决内存过大问题):

LRU和LFU以及他们在Redis里的实现

主从复制

哨兵模式

缓存

缓存雪崩

缓存击穿

缓存穿透

数据库和缓存一致性问题


AOF日志

恢复缓存数据,只保存写操作命令。先执行写操作命令,再将该命令记录到AOF日志里。

优点:避免语法错误等造成的额外开销;不会阻塞当前写操作命令的执行。

缺点:如果Redis还没来得及将命令写入硬盘,服务器就宕机了,会有丢失的风险;给下一个命令带来阻塞风险,因为将执行命令和写入AOF日志都是在主进程完成的。如果日志内容写入硬盘时,服务器的IO压力太大,会导致硬盘读写速度慢。进而阻塞,导致命令无法执行。

三种写回策略

Always:每次写操作命令执行后,都将AOF日志写回硬盘

Everysec:每秒,写操作命令执行后,先将命令写入AOF日志的内核缓冲区,每隔一秒将缓冲区的数据写回硬盘

No:每次操作命令执行后,将命令写入到AOF日志的内核缓冲区,由操作系统决定何时写回硬盘

实现:如果想要应用程序向文件写入数据后,能立马将数据同步到硬盘,立即调用fsync()函数

AOF重写机制

当AOF文件大小超过设定的阙值后,Redis就会启动AOF重写机制,压缩AOF文件。再重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到新的AOF文件中,等所有记录完后,就将新的AOF替换掉原来的AOF文件。

优点:尽管某个键值对被多条写命令反复修改,最终也只需要根据这个「键值对」当前的最新状态,然后用一条命令去记录键值对。

为什么重写 AOF 的时候,不直接复用现有的 AOF 文件,而是先写到新的 AOF 文件再覆盖过去?因为如果 AOF 重写过程中失败了,现有的 AOF 文件就会造成污染,可能无法用于恢复使用。

AOF后台重写

由后台子进程 bgrewriteaof 来完成,好处:主进程可以继续处理命令,避免阻塞;线程会共享内存,如果修改共享内存数据时,还要加锁,会降低性能。进程虽然也共享内存数据,但是这个内存只能以只读的方式。

后面再补充........

RDB快照

记录某一个瞬间的内存数据,是实际数据,Redis恢复数据时,只需将RDB文件读入内存即可。

Redis提供了两个命令来生成RDB文件,save 和 bgsave,save会在主线程生成RDB文件,如果写入时间过长,会阻塞主线程;bgsave会创建一个子进程来生成RDB文件,避免线程阻塞。

缺点:全量快照,每次执行快照,都是将内存中的所有数据都记录到磁盘中。如果执行快照频率过高,可能会对Redis性能产生影响。频率过低,服务器故障时,丢失的数据越多。

写入RDB文件:执行bgsave命令,会通过fork()创建一个子进程(复制父进程的页表,指向的物理内存还是一个),父子进程共享内存数据,当主线程要修改共享数据里的某一块时,会用到写时复制技术,这块数据就会被复制一份,然后主进程在这个数据副本进行修改操作。子进程可以继续把原来的数据写入到RDB文件。那末这次主进程程修改的部分要等到下次子进程将其写入到RDB文件了。

RDB和AOF混合持久化

混合持久化工作在AOF日志重写过程,当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主进程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主进程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。

Redis过期删除和内存淘汰策略:

过期删除策略:

当对一个key设置过期时间之后,Redis会将其加入到过期字典中,Redis里使用的是惰性删除和定期删除相结合的方式,惰性删除是当查询key的时候,先判断是否在过期字典中存在,如果存在,则判断过期时间和当前时间的大小,如果还未过期,返回查询的结果,如果过期,就删除。定期删除每过一段时间在过期字典中随机抽查一些key,判断是否过期,如果过期的key占抽查的key25%以上,就会继续抽查,直到小于25%。然后再过一段时间之后继续抽查。

内存淘汰策略(解决内存过大问题):

当Redis的内存超过最大运行内存时,会触发内存淘汰策略,第一种不进行数据淘汰策略,是Redis3.0版本之后的默认内存淘汰策略,当超过最大内存限制之后,不能再添加key,但是可以查询。

两大种:在设置了过期时间的数据中进行淘汰,1.随机淘汰。2.优先淘汰更早的键值对。3.LRU淘汰算法(最近最少使用)。4.LFU淘汰算法(最近最不常用)。

在所有数据种进行淘汰,1.随机淘汰。2.LRU。3.LFU

LRU和LFU以及他们在Redis里的实现

LRU算法是基于链表的结构,链表中的元素按照操作顺序从后往前排列,最新的操作会被移动到链表头部,淘汰时只需要删除链表尾部的元素。

因为,传统LRU算法需要涉及很多链表的移动操作,会耗时间,影响Redis缓存的性能,所以Redis用的是近似的LRU算法。是在Redis的对象结构体中添加一个额外字段,用于记录此数据的最后一次访问时间。当Redis进行内存淘汰时,会随机采样来淘汰数据,淘汰最久没使用的那个。缺点 无法解决缓存污染问题,比如某一次读取了大量的数据,一下就把内存占满了,那之前的数据就会被清除掉,而这个数据如果只用一次,就会在Redis留存很长的时间,会造成缓存污染。

为了解决问题,Redis 4.0之后引入了LFU算法(最近最不常用),根据访问次数来淘汰数据。Redis里用的是,在Redis的对象结构体中添加了数据的访问频次字段lru,lru 24bits,高16bit存储ldt,记录访问时间戳,低8bit存储 logc,记录访问频次。然后根据这两个值来判断是否淘汰。

主从复制

为了解决单点故障的问题,Redis提供了主从复制模式,并且主从服务器采用的是读写分离方式,主服务器可以读写,当发生写操作时自动把写操作同步给从服务器,从服务器一般是只读,并且接收主服务器同步过来的写操作命令,然后执行命令。

第一次同步:

1.建立连接(replicaof 命令),确定主从。主服务器会返回FULLRSYNC响应命令,表示采用全量复制,把所有数据都同步给从服务器。

2.将主服务器同步数据给从服务器。主服务器生成RDB快照(异步操作,不会影响Redis处理命令)并发送给从服务器,从服务器清空所有数据然后载入RDB文件。但是同步期间发生的操作命令,主服务器会将命令写入到 replication buffer 中,会通过第三步发送给从服务器

3.主服务器发送新写操作给从服务器,将replication buffer中的写操作命令发送给从服务器。

命令传播:TCP连接,长连接。传输写操作命令

分摊主服务器压力:服务器B可以是服务器A的从服务器,也可以是服务器C的主服务器。这样减少了主服务器生成RDB文件和传输文件的消耗。

增量复制:由于网络问题,如果主从服务器间的网络连接断开,无法保持主从一致,Redis采用了增量复制的方式继续同步,只会把网络断开期间主服务器收到的写操作命令同步给从服务器。

首先从服务器会发送自己读的位置,然后主服务器会在 环形缓冲区内查找位置,若找到,主服务器根据当前写的位置和从服务器读的位置,会将之间的增量写入到replication buffer ,然后发送给从服务器。若未找到,主服务器会采用全量同步方式。

哨兵模式

主从架构中,当主节点挂了,那就没有主节点来执行客户端写操作的请求,也不能给从节点进行数据同步了。哨兵机制能实现主从节点故障转移(监控,选主,通知),会检测主节点是否存活,如果发现挂了,就会选举一个从节点切换为主节点,并且把主节点的相关信息通知给各从节点和客户端。

监控:主观下线,哨兵每隔一秒ping所有主从节点,如果有主从节点没有在规定的时间响应哨兵的ping命令,哨兵就会标记为主观下线,此时会通知所有的哨兵,其他哨兵根据自身和主节点的网络状况来投赞成或拒绝票。如果赞成票数到达要求后 quorum(建议为哨兵个数的二分之一加一),此时会被哨兵标记为客观下线

选哨兵进行主从故障转移:哨兵标记主节点客观下线后,该哨兵就会发起投票,其他哨兵投赞成或拒绝票。拿到半数以上赞成并且大于等于配置文件中quorum值,才能做leader。

选主:第一步:在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点,选择的规则:

  • 过滤掉已经离线的从节点;
  • 过滤掉历史网络连接状态不好的从节点;
  • 将剩下的从节点,进行三轮考察:优先级、复制进度、ID 号。在每一轮考察过程中,如果找到了一个胜出的从节点,就将其作为新主节点。

通知:让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」;

将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;

继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点;

缓存

用户请求时,利用Redis做MySQL的缓存层,Redis是内存数据库,相当于数据缓存在内存中。虽然速度比磁盘提升几个等级,但引入缓存层后,会出现缓存穿透、击穿、雪崩问题。

缓存雪崩

大量缓存数据在同一时间过期,或者Redis故障宕机,此时如果有大量的用户请求,都无法在Redis中处理,于是全部请求都直接访问数据库,导致数据库宕机,造成系统崩溃。

解决办法:

针对大量缓存数据在同一时间过期:

1.随机设置过期时间,保证数据不会在同一个时间过期。

2.添加互斥锁,如果发现访问的数据不在Redis里,就加一个互斥锁(设置过期时间。防止出现意外不释放锁,其他请求拿不到锁),保证同一时间内只有一个请求来构建缓 存,当缓存构建完成后,再释放。

Redis故障宕机

1.通过主从节点的方式构建Redis集群。当主节点宕机,可以通过哨兵选主,避免了宕机带来的问题。

2.启动服务熔断(限流。只能少部分请求)机制,暂停业务应用对缓存服务的访问,直接返回错误,不用再继续访问数据库,降低了对数据库访问的压力,等Redis恢复正常后,再允许业务访问。

缓存击穿

热点数据过期了,但是大量请求访问这个热点数据。缓存中没了,就要去数据库查询。数据库很容易发生崩溃。

解决方法:

1.添加互斥锁

2.不给热点数据添加过期时间。或者在热点数据将要过期前,提前通知后台更新缓存以及重新设置过期时间。

缓存穿透

请求到来时,访问缓存没有数据,访问数据库也没有数据,无法构建缓存数据。大量的请求,导致数据库压力激增。

解决方法:

1.非法请求的限制。

2.发现缓存穿透现象时,可以针对查询的数据,在缓存中设置一个空值或默认值。

3.布隆过滤器。在写入数据时,先在布隆过滤器中做标记。等请求来时,判断是否标记过。

数据库和缓存一致性问题

并发情况下,先更新数据库,再删除缓存可以解决数据库和缓存一致性问题。但是如果第二步操作没有成功执行,那末还是没作用。

解决办法:

1.给缓存设置较短的过期时间,勉强可以解决一些问题。

2.利用消息队列,将第二个操作加入到消息队列。如果删除缓存失败,那就可以从消息队列中重新读取数据,然后再次删除缓存。这个就是重试机制。

你可能感兴趣的:(redis,数据库,缓存)