Redis相关学习

  • reids的底层实现:
    • 基础的数据类型:(分别的底层结构实现和具体的应用场景)
      • string
        • 结构:字符串
          • 如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度大于 39 (44)字节, 那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值, 并将对象的编码设置为 raw 。
            • SDS 与C字符串的区别
              • 常数复杂度获取字符串长度:当取字符串长度时需要遍历整个字符串,对字符串进行计数直到为空的字符串为值,这个操作的复杂度为O(N)SDS简单动态字符的结构len属性就已存储了SDS长度,所以取一个SDS的长度为O(1)
              • 杜绝绶冲区溢出:当SDS API需要对对SDS进行修改时,API会先检查SDS的空间是否满足需要,如果不满足则会自动扩展至执行修改所需的大小(属性len增加),然后才执行实际的修改操作
              • 减少修改字符串时带来的内存重分配次数:空间预分配和惰性空间释放两种优化策略
                • 空间预留分配:对SDS进行修改时对SDS进行空间扩展的同时,还会对SDS分配额外的未使用的空间
                  • 分配公式:
                  • SDS的属性小于1MB 将分配同等大小的free属性 例:SDS的len将变成13字节,那么程序也会分配13字节的未使用空间。SDS组数的实际长度:13+13+1 = 27(未使用+已使用+1)
                  • SDS的属性大小1MB 如果SDS的长度大于1MB将分配1MB的未使用空间,例SDS修改之后SDS的len将变成30MB,那么会分配1MB的未使用空间。SDS数组的实际长充为:30MB+1MB+1byte
                • 惰性空间释放:
                  • SDS的长度缩短,内存分配的空间不会发生变化,SDS的惰性空间策略避免了内存重新分配操作,并为将来的可能的增长操作提供了优化,API也提供了释放未使空间,所以不用担心空间的浪费
              • 二进制安全
                • C字符串除了字符串的未尾之外,字符串里面不能包含空字符,否则最先被程序读入的空间将被误认为是字符串结尾,这些限制了C字符串只能保存文本数
                • SDS则没有这种限制,可以保存文本或者二进制数据
            • embstr编码是由redIsObject和sdshdr组成,redIsObject占用16字节,如果buf长度是39个字节,那么sdshdr就是8+39+1=48个字节,那么embstr就是64字节,而redis采用的是jemalloc内存分配器,可以分配8,16,32,64字节等大小的内存,而sdshdr最小分配8+8+1=17字节,那么embstr最小就是33字节,需要分配64字节。所以对于redis来说小于等于39字节的字符串采用embstr编码,大于则用raw编码。
              • embstr好处:
              • embstr编码是专门用于保存短字符串的一种优化编码方式,跟正常的字符编码相比,字符编码会调用两次内存分配函数来分别创建redisObject和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中一次包含redisObject和sdshdr两个结构。
        • 应用:
          • 缓存功能:字符串最经典的使用场景,redis最为缓存层,Mysql作为储存层,绝大部分请求数据都是redis中获取,由于redis具有支撑高并发特性,所以缓存通常能起到加速读写和降低 后端压力的作用。
          • 计数器:许多运用都会使用redis作为计数的基础工具,他可以实现快速计数、查询缓存的功能,同时数据可以一步落地到其他的数据源。如:视频播放数系统就是使用redis作为视频播放数计数的基础组件。
          • 共享session:出于负载均衡的考虑,分布式服务会将用户信息的访问均衡到不同服务器上,用户刷新一次访问可能会需要重新登录,为避免这个问题可以用redis将用户session集中管理,在这种模式下只要保证redis的高可用和扩展性的,每次获取用户更新或查询登录信息都直接从redis中集中获取。
          • 限速:处于安全考虑,每次进行登录时让用户输入手机验证码,为了短信接口不被频繁访问,会限制用户每分钟获取验证码的频率。
      • map
        • 结构:hashmap:哈希对象的底层实现可以是ziplist或者hashtable。ziplist中的哈希对象是按照key1,value1,key2,value2这样的顺序存放来存储的。当对象数目不多且内容不大时,这种方式效率是很高的。
        • 应用:哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。所以常常用于**用户信息**等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的,而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而redis去模拟关系型复杂查询开发困难,维护成本高。
      • list
        • 结构:它可以充当栈和队列的角色:列表对象的编码可以是ziplist或者linkedlist。ziplist是一种压缩链表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储。但当数据量过大时就ziplist就不是那么好用了。因为为了保证他存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。如下图所示,对象结构中ptr所指向的就是一个ziplist。整个ziplist只需要malloc一次,它们在内存中是一块连续的区域。
          • lpush+lpop=Stack(栈)
          • lpush+rpop=Queue(队列)
          • lpush+ltrim=Capped Collection(有限集合)
          • lpush+brpop=Message Queue(消息队列)
        • 应用:最近来访;粉丝、关注列表
      • set
        • 结构:hashmap (value=null)
        • 应用:
          • 标签(tag):集合类型比较典型的使用场景,如一个用户对娱乐、体育比较感兴趣,另一个可能对新闻感兴趣,这些兴趣就是标签,有了这些数据就可以得到同一标签的人,以及用户的共同爱好的标签,这些数据对于用户体验以及曾强用户粘度比较重要。
      • zset
        • 结构:
          • 有序集合的编码可能两种,一种是ziplist,另一种是skiplist与dict的结合。
          • ziplist作为集合和作为哈希对象是一样的,member和score顺序存放。按照score从小到大顺序排列。它的结构不再复述。
          • skiplist是一种跳跃表,它实现了有序集合中的快速查找,在大多数情况下它的速度都可以和平衡树差不多。但它的实现比较简单,可以作为平衡树的替代品。它的结构比较特殊。
        • 应用:
          • 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面: 按照时间、按照播放量、按照获得的赞数等。
    • 持久化方案
      • 持久化

        • RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是调用系统的fork()函数创建一个与当前进程一模一样的一个子进程(所有数据,变量、环境变量、程序计数器等)进行持久化操作,先将数据集写入临时文件,待持久化结束,再用临时文件替换上次的持久化文件,用二进制压缩存储。主进程不需要任何io操作,提高性能(dump文件)
          • 什么时候出发fork子进程触发持久化:
            • 在shoutdown,如果没有开其aof,会触发
            • 配置文件中默认的快照配置
              • save 900 1: 900秒内,出现一次增删改操作会触发
              • save 300 10:300秒内,出现10次增删改
              • save 60 10000:60秒内,10000次增删改
              • save“”:关闭
            • 手动执行sava:只是保存,主进程进行持久化,其他请求阻塞;bgsave:在后台进行常规fork进行持久化
        • AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作命令,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。在重新启动之后,会照着日志文件重新运行一遍命令完成持久化
          • 持久化文件 :apppendonly.aof
          • 触发机制:
            • no:操作系统数据缓存同步到磁盘(快,持久化没保证)
            • always:同步持久化,每次发生数据变更,都会记录日志(慢。安全)
            • everysec:每秒同步一次(默认,很快,但可能会丢失1秒钟以内的数据)
          • aof重写机制:如果日志过大,Redis可以自动启用rewrite机制。Aof重写不需要对原有的aof文件进行任何操作,而是通过当前数据库的状态,直接替换掉多余或者重复操作。
            • 1、fork出一个子进程进行AOF重写,主进程可以继续处理命令请求;子进程带有主进程的数据副本,使用子进程而不是线程,可以避免在锁的情况下,保证数据的安全性。
            • 2、Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis服务器主进程在执行完写命令之后,会同时将这个写命令追加到AOF缓冲区和AOF重写缓冲区;AOF缓冲区的内容会定期被写入和同步到AOF文件中,对现有的AOF文件的处理工作会正常进行从创建子进程开始,服务器执行的所有写操作都会被记录到AOF重写缓冲区中;
            • 3、将AOF重写缓存中的内容全部写入到新的AOF文件中;这个时候新的AOF文件所保存的数据库状态和服务器当前的数据库状态一致;对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换
            • 触发机制(配置):最大AOF文件达到xmb就触发重写机制,如果正在率为100%,那么下一次从写就是2xmb

          • redis4.0后的混合持久化机制:对重复多余操作进行改写合并,然后存储到dump.rdb中
        • RDB和AOF:RDB由于AOF,如果两种机制共存,以Aof为主;一般需要同时开启,优先使用aof持久化机制。
          • RDB存在哪些优势呢?
            • 1). 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
            • 2). 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
            • 3). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
            • 4). 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。
          • RDB又存在哪些劣势呢?
            • 1). 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
            • 2). 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
          • AOF的优势有哪些呢?
            • 1). 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。
            • 2). 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
            • 3). 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
            • 4). AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。
          • AOF的劣势有哪些呢?
            • 1). 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
            • 2). 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。
            • 二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。
        • 常用配置
          • RDB持久化配置
            • Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:
              • save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
              • save 300 10            #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
              • save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
          • AOF持久化配置
            • 在Redis的配置文件中存在三种同步方式,它们分别是:
              • appendfsync always     #每次有数据修改发生时都会写入AOF文件。
              • appendfsync everysec  #每秒钟同步一次,该策略为AOF的缺省策略。
              • appendfsync no          #从不同步。高效但是数据不会被持久化。
      • Redis集群作用
        • (1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
        • (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
        • (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
        • (4) 尽量避免在压力很大的主库上增加从库
        • (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
        • 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
      • Redis 常见的性能问题都有哪些?如何解决?
        • 1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
        • 2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
        • 3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
        • 4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内
      • redis 最适合的场景
        • (1)、会话缓存(Session Cache)
          • 最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
          • 幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
        • (2)、全页缓存(FPC)
          • 除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
          • 再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
          • 此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
        • (3)、队列
          • Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
          • 如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。
        • (4),排行榜/计数器
          • Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
          • 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
          • ZRANGE user_scores 0 10 WITHSCORES
          • Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。
        • (5)、发布/订阅
          • 最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。
          • Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。
    • 使用redis实现分布式锁的设计
      • 分布式环境下redis怎么保证线程安全?
        • 利用redis 实现分布式锁,使用setnx命令(在key不存在时,创建并设置value 返回1,key存在时,会反回0)来获取锁,在业务逻辑中,我们可以通过这样的方案来操作
        • 可以看到,我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:
          • 第一个为key,我们使用key来当锁,因为key是唯一的。
          • 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
          • 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
          • 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
          • 第五个为time,与第四个参数相呼应,代表key的过期时间。
          • 总的来说,执行上面的set()方法就只会导致两种结果:1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。
        • 删除:首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)
    • 过期时间的删除和内存淘汰策略
      • 过期策略:
        • 我们set key的时候,都可以给一个expire time,就是过期时间,指定这个key比如说只能存活1个小时,我们自己可以指定缓存到期就失效。如果假设你设置一个一批key只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?答案是:定期删除+惰性删除;所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。但是,定期删除可能会导致很多过期key到了时间并没有被删除掉,所以就得靠惰性删除了。
        • 这就是说,在你get某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。并不是key到时间就被删除掉,而是你查询这个key的时候,redis再懒惰的检查一下
        • 如果大量过期key堆积在内存里,导致redis内存块耗尽了,怎么办?答案是:内存淘汰机制。
      • 内存淘汰机制
        • 如果redis的内存占用过多的时候,此时会进行内存淘汰,有如下一些策略:
        • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
        • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
        • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
    • 分布式主从部署
      • Redis 主从同步:Redis主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布 记录。同步对读取操作的可扩展性和数据冗余很有帮助。
        • 工作原理:
          • Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步。
        • 全量同步:Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
          • 1)从服务器连接主服务器,发送SYNC命令;
          • 2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
          • 3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
          • 4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
          • 5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
          • 6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

        • 增量同步
          • Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
          • 增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
        • Redis主从同步策略
          • 主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
          • 注意点:如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机。
      • 分布式redis怎么实现?
        • (1)主从方式(哨兵模式,高可用)
          • 这种采取一主多从的办法,主从之间进行数据同步。 当Master宕机后,通过选举算法(Paxos、Raft)从slave中选举出新Master继续对外提供服务,主机恢复后以slave的身份重新加入。
          • 主从另一个目的是进行读写分离,这是当单机读写压力过高的一种通用型解决方案。 其主机的角色只提供写操作或少量的读,把多余读请求通过负载均衡算法分流到单个或多个slave服务器上。缺点是主机宕机后,Slave虽然被选举成新Master了,但对外提供的IP服务地址却发生变化了,意味着会影响到客户端。 解决这种情况需要一些额外的工作,在当主机地址发生变化后及时通知到客户端,客户端收到新地址后,使用新地址继续发送新请求。
        • (2)数据同步
          • 无论是主备还是主从都牵扯到数据同步的问题,这也分2种情况:
          • 同步方式:当主机收到客户端写操作后,以同步方式把数据同步到从机上,当从机也成功写入后,主机才返回给客户端成功,也称数据强一致性。 很显然这种方式性能会降低不少,当从机很多时,可以不用每台都同步,主机同步某一台从机后,从机再把数据分发同步到其他从机上,这样提高主机性能分担同步压力。 在redis中是支持这杨配置的,一台master,一台slave,同时这台salve又作为其他slave的master。
          • 异步方式:主机接收到写操作后,直接返回成功,然后在后台用异步方式把数据同步到从机上。 这种同步性能比较好,但无法保证数据的完整性,比如在异步同步过程中主机突然宕机了,也称这种方式为数据弱一致性。
          • Redis主从同步采用的是异步方式,因此会有少量丢数据的危险。还有种弱一致性的特例叫最终一致性,这块详细内容可参见CAP原理及一致性模型。
      • Redis Sentinel(哨兵)架构下的高可用:Redis的主从复制下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方法是无法接受的。但是Redis从2.8开始正式提供了Redis Sentinel(哨兵)架构来解决这个问题。Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化通知给Redis应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。
        • 实现原理:
          • 三个定时监控任务
            • 1)每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构。
            • 2)每隔2秒,每个Sentinel节点会向Redis数据节点的__sentinel__:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断。
            • 3)每隔一秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。
          • 主观下线
            • 因为每隔一秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。
          • 客观下线
            • 当Sentinel主观下线的节点是主节点时,该Sentinel节点会向其他Sentinel节点询问对主节点的判断,当超过<quorum>个数,那么意味着大部分的Sentinel节点都对这个主节点的下线做了同意的判定,于是该Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定。
          • 领导者Sentinel节点选举
            • Raft算法:假设s1(sentinel-1)最先完成客观下线,它会向其余Sentinel节点发送命令,请求成为领导者;收到命令的Sentinel节点如果没有同意过其他Sentinel节点的请求,那么就会同意s1的请求,否则拒绝;如果s1发现自己的票数已经大于等于某个值,那么它将成为领导者。
        • 故障转移
          • 1)领导者Sentinel节点在从节点列表中选出一个节点作为新的主节点
          • 2)上一步的选取规则是与主节点复制相似度最高的从节点
          • 3)领导者Sentinel节点让剩余的从节点成为新的主节点的从节点
          • 4)Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点
        • 选举算法:
          • 一致性
            • 分布式系统的特征在于一组安全性和活跃性或两者的混合。简单地讲,安全是一种财产,规定在执行程序期间不会发生任何不良事件。另一方面,活跃性规定最终会发生一些好事。一致性的目标是使一组服务器在一个值上达成一致,所以活跃的特征在于最终每个服务器都可以决定一个值。安全性表明没有两台服务器来设定值。不幸的是,服务器执行算法步骤可能比其他服务器花费更长时间,并且可能崩溃并停止处理一致性算法。邮件可能会延迟,无序传递或丢失。这些方面使得一致性算法的实施变得非常困难,并迫使它们在“不稳定”期间降低标准并保持安全。确切地说,当系统变得“稳定”时虽然未知,但最终它将保持足够长的“稳定”,以便一致性算法能够做出决定达成最终的一致性。
            • 在稳定运行中,系统需要两个通信步骤:leader - (1) - > servers - (2) - > leader:

            • 领导者向所有服务器发送它想要达成协议的值,并且每个服务器回复给领导者,通知他已经接受了请求。因此,当领导者从法定数量(n/2+1节点)的服务器接收消息时,就达成了协议。请注意,我们在此分析中省略了两条消息:将服务器希望与领导者达成协议的值转发给服务器的消息以及通知服务器已达到该值协议的消息。如果服务器将接受消息发送到所有服务器,或者在领导者发送给服务器的下一条消息中捎带信息,则后一条消息可能不是必需的。
          • 复制
            • 为了实现复制,运行一致性算法的几个实例,并且每个实例都被限制在复制日志中的一个槽条目中,该条目可能会持久存储在磁盘上。领导者可以并行运行多个实例以填充不同的插槽,从而提高性能。但是,并行度高度依赖于硬件、使用的网络和应用程序。每个领导者都对自己当选时增加的一轮或一个周期负有独特的责任:

          • 领导人选举
            • Paxos和Raft都认为最终会有一个领导者,所有稳定的服务器都会信任,而一个领导者负责一个周期(Term)。如果怀疑现任领导人有问题,新领导人将提出一个新任期,必须大于前一任期。在Raft中,服务器向其他服务器发送“领导请求”,并且在认为自己是领导者之前期望大多数服务器的回复。如果它没有得到大多数服务器的回复或者接收到另一个服务器已成为领导者的消息,它将超时并重新开始新的选举过程。服务器每个Term只能投票给一个领导者请求。但是,Paxos并没有真正定义服务器如何成为领导者。为简单起见,研究人员利用服务器id(整数)等进程之间的先后排名。因此,没有被怀疑的排名最高或最低的服务器成为新的领导者。虽然这是一个简单直观的解决方案,但它需要在服务器之间划分术语空间:新术语=旧术语+ N,其中N是服务器的最大数量。Raft对领导者选举过程施加限制:只有最新的服务器才能成为领导者。基本上,它保证领导者拥有以前周期中的所有已提交条目,并且不需要了解它不知道的复制日志中的旧条目。因此,在成为领导者之后,服务器可以简单地在其他服务器上“强加”其“愿望”。然而,Paxos允许任何服务器成为领导者。因此,服务器必须在开始在其他服务器上“强加”其“愿望”之前了解过去,提高了灵活性但也伴随着额外的复杂性。

            • 在Raft中,服务器1或服务器2可以成为领导者。而在Paxos中,任何一个都可以。
          • 安全
            • 由于系统的异步性质,服务器可能在不同时间感知故障和选举。这意味着服务器可能会以不同的方式临时运行,但最终所有服务器都会收敛到一个Term。在任何情况下,如果服务器从比其当前版本更早的Term获得消息,则这意味着发送者要么是领导者,要么试图成为旧Term中的一个,并且接收者必须拒绝该消息并通知发送者。如果服务器从一个大于当前的Term获得消息,这意味着有一个新Term和一个新的领导者,并且接收者必须开始接受领导者的“愿望”。但是,两种算法都必须小心避免覆盖旧领导做出的决定,从而违反安全规定。这就是Raft和Paxos分歧的地方,我们可以看到Raft使用了简单而优雅的方法。如上所述,Raft对领导者选举算法施加限制,只有最新的服务器才能成为领导者:
            • Raft通过比较日志中最后一个条目的索引和术语来确定两个日志中哪一个更新。如果日志包含具有不同术语的最后一个条目,则具有较晚术语的日志将更新。如果日志以相同的术语结束,则更长的日志更新是最新的。然后,领导者只需要确保服务器中的复制日志最终收敛,这是通过施加以下限制来完成的:如果服务器之前没有接受插槽的值,“n"服务器不能接受插槽的值"n - 1”。领导者包括当前请求中的先前日志条目的术语,并且如果其先前请求的术语与领导者发送的术语匹配,则服务器仅接受该请求。否则,它要求领导者首先发送先前失踪的请求,如此反复"n - 2"和 "n - 3"等。
            • 在Paxos中,任何服务器都可以成为领导者,因此避免决策不被覆盖的任务变得有点复杂,因为新的领导者必须在开始“强加”它之前找出其他服务器已处理的内容。希望“在别人身上。这是Paxos算法的准备阶段,必须在选出新领导者后运行一次。准备消息包含新术语和插槽号,“n"通过该插槽号可以达到所有先前条目的协议。服务器回复有关大于的插槽的信息"n”,此信息用于限制新领导者为这些插槽建议的值。
          • 活跃度
            • 只要大多数服务器还活着,保证n/2+1节点正常就能够提供服务。
          • 结论
            • 我们已经展示了Raft和Paxos之间的相似之处,关键的区别在于如何选出领导者并保持安全。在Raft中,只有最新的服务器才能成为领导者,而Paxos允许任何服务器成为领导者。然而,这种灵活性伴随着额外的复杂性。
            • 请注意,Raft和Paxos中的领导者可能会成为瓶颈,因为所有流量都会通过它。
    • 带来的问题
      • 缓存穿透
        • 缓存穿透,即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
          • 缓存穿透解决方案:
            • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。
            • 采用异步更新策略,无论 Key 是否取到值,都直接返回。Value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
            • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的 Key。迅速判断出,请求所携带的 Key 是否合法有效。如果不合法,则直接返回。
      • 缓存雪崩
        • 给缓存的失效时间,加上一个随机值,避免集体失效。
        • 使用互斥锁,但是该方案吞吐量明显下降了。
        • 双缓存。我们有两个缓存,缓存 A 和缓存 B。缓存 A 的失效时间为 20 分钟,缓存 B 不设失效时间。自己做缓存预热操作。
        • 然后细分以下几个小点:从缓存 A 读数据库,有则直接返回;A 没有数据,直接从 B 读数据,直接返回,并且异步启动一个更新线程,更新线程同时更新缓存 A 和缓存 B。
      • 数据库和redis的双写一致性:
        • 一致性问题还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。前提是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。
        • 另外,我们所做的方案从根本上来说,只能降低不一致发生的概率。因此,有强一致性要求的数据,不能放缓存。首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。
    • 具体使用:
      • Redis使用的是单进程,所以在配置时,一个实例只会用到一个CPU;在配置时,如果需要让CPU使用率最大化,可以配置Redis实例数对应CPU数, Redis实例数对应端口数(8核Cpu, 8个实例, 8个端口), 以提高并发:单机测试时, 单条数据在200字节, 测试的结果为8~9万tps;
      • 过程: 数据写到master–>master存储到slave的rdb中–>slave加载rdb到内存。存储点(save point): 当网络中断了, 连上之后, 继续传.Master-slave下第一次同步是全传,后面是增量同步;
      • 对于主库未及时同步从库导致的不一致,称之为延时问题;
        • 对于一致性要求不是那么严格的场景,我们只需要要保证最终一致性即可;
        • 对于延时问题,需要根据业务场景特点分析,从应用层面增加策略来解决这个问题;
        • 例如:
          • 1.新注册的用户,必须先查询主库;
          • 2.注册成功之后,需要等待3s之后跳转,后台此时就是在做数据同步。
  • 分布式缓存应用场景
    • 页面缓存.用来缓存Web 页面的内容片段,包括 HTML 、CSS 和图片等;
    • 应用对象缓存.缓存系统作为ORM 框架的二级缓存对外提供服务,目的是减轻数据库的负载压力,加速应用访问;
    • 解决分布式Web部署的session同步问题,状态缓存.缓存包括Session 会话状态及应用横向扩展时的状态数据等,这类数据一般是难以恢复的,对可用性要求较高,多应用于高可用集群。
    • 并行处理.通常涉及大量中间计算结果需要共享;
    • 云计算领域提供分布式缓存服务。
  • 分布式缓存比较:Memcache VS Redis
    • 1、 Redis 不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。而 memcache只支持简单数据类型,需要客户端自己处理复杂对象
    • 2、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用(PS:持久化在rdb、aof)。
    • 3、由于Memcache没有持久化机制,因此宕机所有缓存数据失效。Redis配置为持久化,宕机重启后,将自动加载宕机时刻的数据到缓存系统中。具有更好的灾备机制。
    • 4、Memcache可以使用Magent在客户端进行一致性hash做分布式。Redis支持在 服务器 端做分布式(PS:Twemproxy/Codis/Redis-cluster多种分布式实现方式)
    • 5、Memcached的简单限制就是键(key)和Value的限制。最大键长为250个字符。可以接受的储存数据不能超过1MB(可修改配置文件变大),因为这是典型slab 的最大值,不适合虚拟机使用。而Redis的Key长度支持到512k。
    • 6、Redis使用的是单线程模型,保证了数据按顺序提交。Memcache需要使用cas保证数据一致性。CAS(Check and Set)是一个确保并发一致性的机制,属于“乐观锁”范畴;原理很简单:拿版本号,操作,对比版本号,如果一致就操作,不一致就放弃任何操作cpu利用。由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更 高。而在100k以上的数据中,Memcached性能要高于Redis 。
    • 7、memcache内存管理:使用Slab Allocation。原理相当简单,预先分配一系列大小固定的组,然后根据数据大小选择最合适的块存储。避免了内存碎片。(缺点:不能变长,浪费了一定空间)memcached默认情况下下一个slab的最大值为前一个的1.25倍。
    • 8、redis内存管理: Redis通过定义一个数组来记录所有的内存分配情况, Redis采用的是包装的malloc/free,相较于Memcached的内存 管理方法来说,要简单很多。由于malloc 首先以链表的方式搜索已管理的内存中可用的空间分配,导致内存碎片比较多。
  • 分布式缓存的常见问题和挑战
    • 1.缓存雪崩
      • 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
    • 2.缓存穿透
      • 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
    • 3.缓存预热
      • 缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
    • 4.缓存更新
      • 除了缓存服务器自带的缓存失效策略之外,我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
      • (1)定时去清理过期的缓存;
      • (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
      • 两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。
    • 5.缓存降级
      • 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
      • 降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
      • 在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
        • (1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
        • (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
        • (3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
        • (4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
  • 同步方式:主从同步(哨兵模式---解决了高可用性)----了解到使用的是异步的方式,主收到写操作的时候,直接返回成功(同步策略:主从连接的时候首先进行全量同步,完成后进入增量同步,从服务器随之可以要求全量同步,但先回进行增量同步,不成功会进行全量同步),异步的将数据同步到其他从服务器上,这样就带了一致性的问题(它保证的若一致性)那么cap的一致性问题该怎么解决(2xa,tcc,消息中间件)
  • 哨兵模式:Redis的主从复制下(读写分离),一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方法是无法接受的。但是Redis从2.8开始正式提供了Redis Sentinel(哨兵)架构来解决这个问题。Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化通知给Redis应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。
    • 实现原理:
      • 三个定时监控任务
        • 1)每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令获取最新的拓扑结构。
        • 2)每隔2秒,每个Sentinel节点会向Redis数据节点的__sentinel__:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断。
        • 3)每隔一秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。
      • 主观下线
        • 因为每隔一秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。
      • 客观下线
        • 当Sentinel主观下线的节点是主节点时,该Sentinel节点会向其他Sentinel节点询问对主节点的判断,当超过<quorum>个数,那么意味着大部分的Sentinel节点都对这个主节点的下线做了同意的判定,于是该Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定。
      • 领导者Sentinel节点选举
        • Raft算法:假设s1(sentinel-1)最先完成客观下线,它会向其余Sentinel节点发送命令,请求成为领导者;收到命令的Sentinel节点如果没有同意过其他Sentinel节点的请求,那么就会同意s1的请求,否则拒绝;如果s1发现自己的票数已经大于等于某个值,那么它将成为领导者。
    • 故障转移
      • 1)领导者Sentinel节点在从节点列表中选出一个节点作为新的主节点
      • 2)上一步的选取规则是与主节点复制相似度最高的从节点
      • 3)领导者Sentinel节点让剩余的从节点成为新的主节点的从节点
      • 4)Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主节点
  • 弊端:
    • 同步方式带来的,如果有从服务器加入或者在主机宕机重连的情况下,甚至网咯波动,都会触发全量同步(rdb复制),若文件过大,频繁的触发,则会影响实际系统的运行效率还有因为是异步方式带来的延时问题
    • 哨兵模式带来的选举新主服务器做法,虽然从服务其被选举为主服务器,但意味着对外提供服务的ip地址发生了变化,前端的部分请求失败,宕机前的数据也有可能没有进行即使同步,切换ip,因为选举的节点是跟主节点最相似的节点,所以还是会有数据不一致的情况存在;
  • 解决办法:
    • 设计跳转等待时间
    • 在Master不做任何持久化方案,而是可以在某个从服务器进行AOF持久化,每秒同步一次
    • 为了提高复制速度和连接稳定性最好将主从放置在同一个局域网中;
  • 一致性哈希算法做负载均衡
    • 一致性哈希算法通过将缓存服务器和被缓存对象都映射到Hash环上以后,从被缓存对象的位置出发,沿顺时针方向遇到的第一个服务器,就是当前对象将要被缓存于的服务器,由于被缓存对象
    • 与服务器hash后的值是固定的,所以在服务器不变的情况下,一个对象必定缓存在一个固定的服务器上,那么,当再次访问这对象时,只要再次使用相同的算法计算即可算出这对象被缓存到哪个服务器上。
    • 做法就是既然没有多余的真正的物理服务器节点,我们就可能将现有的物理节点通过虚拟的方法复制出来,而被复制出来的节点被称为“虚拟节点”
  • Redis官方集群方案 Redis Cluster
    • 我们所做的这些工作只是保证了数据备份以及高可用,目前为止我们的程序一直都是向1台redis写数据,其他的redis只是备份而已。实际场景中,单个redis节点可能不满足要求,因为:
      • 单个redis并发有限
      • 单个redis接收所有的数据,最终回导致内存太大,内存太大回导致rdb文件过大,从很大的rdb文件中同步恢复数据会很慢。
      • 所以,我们需要redis cluster 即redis集群。 集群可以使用的功能是普通单机 Redis 所能使用的功能的一个子集(subset),比如Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误。
        • Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
        • Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.
        • Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令. Redis 集群的优势:
          • 自动分割数据到不同的节点上。
          • 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
      • Redis集群的几个重要特征:
        • (1). Redis 集群的分片特征在于将键空间分拆了16384个槽位,每一个节点负责其中一些槽位。
        • (2). Redis提供一定程度的可用性,可以在某个节点宕机或者不可达的情况下继续处理命令.
    • 槽:
      • 概念:
        • Redis Cluster中有一个16384长度的槽的概念,他们的编号为0、1、2、3……16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster中的每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。这里值得一提的是,在Redis Cluster中,只有Master才拥有槽的所有权,如果是某个Master的slave,这个slave只负责槽的使用,但是没有所有权。Master节点维护着一个16384/8字节的位序列,Master节点用bit来标识对于某个槽自己是否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从0开始)是不是为1即可。如上面的序列,表示当前Master拥有编号为1,134的槽
      • 故障容忍度
        • 心跳和gossip消息
          • Redis Cluster持续的交换PING和PONG数据包。这两种数据包的数据结构相同,都包含重要的配置信息,唯一的不同是消息类型字段。PING和PONG数据包统称为心跳数据包。每个节点在每一秒钟都向一定数量的其它节点发送PING消息,这些节点应该向发送PING的节点回复一个PONG消息。节点会尽可能确保拥有每个其它节点在NOTE_TIMEOUT/2秒时间内的最新信息,否则会发送一个PING消息,以确定与该节点的连接是否正常。假定一个Cluster有301个节点,NOTE_TIMEOUT为60秒,那么每30秒每个节点至少发送300个PING,即每秒10个PING, 整个Cluster每秒发送10x301=3010个PING。这个数量级的流量不应该会造成网络负担。
      • 故障检测。
        • Redis Cluster的故障检测用于检测一个master节点何时变得不再有效,即不能提供服务,从而应该让slave节点提升为master节点。如果提升失败,则整个Cluster失效,不再接受客户端的服务请求。当一个节点A向另外一个节点B发送了PING消息之后,经过NODE_TIMEOUT秒时间之后仍然没有收到PONG应答,则节点A认为节点B失效,节点A将为该节点B设置PFAIL标志。在 NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT时间内,当Cluster中大多数节点认为节点B失效,即设置PFAIL标志时,这个Cluster认为节点B真的失效了,此时节点A将为节点B设置FAIL标志,并向所有节点发送FAIL消息。
        • 在一些特定情况下,拥有FAIL标志的节点,也可以清除掉FAIL标志。
          • Redis Cluster故障检测机制最终应该让所有节点都一致同意某个节点处于某个确定的状态。如果发生这样的情况少数节点确信某个节点为FAIL,同时有少数节点确认某个节点为非FAIL,则Redis Cluster最终会处于一个确定的状态:
        • 情况1:最终大多数节点认为该节点FAIL,该节点最终实际为FAIL。
        • 情况2:最终在N x NODE_TIMEOUT时间内,仍然只有少数节点将给节点标记为FAIL,此时最终会清除这个节点的FAIL标志。
      • 复制迁移
        • Redis Cluster在节点失效时,可能进行自动的slave节点重新配置,修改了Cluster中部分节点的master-slave复制关系,即复制迁移。
        • 假定场景:
        • Cluster中有三个master节点:A、B、C。A有1个slave节点A1,B有1个slave节点B1,C有2个slave节点C1和C2。Aj节点失效了,将A1节点提升为master节点。
          • 考虑不进行自动的slave节点的复制迁移:
            • 如果A失效了,则会将唯一的slave节点A1提升为master节点,但是它没有slave节点。这时如果A1节点又失效了,则原来A节点负责的slots将失效,导致整个cluster不能正常工作。
          • 考虑进行自动的slave节点的复制迁移:
            • 如果A节点失效了,将唯一的slave节点A1提升为master节点,由于它没有slave节点,此时发现C节点有2个slave节点,将其中的C2节点重新配置为A1节点的子节点。这时,Cluster中每个master节点至少有1个slave节点。如果A1节点失效,可将C2节点提升为master。这样的结果是提高了整个cluster的可用性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值