01——redis

参见blog
https://mp.weixin.qq.com/s/6NobACeeKCcUy98Ikanryg

redis

Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久化的高性能键值对数据库。

redis命令大全
http://redisdoc.com/string/index.html

1. redis 与 memcached 的区别

参考Blog
https://blog.csdn.net/qq_18671415/article/details/104540628
1)存储方式
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis可以把数据保存在硬盘上,用来保证数据的持久性。

2)数据类型
Memcache的数据类型比较简单。
Redis有复杂的数据类型。

3)使用底层模型不同
它们之间底层的实现方式 以及与客户端之间通信的应用协议不一样。

4)value大小
redis最大可以达到1GB,而memcache只有1MB

2. Redis有哪些数据结构

String、Hash、List、Set、Zset。
新增加的 Bitmaps、HyperLogLogs、GEO
(避免缓存击穿的利器之BloomFilter https://juejin.cn/post/6844903982209449991)

3. redis持久化

在服务器发生宕机时,作为内存数据库Redis里的所有数据将会丢失,因此Redis提供了持久化两大利器:RDB和AOF
RDB 将数据库快照以二进制的方式保存到磁盘中。
AOF 以协议文本方式,将所有对数据库进行过写入的命令和参数记录到 AOF 文件,从而记录数据库状态。

3.1 RDB的SAVE和BGSAVE

RDB文件适合数据的容灾备份与恢复,通过RDB文件恢复数据库耗时较短,可以快速恢复数据。
RDB持久化只会周期性的保存数据,在未触发下一次存储时服务宕机,就会丢失增量数据。当数据量较大的情况下,fork子进程这个操作很消耗cpu,可能会发生长达秒级别的阻塞情况。

SAVE是阻塞式持久化,执行命令时Redis主进程把内存数据写入到RDB文件中直到创建完毕,期间Redis不能处理任何命令。

BGSAVE属于非阻塞式持久化,创建一个子进程把内存中数据写入RDB文件里同时主进程处理命令请求。
如图展示了bgsave的简单流程:
avatar

  1. bgsave的实现细节
    RDB方式的持久化是通过快照实现的,符合条件时Redis会自动将内存数据进行快照并存储在硬盘上,以BGSAVE为例,一次完整数据快照的过程:
    1. Redis使用fork函数创建子进程;
    2. 父进程继续接收并处理命令请求,子进程将内存数据写入临时文件;
    3. 子进程写入所有数据后会用临时文件替换旧RDB文件;

执行fork的时OS会使用写时拷贝策略,对子进程进行快照过程优化。

Redis在进行快照过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是任何时候RDB文件都是完整的。

我们可以通过定时备份RDB文件来实现Redis数据库备份,RDB文件是经过压缩的,占用的空间会小于内存中的数据大小。

除了自动快照还可以手动发送SAVE或BGSAVE命令让Redis执行快照。通过RDB方式实现持久化,由于RDB保存频率的限制,如果数据很重要则考虑使用AOF方式进行持久化。

  1. AOF
    在使用AOF持久化方式时,Redis会将每一个收到的写命令都通过Write函数追加到文件中。
    当开启AOF后,服务端每执行一次写操作就会把该条命令追加到一个单独的AOF缓冲区的末尾,然后把AOF缓冲区的内容写入AOF文件里,由于磁盘缓冲区的存在写入AOF文件之后,并不代表数据已经落盘了,而何时进行文件同步则是根据配置的appendfsync来进行配置:

appendfsync选项:always、everysec和no:
1.always:服务器在每执行一个事件就把AOF缓冲区的内容强制性的写入硬盘上的AOF文件里,保证了数据持久化的完整性,效率是最慢的但最安全的;

2.everysec:服务端每隔一秒才会进行一次文件同步把内存缓冲区里的AOF缓存数据真正写入AOF文件里,兼顾了效率和完整性,极端情况服务器宕机只会丢失一秒内对Redis数据库的写操作;

3.no:表示默认系统的缓存区写入磁盘的机制,不做程序强制,数据安全性和完整性差一些。

AOF比RDB文件更大,并且在存储命令的过程中增长更快,为了压缩AOF的持久化文件,Redis提供了重写机制以此来实现控制AOF文件的增长。

AOF重写实现的理论基础是这样的:
执行set hello world 50次
最后执行一次 set hello china
最终对于AOF文件而言前面50次都是无意义的,AOF重写就是将key只保存最后的状态。
重写期间的数据一致性问题

子进程在进行 AOF 重写期间, 主进程还需要继续处理命令, 而新的命令可能对现有的数据进行修改, 会出现数据库的数据和重写后的 AOF 文件中的数据不一致。

因此Redis 增加了一个 AOF 重写缓存, 这个缓存在 fork 出子进程之后开始启用, Redis 主进程在接到新的写命令之后, 除了会将这个写命令的协议内容追加到现有的 AOF 文件之外, 还会追加到这个缓存中。
avatar

当子进程完成 AOF 重写之后向父进程发送一个完成信号, 父进程在接到完成信号之后会调用信号处理函数,完成以下工作:
将 AOF 重写缓存中的内容全部写入到新 AOF 文件中
对新的 AOF 文件进行改名,覆盖原有的 AOF 文件
AOF重写的阻塞性

4. redis数据恢复

  1. Redis的数据恢复优先级
    如果只配置 AOF ,重启时加载 AOF 文件恢复数据;
    如果同时配置了 RDB 和 AOF ,启动只加载 AOF 文件恢复数据;
    如果只配置 RDB,启动将加载 dump 文件恢复数据。

  2. RDB和AOF都有各自的缺点:
    RDB是每隔一段时间持久化一次, 故障时就会丢失宕机时刻与上一次持久化之间的数据,无法保证数据完整性
    AOF存储的是指令序列, 恢复重放时要花费很长时间并且文件更大

5. redis集群

  1. 集群需要客服分片问题,也就是一致性哈希问题,常见的方案分三种
    1.客户端分片
    2.中间层分片
    3.服务端分片

  2. redis cluster基本运行原理
    avatar
    集群中Node相互八卦传播流言蜚语的内容主题,而且比较全面,既有自己的更有别人的,这么一来大家都相互传,最终信息就全面而且准确了,区别于拜占庭帝国问题,信息的可信度很高。

  3. Gossip协议的概念
    gossip 协议(gossip protocol)又称 epidemic 协议(epidemic protocol),是基于流行病传播方式的节点或者进程之间信息交换的协议。在分布式系统中被广泛使用,比如我们可以使用 gossip 协议来确保网络中所有节点的数据一样。

Gossip协议已经是P2P网络中比较成熟的协议了。Gossip协议的最大的好处是,即使集群节点的数量增加,每个节点的负载也不会增加很多,几乎是恒定的。这就允许Consul管理的集群规模能横向扩展到数千个节点。

5. redis的内存回收机制理解

5.1 回收的内存

redis占用的内存包括:
1. 存储键值对
键值对可以分为几种:带过期的、不带过期的、热点数据、冷数据
2. 本身运行消耗内存
avatar

5.2 如何删除过期的键值对

要实施对键值对的删除我们需要明白如下几点:
带过期超时的键值对存储在哪里?
如何判断带超时的键值对是否可以被删除了?
删除机制有哪些以及如何选择?

  1. 键值对的存储
    key-value都是存储在redisDb的dict中

redisDb的expires成员的类型也是dict,和键值对是一样的,本质上expires是dict的子集,expires保存的是所有带过期的键值对,称之为过期字典

  1. 键值对的过期删除判断
    判断键是否过期可删除,需要先查过期字典是否存在该值,如果存在则进一步判断过期时间戳和当前时间戳的相对大小,做出删除判断,简单的流程如图:
    avatar

  2. 键值对的删除策略

先抛开Redis来想一下可能的几种删除策略:

1.定时删除:在设置键的过期时间的同时,创建定时器,让定时器在键过期时间到来时,即刻执行键值对的删除;

2.定期删除:每隔特定的时间对数据库进行一次扫描,检测并删除其中的过期键值对;

3.惰性删除:键值对过期暂时不进行删除,至于删除的时机与键值对的使用有关,当获取键时先查看其是否过期,过期就删除,否则就保留;

三种策略都有各自的优缺点:定时删除对内存使用率有优势,但是对CPU不友好,惰性删除对内存不友好,如果某些键值对一直不被使用,那么会造成一定量的内存浪费,定期删除是定时删除和惰性删除的折中。

Reids采用的是惰性删除和定期删除的结合,一般来说可以借助最小堆来实现定时器,不过Redis的设计考虑到时间事件的有限种类和数量,使用了无序链表存储时间事件,这样如果在此基础上实现定时删除,就意味着O(N)遍历获取最近需要删除的数据。
avatar

  1. 定期删除的实现细节

  2. DEL删除键值对
    在Redis4.0之前执行del操作时如果key-value很大,那么可能导致阻塞,在新版本中引入了BIO线程以及一些新的命令,实现了del的延时懒删除,最后会有BIO线程来实现内存的清理回收。

5.2 内存淘汰机制

为了保证Redis的安全稳定运行,设置了一个max-memory的阈值,那么当内存用量到达阈值,新写入的键值对无法写入,此时就需要内存淘汰机制,在Redis的配置中有几种淘汰策略可以选择,详细如下:
noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错;

allkeys-lru:当内存不足以容纳新写入数据时,在键空间中移除最近最少使用的 key;

allkeys-random:当内存不足以容纳新写入数据时,在键空间中随机移除某个 key;

volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key;

volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key;

volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除;

后三种策略都是针对过期字典的处理,但是在过期字典为空时会noeviction一样返回写入失败,毫无策略地随机删除也不太可取,所以一般选择第二种allkeys-lru基于LRU策略进行淘汰。

6. redis数据同步机制和原理

理解持久化和数据同步的关系,需要从单点故障和高可用两个角度来分析:

6.1 单点宕机故障

avatar

作为一个高可用的缓存系统单点宕机是不允许的,因此就出现了主从架构,对主节点的数据进行多个备份,如果主节点挂点,可以立刻切换状态最好的从节点为主节点,对外提供写服务,并且其他从节点向新主节点同步数据,确保整个Redis缓存系统的高可用。

如图展示了一个一主两从读写分离的Redis系统主节点故障迁移的过程,整个过程并没有停止正常工作,大大提高了系统的高可用:

avatar

6.2 Redis系统中的CAP理论

C Consistent 一致性 连贯性
A Availability 可用性
P Partition Tolerance 分区容忍性

对于Redis数据同步而言,假设从结点和主结点在两个机架上,某时刻发生网络断开,如果此时Redis读写分离,那么从结点的数据必然无法与主继续同步数据。在这种情况下,如果继续在从结点读取数据就造成数据不一致问题,如果强制保证数据一致从结点就无法提供服务造成不可用问题,从而看出在P的影响下C和A无法兼顾。

其他几种情况就不深入了,从上面我们可以得出结论:当Redis多台机器分布在不同的网络中,如果出现网络故障,那么数据一致性和服务可用性无法兼顾,Redis系统对此必须做出选择,事实上Redis选择了可用性,或者说Redis选择了另外一种最终一致性。

6.3 Redis的最终一致性和复制

Redis选择了最终一致性,也就是不保证主从数据在任何时刻都是一致的,并且Redis主从同步默认是异步的.
同步复制和异步复制
avatar
异步复制:当客户端向主结点写了hello world,主节点写成功之后就向客户端回复OK,这样主节点和客户端的交互就完成了,之后主节点向从结点同步hello world,从结点完成之后向主节点回复OK,整个过程客户端不需要等待从结点同步完成,因此整个过程是异步实现的。

同步复制:当客户端向主结点写了hello world,主节点向从结点同步hello world,从结点完成之后向主节点回复OK,之后主节点向客户端回复OK,整个过程客户端需要等待从结点同步完成,因此整个过程是同步实现的。

1.从从复制
avatar

2.全量复制和增量复制
avatar
全量复制是从结点因为故障恢复或者新添加从结点时出现的初始化阶段的数据复制,这种复制是将主节点的数据全部同步到从结点来完成的,所以成本大但又不可避免。

增量复制是主从结点正常工作之后的每个时刻进行的数据复制方式,涓涓细流同步数据,这种同步方式又轻又快,优点确实不少,不过如果没有全量复制打下基础增量复制也没戏,所以二者不是矛盾存在而是相互依存的。

3.全量复制过程分析
Redis的全量复制过程主要分三个阶段:

快照阶段:从结点向主结点发起SYNC全量复制命令,主节点执行bgsave将内存中全部数据生成快照并发送给从结点,从结点释放旧内存载入并解析新快照,主节点同时将此阶段所产生的新的写命令存储到缓冲区。

缓冲阶段:主节点向从节点同步存储在缓冲区的操作命令,这部分命令主节点是bgsave之后到从结点载入快照这个时间段内的新增命令,需要记录要不然就出现数据丢失。

增量阶段:缓冲区同步完成之后,主节点正常向从结点同步增量操作命令,至此主从保持基本一致的步调。
note:我的理解就是,先同步发起同步命令那一刻开始,主节点的数据。在同步过程中新来的写命令缓存到缓冲区中。等到同步完之后。再开始同步缓冲区的命令。最后就主从一起同步

7. 基于Redis的分布式锁和Redlock算法

7.1 基于Redis的分布式锁简介

最初分布式锁借助于setnx和expire命令,但是这两个命令不是原子操作,如果执行setnx之后获取锁但是此时客户端挂掉,这样无法执行expire设置过期时间就导致锁一直无法被释放,因此在2.8版本中Antirez为setnx增加了参数扩展,使得setnx和expire具备原子操作性。

7.2 Redlock算法基本过程

在Redis的分布式环境中,我们假设有N个完全互相独立的Redis节点,在N个Redis实例上使用与在Redis单实例下相同方法获取锁和释放锁。

重点可能会问到
现在假设有5个Redis主节点(大于3的奇数个),这样基本保证他们不会同时都宕掉,获取锁和释放锁的过程中,客户端会执行以下操作:

1.获取当前Unix时间,以毫秒为单位
2.依次尝试从5个实例,使用相同的key和具有唯一性的value获取锁
当向Redis请求获取锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间,这样可以避免客户端死等
3.客户端使用当前时间减去开始获取锁时间就得到获取锁使用的时间。当且仅当从半数以上的Redis节点取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功
4.如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间,这个很重要
5.如果因为某些原因,获取锁失败(没有在半数以上实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁,无论Redis实例是否加锁成功,因为可能服务端响应消息丢失了但是实际成功了,毕竟多释放一次也不会有问题

缓存雪崩 Cache Avalanche

如果缓存系统故障,大量的请求无法从缓存完成数据请求,就全量汹涌冲向磁盘数据库系统,导致数据库被打死,整个系统彻底崩溃。

  1. 解决方案
    造成缓存雪崩的主要原因是缓存系统不够高可用,因此提高缓存系统的稳定性和可用性十分必要,比如对于使用Redis作为缓存的系统而言可以使用哨兵机制、集群化、持久化等来提高缓存系统的HA。

除了保证缓存系统的HA之外,服务本身也需要支持降级,可以借助比如Hystrix来实现服务的熔断、降级、限流来降低出现雪崩时的故障程度。
avatar

缓存穿透 Cache Penetration

在高并发系统中缓存穿透,如果一个req需要请求的数据在缓存中没有,这时业务线程就会访问磁盘数据库系统,然而磁盘数据库也没有这个数据,无奈业务线程只能白白处理一圈。

  1. 缓存穿透解决方案
    转换为查找问题,类似于在海量数据中查找某个key是否存在,考虑空间复杂度和时间复杂度,一般选用布隆过滤器来实现。
    布隆过滤器是个好东西,有非常多的用途,包括:垃圾邮件识别、搜索蜘蛛爬虫url去重等,主要借助K个哈希函数和一个超大的bit数组来降低哈希冲突本身带来的误判,从而提高识别准确性。

布隆过滤器也存在一定的误判,假如判断存在可能不一定存在,但是假如判断不存在就一定不存在,因此刚好用在解决缓存穿透的key查找场景,事实上很多系统都是基于布隆过滤器来解决缓存穿透问题的。

缓存击穿 Hotspot Invalid

avatar
缓存击穿是这样一种情况:

由于缓存系统中的热点数据都有过期时间,如果没有过期时间就造成了主存和缓存的数据不一致,因此过期时间一般都不会太长。

设想某时刻一批热点数据同时在缓存系统中过期失效,那么这部分数据就都将请求磁盘数据库系统。

  1. 解决方案

  2. 在设置热点数据过期时间时尽量分散,比如设置100ms的基础值,在此基础上正负浮动10ms,从而降低相同时刻出现CacheMiss的key的数量。

  3. 另外一种做法是多线程加锁,其中第一个线程发现CacheMiss之后进行加锁,再从数据库获取内容之后写到缓存中,其他线程获取锁失败则阻塞数ms之后再进行缓存读取,这样可以降低访问数据数据库的线程数,需要注意在单机和集群需要使用不同的锁,集群环境使用分布式锁来实现,但是由于锁的存在也会影响并发效率。

  4. 一种方法是在业务层对使用的热点数据查看是否即将过期,如果即将过期则去数据库获取最新数据进行更新并延长该热点key在缓存系统中的时间,从而避免后面的过期CacheMiss,相当于把事情提前解决了。

https://baijiahao.baidu.com/s?id=1627532543449682950&wfr=spider&for=pc

posted @ 2021-04-18 17:57  codeDJH  阅读(50)  评论(0编辑  收藏  举报