专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

Redis综述篇与面试官彻夜长谈Redis缓存哨兵集群底层原

  一、Redis基本概念面试官心理:靠!手上活都没干完又叫我过来面试,这不耽误我事么,今儿又得加班补活了。。。。。。。。咦,这小伙子简历不错啊,先考考它Redis。。。。。。。。。。面试官:谈谈你对Redis的理解?我:Redis是ANSIC语言编写的一个基于内存的高性能键值对(keyvalue)的NoSQL数据库,一般用于架设在Java程序与数据库之间用作缓存层,为了防止DB磁盘IO效率过低造成的请求阻塞、响应缓慢等问题,用来弥补DB与Java程序之间的性能差距,同时,也可以在DB吞吐跟不上系统并发量时,避免请求直接落入DB从而起到保护DB的作用。而Redis一般除了缓存DB数据之外还可以利用它丰富的数据类型及指令来实现一些其他功能,比如:计数器、用户在线状态、排行榜、session存储等,同时Redis的性能也非常可观,通过官方给出的数据显示能够达到10ws的QPS处理,但是在生产环境的实测结果大概读取QPS在79ws,写入QPS在68ws左右(注:与机器性能也有关),同时Redis也提供事务、持久化、高可用等一些机制的支持。二、Redis基本数据类型与常用指令面试官:刚刚听你提到了可以利用它丰富的数据类型及指令来实现一些其他功能,那你跟我讲讲Redis的一些常用指令。我:Redis常用的一些命令的话一般是都是对于基本数据类型的操作指令以及一些全局指令。。。。。叭啦叭啦叭。。。。。。,如下:
  命令
  作用
  keys
  返回所有键(keys还能用来搜索,比如keysh:搜索所有以h开头的键)
  dbsize
  返回键数量,如果存在大量键,线上禁止使用此指令
  existskey
  检查键是否存在,存在返回1,不存在返回0
  delkey
  删除键,返回删除键个数,删除不存在键返回0
  ttlkey
  查看键存活时间,返回键剩余过期时间,不存在返回1
  expirekeyseconds
  设置过期时间(单位:s),成功返回1,失败返回0
  expireatkeytimestamp
  设置key在某个时间戳(精确到秒)之后过期
  pexpirekeymilliseconds
  设置过期时间(单位:ms),成功返回1,失败返回0
  persistkey
  去掉过期时间
  monitor
  实时监听并返回Redis服务器接收到的所有请求信息
  shutdown
  把数据同步保存到磁盘上,并关闭Redis服务
  info
  查看当前Redis节点信息
  。。。。。。。
  。。。。。。。
  当然了,一般也是记得一些常用的命令,但是更多命令参考:Redis命令大全,因为Redis命令和JVM参数一样,只要记得可以这样做就行了,但是具体的可以去参考相关文档资料。
  面试官:嗯嗯,不错,那再接着讲讲Redis的基本数据类型以及你是在项目中怎么使用它们的吧!我:Redis数据类型在之前是五种,但是现在的版本中存在九种,分别为:字符串(stringsstring)、散列(hasheshash)、列表(listslist)、集合(setsset)、有序集合(sortedsetszset)以及后续的四种数据类型:bitmaps、hyperloglogs、地理空间(geospatial)、消息(Streams),不过无论是哪种数据类型Redis都不会直接将它放在内存中存储,而是转而内部使用RedisObject来存储以及表示所有类型的keyvalue(说着说着我拿出了纸和笔,给面试官画了一张图):
  Redis内部使用一个RedisObject对象来表示所有的key和value,RedisObject最主要的信息如上图所示:type表示一个value对象具体是何种数据类型,encoding是不同数据类型在Redis内部的存储方式。比如:typestring表示value存储的是一个普通字符串,那么encoding可以是raw或者int,而关于其他数据类型的内部编码实现我顿时再拿起笔chuachuachua:我接着回答:下面我再简单讲讲Redis的基本数据类型以及它们的应用场景:
  类型
  描述
  特性
  场景
  string
  二进制安全
  可以存储任何元素(数字、字符、音视频、图片、对象。。。。。)
  计数器、分布式锁、字符缓存、分布式ID生成、session共享、秒杀token、IP限流等
  hash
  键值对存储,类似于Map集合
  适合存储对象,可以将对象属性一个个存储,更新时也可以更新单个属性,操作某一个字段
  对象缓存、购物车等
  list
  双向链表
  增删快
  栈、队列、有限集合、消息队列、消息推送、阻塞队列等
  set
  元素不能重复,每次获取无序
  添加、删除、查找的复杂度都是O(1),提供了求交集、并集、差集的操作
  抽奖活动、朋友圈点赞、用户(微博好友)关注、相关关注、共同关注、好友推荐(可能认识的人)等
  sortedset
  有序集合,每个元素有一个对应的分数,不允许元素重复
  基于分数进行排序,如果分数相等,以key值的ascii值进行排序
  商品评价标签(好评、中评、差评等)、排行榜等
  bitmaps
  Bitmaps是一个字节由8个二进制位组成
  在字符串类型上面定义地位操作
  在线用户统计、用户访问统计、用户点击统计等
  hyperloglog
  Redis2。8。9版本添加了HyperLogLog结构。RedisHyperLogLog是用来做基数统计的算法。
  用于进行基数统计,不是集合,不保存数据,只记录数量而不是具体数据
  统计独立UV等
  geospatial
  Redis3。2版本新增的数据类型:GEO对地理位置的支持
  以将用户给定的地理位置信息储存起来,并对这些信息进行操作
  地理位置计算
  stream
  Redis5。0之后新增的数据类型
  支持发布订阅,一对多消费
  消息队列
  PS:HyperLogLog的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。在Redis里面,每个HyperLogLog键只需要花费12KB内存,就可以计算接近264个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为HyperLogLog只会根据输入元素来计算基数,而不会储存输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素(核心是基数估算算法,最终数值存在一定误差误差范围:基数估计的结果是一个带有0。81标准错误的近似值,耗空间极小,每个hyperloglogkey占用了12K的内存用于标记基数,pfadd命令不是一次性分配12K内存使用,会随着基数的增加内存逐渐增大,Pfmerge命令合并后占用的存储空间为12K,无论合并之前数据量多少)三、Redis缓存及一致性、雪崩、击穿与穿透问题面试官提问:那么你们在使用Redis做为缓存层的时候是怎么通过Java操作Redis的呢?我的心理:这问题不是送命题吗。。。。。我:Java操作Redis的客户端有很多,比如springData中的RedisTemplate,也有SpringCache集成Redis后的注解形式,当然也会有一些Jedis、Lettuce、Redisson等等,而我们使用的是Lettuce以及Redisson。。。。。。。。面试官提问:那你们在使用Redis作为缓存的时候有没有遇到什么问题呢?我:咳咳,是的,确实遇到了以及考虑到了一些问题,比如缓存一致性、雪崩、穿透与击穿,关于Redis与MySQL之间的数据一致性问题其实也考虑过很多方案,比如先删后改,延时双删等等很多方案,但是在高并发情况下还是会造成数据的不一致性,所以关于DB与缓存之间的强一致性一定要保证的话那么就对于这部分数据不要做缓存,操作直接走DB,但是如果这个数据比较热点的话那么还是会给DB造成很大的压力,所以在我们的项目中还是采用先删再改过期的方案来做的,虽然也会存在数据的不一致,但是勉强也能接受,因为毕竟使用缓存访问快的同时也能减轻DB压力,而且本身采用缓存就需要接受一定的数据延迟性和短暂的不一致性,我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括合适的缓存更新策略,合适的缓存淘汰策略,更新数据库后及时更新缓存、缓存失败时增加重试机制等。面试官话锋一转:打断一下,你刚刚提到了使用缓存能让访问变快,那么你能不能讲讲Redis为什么快呢?我的心理:好家伙,这一手来的我猝不及防。。。。。。硬着头发回答:Redis快的原因嘛其实可以从多个维度来看待:一、Redis完全基于内存二、Redis整个结构类似于HashMap,查找和操作复杂度为O(1),不需要和MySQL查找数据一样需要产生随机磁盘IO或者全表三、Redis对于客户端的处理是单线程的,采用单线程处理所有客户端请求,避免了多线程的上下文切换和线程竞争造成的开销四、底层采用selectepoll多路复用的高效非阻塞IO模型五、客户端通信协议采用RESP,简单易读,避免了复杂请求的解析开销面试官露出姨父般的慈笑:嗯嗯,还不错,那你继续谈谈刚刚的缓存雪崩、穿透与击穿的问题吧我:好的,先说缓存雪崩吧,缓存雪崩造成的原因是因为我们在做缓存时为了保证内存利用率,一般在写入数据时都会给定一个过期时间,而就是因为过期时间的设置有可能导致大量的热点key在同一时间内全部失效,此时来了大量请求访问这些key,而Redis中却没有这些数据,从而导致所有请求直接落入DB查询,造成DB出现瓶颈或者直接被打宕导致雪崩情况的发生。关于解决方案的的话也可以从多个维度来考虑:一、设置热点数据永不过期,避免热点数据的失效导致大量的相同请求落入DB二、错开过期时间的设置,根据业务以及线上情况合理的设置失效时间三、使用分布式锁或者MQ队列使得请求串行化,从而避免同一时间请求大量落入DB(性能会受到很大的影响)面试官:那缓存穿透呢?指的是什么?又该怎么解决?我喝了口水接着回答:缓存穿透这个问题是由于请求参数不合理导致的,比如对外暴露了一个接口getUser?userIDxxx,而数据库中的userID是从1开始的,当有黑客通过这个接口携带不存在的ID请求时,比如:getUser?userID1,请求会先来到Redis中查询缓存,但是发现没有对应的数据从而转向DB查询,但是DB中也无此值,所以也无法写入数据到缓存,而黑客就通过这一点利用肉鸡等手段疯狂请求这个接口,导致出现大量Redis不存在数据的请求落入DB,从而导致DB出现瓶颈或者直接被打宕机,整个系统陷入瘫痪。面试官:嗯,那又该如果避免这种情况呢?我:解决方案也有好几种呢:一、做IP限流与黑名单,避免同一IP一瞬间发送大量请求二、对于请求做非法校验,对于携带非法参数的请求直接过滤三、对于DB中查询不存在的数据写入Redis中NotData并设置短暂的过期时间,下次请求能够直接被拦截在Redis而不会落入DB四、布隆过滤器面试官:那接下来的缓存击穿呢?又是怎么回事?怎么解决?我:这个简单,缓存击穿和缓存雪崩有点类似,都是由于请求的key过期导致的问题,但是不同点在于失效key的数量,对于雪崩而言指的是大量的key失效导致大量请求落入DB,而对于击穿而言,指的是某一个热点key突然过期,而这个时候又突然又大量的请求来查询它,但是在Redis中却并没有查询到结果从而导致所有请求全部打向DB,导致在这个时刻DB直接被打穿。解决方案的话也是有多种:一、设置热点key永不过期二、做好Redis监控,请求串行化访问(性能较差)使用mutex锁机制:就是在缓存失效的时候(判断拿出来的值为空),不是立即去loaddb,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutexkey,当操作返回成功时,再进行loaddb的操作并回设缓存;否则,就重试整个get缓存的方法,代码实现如下:publicResultget(intID){RedisResultRedis。get(ID);if(RedisResult!null){returnRedisResult;}if(Redis。setnx(update:ID)!0){DBResultDB。selectByID(ID);if(DBResult!null){避免缓存穿透Redis。set(ID,DBResult);Redis。del(update:ID);returnDBResult;}Redis。set(ID,NotData);return抱歉,当前查询暂时没有找到数据。。。。。。;}Thread。sleep(2);returnget(ID);}复制代码四、Redis八种淘汰策略与三种删除策略4。1。八种键淘汰(过期)策略面试官:你前面提到过,Redis的数据是全部放在内存中的,那么有些数据我也没有设置过期时间,导致了大量的内存浪费,当我有新的数据需要写入内存不够用了怎么办?我的内心:好家伙,问个Redis淘汰策略这么拐弯抹角。。。。。。。我:我想你是想问内存淘汰策略吧,Redis在5。0之前为我们提供了六种淘汰策略,而5。0为我们提供了八种,但是大体上来说这些lru、lfu、random、ttl四种类型,如下:
  策略
  概述
  volatilelru
  从已设置过期时间的数据集中挑选最近最少使用的数据淘汰,没有设置过期时间的key不会被淘汰,这样就可以在增加内存空间的同时保证需要持久化的数据不会丢失。
  volatilettl
  从已设置过期时间的数据集中挑选将要过期的数据淘汰,ttl值越大越优先被淘汰。
  volatilerandom
  从已设置过期时间的数据集中任意选择数据淘汰
  volatilelfu
  从已设置过期时间的数据集挑选使用频率最低的数据淘汰
  allkeyslru
  从数据集中挑选最近最少使用的数据淘汰,该策略要淘汰的key面向的是全体key集合,而非过期的key集合(应用最广泛的策略)。
  allkeyslfu
  从数据集中挑选使用频率最低的数据淘汰
  allkeysrandom
  从数据集(server。db〔i〕。dict)中任意选择数据淘汰
  noenviction(驱逐)
  禁止驱逐数据,这也是默认策略。意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用noenviction策略可以保证数据不被丢失。我喘了口气接着说:一、在Redis中,数据有一部分访问频率较高,其余部分访问频率较低,或者无法预测数据的使用频率时,设置allkeyslru是比较合适的。二、如果所有数据访问概率大致相等时,可以选择allkeysrandom。三、如果研发者需要通过设置不同的ttl来判断数据过期的先后顺序,此时可以选择volatilettl策略。四、如果希望一些数据能长期被保存,而一些数据可以被淘汰掉时,选择volatilelru或volatilerandom都是比较不错的。五、由于设置expire会消耗额外的内存,如果计划避免Redis内存在此项上的浪费,可以选用allkeyslru策略,这样就可以不再设置过期时间,高效利用内存了。maxmemorypolicy:参数配置淘汰策略。maxmemory:限制内存大小。4。2。三种键删除策略面试官:那Redis的Key删除策略有了解过吗?我:Redis删除Key的策略策略有三种:定时删除:在设置键的过期时间的同时,设置一个定时器,当键过期了,定时器马上把该键删除。(定时删除对内存来说是友好的,因为它可以及时清理过期键;但对CPU是不友好的,如果过期键太多,删除操作会消耗过多的资源。)惰性删除:key过期后任然留在内存中不做处理,当有请求操作这个key的时候,会检查这个key是否过期,如果过期则删除,否则返回key对应的数据信息。(惰性删除对CPU是友好的,因为只有在读取的时候检测到过期了才会将其删除。但对内存是不友好,如果过期键后续不被访问,那么这些过期键将积累在缓存中,对内存消耗是比较大的。)定期删除:Redis数据库默认每隔100ms就会进行随机抽取一些设置过期时间的key进行检测,过期则删除。(定期删除是定时删除和惰性删除的一个折中方案。可以根据实际场景自定义这个间隔时间,在CPU资源和内存资源上作出权衡。)Redis默认采用定期惰性删除策略。五、Redis三种持久化机制5。1。RDB持久化面试官:那么你刚刚提到的Redis为了保证性能会将所有数据放在内存,那么机器突然断电或宕机需要重启,内存中的数据岂不是没有了?我:Redis的确是将数据存储在内存的,但是也会有相关的持久化机制将内存持久化备份到磁盘,以便于重启时数据能够重新恢复到内存中,避免数据丢失的风险。而Redis持久化机制由三种,在4。X版本之前Redis只支持AOF以及RDB两种形式持久化,但是因为AOF与RDB都存在各自的缺陷,所以在4。x版本之后Redis还提供一种新的持久化机制:混合型持久化(但是最终生成的文件还是。AOF)。面试官:那你仔细讲讲这几种持久化机制吧我:好的,RDB持久化把内存中当前进程的数据生成快照(。rdb)文件保存到硬盘的过程,有手动触发和自动触发:自动触发:RedisRDB持久化默认开启
  save9001900s内存在1个写操作
  save30010300s内存在10个写操作
  save601000060s内存在10000个写操作
  如上是RDB的自动触发的默认配置,当操作满足如上条件时会被触发。手动触发:save:阻塞当前Redis,直到RDB持久化过程完成为止,若内存实例比较大会造成长时间阻塞,线上环境不建议用它bgsave:Redis进程执行fork操作创建子进程,由子进程完成持久化,阻塞时间很短(微秒级),是save的优化,在执行Redisclishutdown关闭Redis服务时或执行flushall命令时,如果没有开启AOF持久化,自动执行bgsave,bgsave执行流程如下:
  而且RDB是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,重启时加载这个文件达到数据恢复。RDB优缺点:优点:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了Redis的高性能;而且RDB文件存储的是压缩的二进制文件,适用于备份、全量复制,可用于灾难备份,同时RDB文件的加载速度远超于AOF文件。缺点:RDB是间隔一段时间进行持久化,如果持久化之间的时间内发生故障,会出现数据丢失。所以这种方式更适合数据要求不严谨的时候,因为RDB无法做到实时持久化,而且每次都要创建子进程,频繁创建成本过高;备份时占用内存,因为Redis在备份时会独立创建一个子进程,将数据写入到一个临时文件(需要的内存是原本的两倍);还有一点,RDB文件保存的二进制文件存在新老版本不兼容的问题。5。1。AOF持久化我:而AOF持久化方式能很好的解决RDB持久化方式造成的数据丢失,AOF持久化到硬盘中的并不是内存中的数据快照,而是和MySQL的binlog日志一样记录写入命令,AOF的持久化策略也有三种:appendfsyncalways:同步持久化形式,每次发生数据更改都将命令追加到AOF文件,因为每次写入时都记录会产生大量磁盘IO,从而性能会受到影响,但是数据最安全。appendfsynceverysec:Redis开启AOF后的缺省配置,异步操作,每秒将写入命令追加到AOF文件,如果在刚持久化之后的一秒内宕机,会造成1S的数据丢失。appendfsyncno:Redis并不直接调用文件同步,而是交给操作系统来处理,操作系统可以根据buffer填充情况通道空闲时间等择机触发同步;这是一种普通的文件操作方式。性能较好,在物理服务器故障时,数据丢失量会因OS配置有关。AOF持久化机制优缺点:优点:根据不同的fsync策略可以保证数据丢失风险降到最低,数据能够保证是最新的,fsync是后台线程在处理,所以对于处理客户端请求的线程并不影响。缺点:文件体积由于保存的是所有命令会比RDB大上很多,而且数据恢复时也需要重新执行指令,在重启时恢复数据的时间往往会慢很多。虽然fsync并不是共用处理客户端请求线程的资源来处理的,但是这两个线程还是在共享同一台机器的资源,所以在高并发场景下也会一定受到影响。AOF机制重写:随着Redis在线上运行的时间越来越久,客户端执行的命令越来越多,AOF的文件也会越来越大,当AOF达到一定程度大小之后再通过AOF文件恢复数据是异常缓慢的,那么对于这种情况Redis在开启AOF持久化机制的时候会存在AOF文件的重写,缺省配置是当AOF文件比上一次重写时的文件大小增长100并且文件大小不小于64MB时会对整个AOF文件进行重写从而达到减肥的目的(这里的100和64MB可以通过autoaofrewritepercentage100与autoaofrewriteminsize64mb来调整)。而AOFrewrite操作就是压缩AOF文件的过程,当然Redis并没有采用基于原aof文件来重写的方式,而是采取了类似snapshot的方式:基于copyonwrite,全量遍历内存中数据,然后逐个序列到aof文件中。因此AOFrewrite能够正确反应当前内存数据的状态,这正是我们所需要的;rewrite过程中,对于新的变更操作将仍然被写入到原AOF文件中,同时这些新的变更操作也会被Redis收集起来(buffer,copyonwrite方式下,最极端的可能是所有的key都在此期间被修改,将会耗费2倍内存),当内存数据被全部写入到新的aof文件之后,收集的新的变更操作也将会一并追加到新的aof文件中,此后将会重命名新的aof文件为appendonly。aof,此后所有的操作都将被写入新的aof文件。如果在rewrite过程中,出现故障,将不会影响原AOF文件的正常工作,只有当rewrite完成之后才会切换文件,因为rewrite过程是比较可靠的,触发rewrite的时机可以通过配置文件来声明,同时Redis中可以通过bgrewriteaof指令人工干预。AOF持久化过程如下:面试官:那你项目中Redis采用的是那种持久化方式呢?我:在我们项目中考虑到了Redis中不仅仅只是用来做缓存,其中还存储着一些MySQL中不存在的数据,所以数据的安全性要求比较高,而RDB因为并不是实时的持久化,会出现数据丢失,但是采用AOF形式在重启、灾备、迁移的时候过程异常耗时,也并不理想,所以在我们线上是同时采用两种形式的,而AOFRDB两种模式同时开启时Redis重启又该加载谁呢?(说着说着我又掏出了纸笔给面试官画了如下一幅图):5。3。4。x之后的混合型持久化
  当然在Redis4。x之后推出了混合型持久化机制,因为RDB虽然加载快但是存在数据丢失,AOF数据安全但是加载缓慢,Redis为了解决这个问题,带来了一个新的持久化选项混合持久化。将RDB文件的内容和增量的AOF日志文件存在一起。
  这里的AOF日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量AOF日志,通常这部分AOF日志很小。Redis重启的时候,可以先加载RDB的内容,然后再重放增量AOF日志,就可以完全替代之前的AOF全量文件重放,恢复效率因此大幅得到提升(混合型持久化最终生成的文件后缀是。aof,可以通过redis。conf文件中aofuserdbpreambleyes配置开启)。混合型持久化优点:结合了RDB和AOF的优点,使得数据恢复的效率大幅提升混合型持久化缺点:兼容性不好,Redis4。x新增,虽然最终的文件也是。aof格式的文件,但在4。0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差六、Redis的事务机制面试官:既然Redis是数据库,那么它支不支持事务呢?我:Redis作为数据库当然是支持事务的,只不过Redis的事务机制是弱事务,相对来说比较鸡肋,官方给出如下几个指令来进行Redis的事务控制:MULTI:标记一个事务块的开始DISCARD:取消事务,放弃执行事务块内的所有命令EXEC:执行所有事务块内的命令UNWATCH:取消WATCH命令对所有key的监视WATCHkey〔key。。。〕:监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断七、Redis内存模型及内存划分面试官:嗯嗯,挺不错,那你对于Redis的内存模型以及内存的划分有去了解过嘛?我:了解过的,Redis的内存模型我们可以通过客户端连接之后使用内存统计命令infomemory去查看,如下:usedmemory(单位:字节):Redis分配器分配的内存总量,包括使用的虚拟内存(稍后会详解)usedmemoryrss(单位:字节):Redis进程占据操作系统的内存;除了分配器分配的内存之外,usedmemoryrss还包括进程运行本身需要的内存、内存碎片等,但是不包括虚拟内存说明:usedmemory是从Redis角度得到的量,usedmemoryrss是从操作系统角度得到的量。二者之所以有所不同,一方面是因为内存碎片和Redis进程运行需要占用内存,使得usedmemoryrss可能更大;另一方面虚拟内存的存在,使得usedmemory可能更大memfragmentationratio:内存碎片比率,该值是usedmemoryrssusedmemory;一般大于1,且该值越大,内存碎片比例越大。而小于1,说明Redis使用了虚拟内存,由于虚拟内存的媒介是磁盘,比内存速度要慢很多,当这种情况出现时,应该及时排查,如果内存不足应该及时处理,如增加Redis节点、增加Redis服务器的内存、优化应用等;一般来说,memfragmentationratio在1。03左右是比较健康的状态(对于jemalloc分配器来说),由于在实际应用中,Redis的数据量会比较大,此时进程运行占用的内存与Redis数据量和内存碎片相比,都会小得多,memfragmentationratio便成了衡量Redis内存碎片率的参数memallocator:Redis使用的内存分配器,在编译时指定;可以是libc、jemalloc或tcmalloc,默认是jemalloc我接着说:而Redis作为内存数据库,在内存中存储的内容主要是数据,但除了数据以外,Redis的其他部分也会占用内存。Redis的内存占用可以划分为以下几个部分:数据:作为数据库,数据是最主要的部分;这部分占用的内存会统计在usedmemory中进程本身运行需要的内存:Redis主进程本身运行肯定需要占用内存,如代码、常量池等等,这部分内存大约几兆,在大多数生产环境中与Redis数据占用的内存相比可以忽略。这部分内存不是由jemalloc分配,因此不会统计在usedmemory中。除了主进程外,Redis创建的子进程运行也会占用内存,如Redis执行AOF、RDB重写时创建的子进程。当然,这部分内存不属于Redis进程,也不会统计在usedmemory和usedmemoryrss中。缓冲内存:缓冲内存包括客户端缓冲区、复制积压缓冲区、AOF缓冲区等;其中,客户端缓冲存储客户端连接的输入输出缓冲;复制积压缓冲用于部分复制功能;AOF缓冲区用于在进行AOF重写时,保存最近的写入命令。在了解相应功能之前,不需要知道这些缓冲的细节;这部分内存由jemalloc分配,因此会统计在usedmemory中。内存碎片:内存碎片是Redis在分配、回收物理内存过程中产生的。例如,如果对数据的更改频繁,而且数据之间的大小相差很大,可能导致Redis释放的空间在物理内存中并没有释放,但Redis又无法有效利用,这就形成了内存碎片。内存碎片不会统计在usedmemory中。内存碎片的产生与对数据进行的操作、数据的特点等都有关;此外,与使用的内存分配器也有关系:如果内存分配器设计合理,可以尽可能的减少内存碎片的产生。如果Redis服务器中的内存碎片已经很大,可以通过安全重启的方式减小内存碎片:因为重启之后,Redis重新从备份文件中读取数据,在内存中进行重排,为每个数据重新选择合适的内存单元,减小内存碎片。面试官:那Redis的共享对象你有了解过吗?在RedisObject对象中有一个refcount,refcount记录的是该对象被引用的次数,类型为整型。refcount的作用,主要在于对象的引用计数和内存回收。当创建新对象时,refcount初始化为1;当有新程序使用该对象时,refcount加1;当对象不再被一个新程序使用时,refcount减1;当refcount变为0时,对象占用的内存会被释放。Redis中被多次使用的对象(refcount1),称为共享对象。Redis为了节省内存,当有一些对象重复出现时,新的程序不会创建新的对象,而是仍然使用原来的对象。这个被重复使用的对象,就是共享对象。目前共享对象仅支持整数值的字符串对象。共享对象的具体实现:
  Redis的共享对象目前只支持整数值的字符串对象。之所以如此,实际上是对内存和CPU(时间)的平衡:共享对象虽然会降低内存消耗,但是判断两个对象是否相等却需要消耗额外的时间。对于整数值,判断操作复杂度为O(1);对于普通字符串,判断复杂度为O(n);而对于哈希、列表、集合和有序集合,判断的复杂度为O(n2)。虽然共享对象只能是整数值的字符串对象,但是5种类型都可能使用共享对象(如哈希、列表等的元素可以使用)。
  就目前的实现来说,Redis服务器在初始化时,会创建10000个字符串对象,值分别是09999的整数值;当Redis需要使用值为09999的字符串对象时,可以直接使用这些共享对象。10000这个数字可以通过调整参数RedisSHAREDINTEGERS(4。0中是OBJSHAREDINTEGERS)的值进行改变。
  共享对象的引用次数可以通过objectrefcount命令查看。八、Redis虚拟内存面试官:刚刚听你提到过Redis的虚拟内存,那你能详细讲讲它是怎么会事吗?我:首先说明下Redis的虚拟内存与操作系统虚拟内存不是一码事,但是思路和目的都是相冋的。就是暂时把不经常访问的数据从內存交换到磁盘中,从而腾出宝贵的内存空间。对于Redis这样的内存数据库,内存总是不够用的。除了可以将数据分割到多个Redis实例以外。另外的能够提高数据库容量的办法就是使用虚拟内存技术把那些不经常访问的数据交换到磁盘上。如果我们存储的数据总是有少部分数据被经常访问,大部分数据很少被访问,对于网站来说确实总是只有少量用户经常活跃。当少量数据被经常访问时,使用虚拟内存不但能提高单台Redis数据库服务器的容量,而且也不会对性能造成太多影响Redis没有使用操作系统提供的虚拟内存机制而是自己在用户态实现了自己的虚拟内存机制。主要的理由有以下两点:一、操作系统的虚拟内存是以4k页为最小单位进行交换的。而Redis的大多数对象都远小于4k,所以一个操作系统页上可能有多个Redis对象。另外Redis的集合对象类型如list,set可能行在于多个操作系统页上。最终可能造成只有10的key被经常访问,但是所有操作系统页都会被操作系统认为是活跃的,这样只有内存真正耗尽时操作系统才会进行页的交换二、相比操作系统的交换方式,Redis可以将被交换到磁盘的对象进行压缩,保存到磁盘的对象可以去除指针和对象元数据信息。一般压缩后的对象会比内存中的对象小10倍。这样Redis的虛拟内存会比操作系统的虚拟内存少做很多I0操作我:而关于Redis虚拟内存的配置也存在于redis。conf文件中,如下:vmenabledves:开启虚拟内存功能vmswapfile。。redis。swap:交换出来value保存的文件路径Vmmaxmemory268435456:Redis使用的最大内存上限(256MB),超过上限后Redis开始交换value到磁盘swap文件中。建议设置为系统空闲内存的6080vmpagesize32:每个Redis页的大小32个字节vmpages134217728:最多在文件中使用多少个页,交换文件的大小vmmaxthreads8:用于执行value对象换入换出的工作线程数量,0表示不使用工作线程(详情后面介绍)。我:Redis的虚拟内存在设计上为了保证key的查询速度,只会将value交换到swap文件。如果是由于太多key很小的value造成的内存问题,那么Redis的虚拟内存并不能解决问题。和操作系统一样Redis也是按页来交换对象的。Redis规定同一个页只能保存一个对象。但是一个对象可以保存在多个页中。在Redis使用的内存没超过vmmaxmemory之前是不会交换任何value的。当超过最大内存限制后,Redis会选择把较老的对象交换到swap文件中去。如果两个对象一样老会优先交换比较大的对象,精确的交换计算公式swappabilityage1og(sizeInmemory)。对于vmpagesize的设置应该根据自己应用将页的大小设置为可以容纳大多数对象的尺寸。太大了会浪费磁盘空间,太小了会造成交换文件出现过多碎片。对于交换文件中的每个页,Redis会在内存中用一个1bit值来对应记录页的空闲状态。所以像上面配置中页数量(vmpages134217728)会占用16MB内存用来记录页的空內状态。vmmaxthreads表示用做交换任务的工作线程数量。如果大于0推荐设为服务器的cpu的核心数。如果是0则交换过程在上线程进行。具体工作模式如下:阻塞模式(vmmaxthreads0):换出:主线程定期检査发现内存超出最大上限后,会直接以阻塞的方式,将选中的对象保存到swap文件中,并释放对象占用的内存空间,此过程会一直重复直到下面条件满足。内存使用降到最大限制以下swap文件满了几乎全部的对象都被交换到磁盘了换入:当有客户端请求已经被换出的value时,主线程会以阳塞的方式从swap文件中加载对应的value对象,加载时此时会阻塞所客户端。然后处理该客户端的请求非阻塞模式(vmmaxthreads0):换出:当主线程检测到使用内存超过最大上限,会将选中要父换的对象信息放到一个队列中父给工作线程后台处理,主线程会继续处理客户端请求换入:如果有客户端请求的key已终被换出了,主线程会先阳塞发出命令的客户端,然后将加载对象的信息放到一个队列中,让工作线程去加载。加载完毕后工作线程通知主线程。主线程再执行客户端的命令。这种方式只阻塞请求的value是已经被换出key的客户端总的来说阻塞方式的性能会好些,因为不需要线程同步、创建线程和恢复被阻塞的客户端等开销。但是也相应的牺牡了响应性。工作线稈方式主线程不会阳塞在磁盘1O上,所以响应性更好。如果我们的应用不太经常发生换入换出,而且也不太在意有点延迟的话推荐使用阻塞方式(详细介绍参考)。九、Redis客户端通信RESP协议面试官:那你再简单讲讲Redis的客户端通信的RESP协议吧我:这个比较简单,RESP是Redis序列化协议,Redis客户端RESP协议与Redis服务器通信。RESP协议在Redis1。2中引入,但在Redis2。0中成为与Redis服务器通信的标准方式。这个通信方式就是Redis客户端实现的协议。RESP实际上是一个序列化协议,它支持以下数据类型:简单字符串、错误、整数、大容量字符串和数组。当我们在客户端中像Redis发送操作命令时,比如:setname竹子爱熊猫这条命令,不会直接以这种格式的形式发送到RedisServer,而是经过RESP的序列化之后再发送给Redis执行,而AOF持久化机制持久化之后生成的AOF文件中也并不是存储setname竹子爱熊猫这个指令,而是存储RESP序列化之后的指令,RESP的特点如下:实现简单能被计算机快速地解析可读性好能够被人工解析十、Redis高可用机制:主从复制、哨兵、代理式分片式集群10。1。主从复制面试官:如果在整个架构中加入Redis作为缓存层,那么会在Java程序与DB之间多出一层访问,假设Redis挂了那么Java程序这边又会抛出异常导致所有请求死在这里从而导致整个系统的不可用,那么怎么避免Redis出现这类的单点故障呢?我:Redis既然这么受欢迎那么这些问题它都提供了相关的解决方案的,Redis有提供了主从、哨兵、代理集群与分片集群的高可用机制来保证出现单点问题时能够及时的切换机器以保障整个系统不受到影响。但是后续的三种高可用机制都是基于主从的基础上来实现的,所以我先说说Redis的主从复制。虽然我们之前讲到过持久化机制可以保证数据重启情况下也不丢失,但是由于是存在于一台服务器上的,如果机器磁盘坏了、机房爆炸(玩笑)等也会导致数据丢失,而主从复制可以将数据同步到多台不同机器,也能够保证在主节点宕机时任然对外提供服务,还可以做到通过读写分离的形式提升整体缓存业务群吞吐量。一般在线上环境时我们去搭建主从环境时,为了保证数据一致性,从节点是不允许写的,而是通过复制主节点数据的形式保障数据同步。所以在整个Redis节点群中只能同时运行存在一台主,其他的全为从节点,示意图如下(读的QPS可以通过对从节点的线性扩容来提升):面试官:那你能详细说下主从数据同步的过程吗?我:可以的,Redis2。8之前使用sync〔runId〕〔offset〕同步命令,Redis2。8之后使用psync〔runId〕〔offset〕命令。两者不同在于,sync命令仅支持全量复制过程,psync支持全量和部分复制。介绍同步之前,先介绍几个概念:runId:每个Redis节点启动都会生成唯一的uuid,每次Redis重启后,runId都会发生变化offset:主节点和从节点都各自维护自己的主从复制偏移量offset,当主节点有写入命令时,offsetoffset命令的字节长度。从节点在收到主节点发送的命令后,也会增加自己的offset,并把自己的offset发送给主节点。这样,主节点同时保存自己的offset和从节点的offset,通过对比offset来判断主从节点数据是否一致replbackbuffer:复制缓冲区,用来存储增量数据命令主从数据同步具体过程如下:我:当然psync命令除了支持全量复制之外还支持部分复制,因为在做主从数据同步时会导致主从机器网络带宽开销非常大,而在2。8之前Redis仅支持全量复制,这样非常容易导致Redis在线上出现网络瓶颈,而在2。8之后的增量(部分)复制,用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当slave再次连上master后,如果条件允许,master会补发丢失数据给slave。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销。部分复制流程图如下(复制缓存区溢出也会导致全量复制):PS:psync〔runid〕〔offset〕命令三种返回值:FULLRESYNC:第一次连接,进行全量复制CONTINUE:进行部分复制ERR:不支持psync命令,进行全量复制面试官:那你觉得主从机制有什么好处?存在什么问题?我:主从机制其实也是为后续的一些高可用机制打下了基础,但是本身也存在一些缺陷,当然在后续的高可用机制中得到了解决,具体如下:优点:能够为后续的高可用机制打下基础在持久化的基础上能够将数据同步到其他机器,在极端情况下做到灾备的效果能够通过主写从读的形式实现读写分离提升Redis整体吞吐,并且读的性能可以通过对从节点进行线性扩容无限提升缺点:全量数据同步时如果数据量比较大,在之前会导致线上短暂性的卡顿一旦主节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干预写入的QPS性能受到主节点限制,虽然主从复制能够通过读写分离来提升整体性能,但是只有从节点能够做到线性扩容升吞吐,写入的性能还是受到主节点限制木桶效应,整个Redis节点群能够存储的数据容量受到所有节点中内存最小的那台限制,比如一主两从架构:master32GB、slave132GB、slave216GB,那么整个Redis节点群能够存储的最大容量为16GB10。2。哨兵机制面试官:你刚刚提到过后续的高可用机制能解决这些问题,你说的是哨兵吗?那你再说说哨兵机制我:好的,哨兵机制的确能够解决之前主从存在的一些问题,如图:
  上图所示是目前企业中常用的Redis架构,一主两从三哨兵架构,RedisSentinel(哨兵)主要功能包括主节点存活检测、主从运行情况检测、自动故障转移、主从切换。RedisSentinel最小配置是一主一从。Redis的Sentinel系统可以用来管理多个Redis节点,该系统可以执行以下四个任务:监控:不断检查主服务器和从服务器是否正常运行通知:当被监控的某个Redis服务器出现问题,Sentinel通过API脚本向管理员或者其他应用程序发出通知自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点,这样就不需要人工干预进行主从切换配置提供者:在Sentinel模式下,客户端应用在初始化时连接的是Sentinel节点集合,从中获取主节点的信息面试官:那你能讲讲哨兵机制原理吗?我:可以的,哨兵的工作原理如下:一、每个哨兵节点每10秒会向主节点和从节点发送info命令获取最级联结构图,哨兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当有新的从节点加入时可以马上感知到二、每个哨兵节点每隔2秒会向Redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵节点的信息及对主节点的判断,其实就是通过消息publish和subscribe来完成的三、隔1秒每个哨兵根据自己info获取的级联结构信息,会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据
  四、Sentinel会以每秒一次的频率向所有与其建立了命令连接的实例(master、salve、其他Sentinel)发ping命令,通过判断ping回复是有效回复还是无效回复来判断实例是否在线存活(对该Sentinel来说是主观在线),Sentinel配置文件中的downaftermilliseconds设置了判断主观下线的时间长度,如果实例在downaftermilliseconds毫秒内,返回的都是无效回复,那么Sentinel会认为该实例已(主观)下线,修改其flags状态为SRISDOWN。如果多个Sentinel监视一个服务,有可能存在多个Sentinel的downaftermilliseconds配置不同,这个在实际生产中要注意(主观下线:所谓主观下线,就是单个Sentinel认为某个实例下线(有可能是接收不到订阅,之间的网络不通等等原因))五、当主观下线的节点是主节点时,此时该哨兵3节点会通过指令sentinelismasterdownbyaddr寻求其它哨兵节点对主节点的判断,如果其他的哨兵也认为主节点主观下线了,则当认为主观下线的票数超过了quorum(选举)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线,一般情况下,每个Sentinel会以每10秒一次的频率向它已知的所有主服务器和从服务器发送INFO命令,当一个主服务器被标记为客观下线时,Sentinel向下线主服务器的所有从服务器发送INFO命令的频率,会从10秒一次改为每秒一次六、Sentinel和其他Sentinel协商客观下线的主节点的状态,如果处于SDOWN状态,则自动选出新的主节点,将剩余从节点指向新的主节点进行数据复制新主选举原理(自动故障转移):Sentinel状态数据结构中保存了主服务的所有从服务信息,领头Sentinel按照如下的规则从从服务列表中挑选出新的主服务:过滤掉主观下线的节点选择slavepriority最高的节点,如果有则返回没有就继续选择选择出复制偏移量最大的系节点,因为复制便宜量越大则数据复制的越完整,如果有就返回了,没有就继续下一步选择runid最小的节点通过slaveofnoone命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点将已下线的主节点设置成新的主节点的从节点,当其回复正常时,复制新的主节点,变成新的主节点的从节点,同理,当已下线的服务重新上线时,Sentinel会向其发送slaveof命令,让其成为新主的从哨兵lerder选举流程:如果主节点被判定为客观下线之后,就要选取一个哨兵节点来完成后面的故障转移工作,选举出一个leader的流程如下:每个在线的哨兵节点都可以成为领导者,当它确认主节点下线时,会向其它哨兵发ismasterdownbyaddr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者如果征求投票的哨兵发现自己在选举的票数大于等于num(sentinels)21时,将成为领导者,如果没有超过,继续重复选举服务下线注意事项:主观下线:单个哨兵节点认为某个节点故障时出现的情况,一般出现主观下线的节点为从节点时,不需要与其他哨兵协商,当前哨兵可直接对改节点完成下线操作客观下线:当一个节点被哨兵判定为主观下线时,这个节点是主节点,那么会和其他哨兵协商完成下线操作的情况被称为客观下线(客观下线只存在于主节点)面试官:那你觉得哨兵真正的实现了高可用吗?或者说你认为哨兵机制完美了嘛?我:刚刚在之前我提到过,哨兵解决了之前主从存在的一些问题,具体如下:哨兵机制优点:解决了之前主从切换需要人工干预问题,保证了一定意义上的高可用哨兵机制缺点:全量数据同步仍然会导致线上出现短暂卡顿写入QPS仍然受到主节点单机限制,对于写入并发较高的项目无法满足需求仍然存在主从复制时的木桶效应问题,存储容量受到节点群中最小内存机器限制10。3。代理式集群面试官:嗯嗯,对于类似于淘宝、新浪微博之类的互联网项目,那么怎么做到真正意义上的高可用呢?我:之前的哨兵并不算真正意义上的集群,只解决了人工切换问题,如果需要大规模的写入支持,或者缓存数据量巨大的情况下只能够通过加机器内存的形式来解决,但是长此已久并不是一个好的方案,而在Redis3。0之前官方却并没有相对应的解决方案,不过在Redis3。0之前却有很多其他的解决方案的提出以及落地,比如:TwemProxy:TwemProxy是一种代理分片机制,由Twitter开源。Twemproxy作为代理,可接受来自多个程序的访问,按照路由规则,转发给后台的各个Redis服务器,再原路返回。这个方案顺理成章地解决了单个Redis实例承载能力的问题。当然,Twemproxy本身也是单点,需要用Keepalived做高可用方案。这么些年来,Twemproxy是应用范围最广、稳定性最高、最久经考验的分布式中间件。只是,他还有诸多不方便之处。Twemproxy最大的痛点在于,无法平滑地扩容缩容。这样增加了运维难度:业务量突增,需增加Redis服务器;业务量菱缩,需要减少Redis服务器。但对Twemproxy而言,基本上都很难操作。或者说,Twemproxy更加像服务器端静态sharding,有时为了规避业务量突增导致的扩容需求,甚至被迫新开一个基于Twemproxy的Redis集群。Twemproxy另一个痛点是,运维不友好,甚至没有控制面板。当然,由于使用了中间件代理,相比客户端直接连服务器方式,性能上有所损耗,实测结果降低20左右。
  Codis:Codis由豌豆英于2014年11月开源,基于Go、C开发,是近期涌现的、国人开发的优秀开源软件之一。现已广泛用于豌豆英的各种Redis业务场景,从各种压力测试来看,稳定性符合高效运维的要求。性能更是改善很多,最初比Twemproxy慢20;现在比Twemproxy快近100(条件:多实例,般Value长度)。Codis具有可视化运维管理界面。Codis无疑是为解决Twemproxy缺点而出的新解决方案。因此综合方面会由于Twemproxy很多。目前也越来越多公司选择Codis,Codis引入了Group的概念,每个Group包括1个Master及至少1个Slave,这是和Twemproxy的区别之一。这样做的好处是,如果当前Master有问题,则运维人员可通过Dashboard自助式切换到Slave,而不需要小心翼翼地修改程序配置文件。为支持数据热迁移(AutoRebalance),出品方修改了RedisServer源码,并称之为CodisServer,Codis采用预先分片(PreSharding)机制,事先规定好了,分成1024个slots(也就是说,最多能支持后端1024个CodisServer),这些路由信息保存在ZooKeeper中。不足之处有对Redis源码进行了修改,以及代理实现本身会有的问题。我:实则代理分片的原理也很简单,类似于代理式的分库分表的实现,之前我们是直接连接Redis,然后对Redis进行读写操作,现在则是连接代理,读写操作全部交由代理来处理分发到具体的Redis实例,而集群的组成就很好的打破了之前的一主多从架构,形成了多主多从的模式,每个节点由一个个主从来构建,每个节点存储不同的数据,每个节点都能够提供读写服务,从而做到真正意义上的高可用,具体结构如下:面试官:嗯,那么为什么现在一般公司在考虑技术选型的时候为什么不考虑这两种方案呢?我:因为使用代理之后能够去解决哨兵存在的问题,但是凡事有利必有弊,代理式集群具体情况如下:优点:打破了传统的一主多从模型,允许多主存在,写入QPS不再受到单机限制数据分片存储,每个节点存储的数据都不同,解决之前主从架构存在的存容问题每个节点都是独立的主从,数据同步并不是真正的全量,每个节点同步数据时都只是同步该节点上master负责的一部分数据缺点:由于使用了代理层来打破之前的架构模型,代理层需要承担所有工作代理层需要维护,保证高可用代理层需要实现服务动态感知、注册与监听代理层需要承载所有客户端流量代理层需要处理所有分发请求由于数据并不存在与同一台机器,Redis的很多命令不再完美支持,如set的交集、并集、差集等10。4。去中心化分片式集群面试官:那么既然代理分片式的集群存在这么多需要考虑解决的问题,现在如果让你做架设,做技术选型你会考虑那种方案呢?我:我会考虑Redis3。x之后的Rediscluster去中心化分片式集群,Rediscluster在Redis3。0中推出,支持Redis分布式集群部署模式。采用无中心分布式架构。所有的Redis节点彼此互联(PINGPONG机制),内部使用二进制协议优化传输速度和带宽节点的fail是通过集群中超过半数的节点检测失效时才生效。客户端与Redis节点直连,不需要中间proxy层。客户端不需要连接集群所有节点连接集群中任何一个可用节点即可,减少了代理层,大大提高了性能。Rediscluster把所有的物理节点映射到〔016383〕slot上,cluster负责维护nodeslotkey之间的关系。目前Jedis已经支持Rediscluster。从计算架构或者性能方面无疑Rediscluster是最佳的选择方案。面试官:那你能讲讲Rediscluster集群的原理吗?我:RedisCluster在设计中没有使用一致性哈希(ConsistencyHashing),而是使用数据分片(Sharding)引入哈希槽(hashSlot)来实现;一个RedisCluster包含16384(016383)个哈希槽,存储在RedisCluster中的所有键都会被映射到这些slot中,集群中的每个键都属于这16384个哈希槽中的一个,集群使用公式slotCRC16(key)16384来计算key属于哪个槽,其中CRC16(key)语句用于计算key的CRC16校验和。集群中的每个主节点(Master)都负责处理16384个哈希槽中的一部分,当集群处于稳定状态时,每个哈希槽都只由一个主节点进行处理,每个主节点可以有一个到N个从节点(Slave),当主节点出现宕机或网络断线等不可用时,从节点能自动提升为主节点进行处理。
  假设我此时向Redis发送一条命令:setname竹子爱熊猫,那么Redis会使用CRC16算法计算KEY值,CRC16(name),类似于一个HASH函数,完成后会得到一个数字,假设此时计算完name后得到的结果是26384,那么会拿着这个计算完成之后的结果总槽数,2638416384得到结果为10000,那么keyname的这个值应该被放入负责10000这个HashSlot存储,如上图中,会被放入到第三个节点存储,当再次get这个缓存时同理(Redis底层的GossIP原理由于本篇篇幅过长则不再阐述)。十一、Redis版本新特性面试官:既然你在前面提到过这么多版本之间都有不同的变化,那么我最后考考你Redis不同的版本之间有什么区别吧我:(心想:这不就是考我新特性吗,Redis问这么久我都扛不住了,嗓子都冒烟了)好的好的,具体如下:Redis3。x:支持集群在2。6基础上再次加大原子性命令支持Redis4。x:主从数据同步机制:4。0之前仅支持pync1,4。x之后支持psync2线程DELFLUSH优化,新的UNLINK与DEL作用相同,FLUSHALLFLUSHDB中添加了ASYNC选项,Redis现在可以在不同的线程中删除后台的key而不会阻塞服务器慢日志记录客户端来源IP地址,这个小功能对于故障排查很有用处混合RDBAOF格式新的管理命令:MEMORY:能够执行不同类型的内存分析:内存问题的故障排除(使用MEMORYDOCTOR,类似于LATENCYDOCTOR),报告单个键使用的内存量,更深入地报告Redis内存使用情况SWAPDB:能够完全立即(无延迟)替换同实例下的两个Redis数据库(目前我们业务没啥用)内存使用和性能改进:Redis现在使用更少的内存来存储相同数量的数据Redis现在可以对使用的内存进行碎片整理,并逐渐回收空间Redis5。x:新的流数据类型(Streamdatatype)新的Redis模块API:定时器、集群和字典APIRDB可存储LFU和LRU信息Rediscli中的集群管理器从Ruby(redistrib。rb)移植到了C语言代码。执行rediscliclusterhelp命令以了解更多信息新的有序集合(sortedset)命令:ZPOPMINMAX和阻塞变体(blockingvariants)升级Activedefragmentation至v2版本增强HyperLogLog的实现更好的内存统计报告许多包含子命令的命令现在都有一个HELP子命令优化客户端频繁连接和断开连接时,使性能表现更好升级Jemalloc至5。1版本引入CLIENTUNBLOCK和CLIENTID新增LOLWUT命令在不存在需要保持向后兼容性的地方,弃用masterslave术语网络层中的差异优化Lua相关的改进:将Lua脚本更好地传播到replicasAOFLua脚本现在可以超时并在副本中进入BUSY状态引入动态的HZ(DynamicHZ)以平衡空闲CPU使用率和响应性对Redis核心代码进行了重构并在许多方面进行了改进,许多错误修复和其他方面的改进Redis6。x:ACL:在Redis5版本之前,Redis安全规则只有密码控制还有通过rename来调整高危命令比如flushdbKEYSshutdown等。Redis6则提供ACL的功能对用户进行更细粒度的权限控制:接入权限:用户名和密码可以执行的命令可以操作的KEY新的Redis通信协议:RESP3Clientsidecaching客户端缓存:基于RESP3协议实现的客户端缓存功能。为了进一步提升缓存的性能,将客户端经常访问的数据cache到客户端。减少TCP网络交互,提升RTIO多线程:O多线程其实指客户端交互部分的网络IO交互处理模块多线程,而非执行命令多线程。作者不想将执行命令多线程是因为要避免复杂性、锁的效率低下等等。此次支持IO多线程的设计大体如下:
  工具支持Cluster集群:Redis6。0版本后redissrc目录下提供的大部分工具开始支持Cluster集群ModulesAPI:Redis6中模块API开发进展非常大,因为RedisLabs为了开发复杂的功能,从一开始就用上Redis模块。Redis可以变成一个框架,利用Modules来构建不同系统,而不需要从头开始写然后还要BSD许可。Redis一开始就是一个向编写各种系统开放的平台Disque:Disque作为一个RedisModule使用足以展示Redis的模块系统的强大。集群消息总线API、屏蔽和回复客户端、计时器、模块数据的AOF和RDB等等面试官:嗯嗯,小伙子你前途无量呀,今天晚上方便入职吗?我:。。。。。。。。。。

你的孩子,放心交给邻居照看吗?前几天在小区里陪孩子玩的时候,同小区的一个老奶奶,大约五六十岁的样子,因为着急去接大孙女,就把小孙子拜托我看着。她的大孙女上小学二年级,小孙子上幼儿园大班,我刚开始是推脱的,建议她姜广涛或将10年以上刑期据爆料者透露,姜广涛的批捕日期早在8月下旬就已经准确告知,但外界一直以为是谣言,甚至不少粉丝都还在为其洗白,以至于该网友询问了相关部门。北京人民检察院在回复中表示姜广涛一案在2021991年,刘德华和周星驰表演胸口碎大石!结果华仔当场吐血!1991年,刘德华和周星驰表演胸口碎大石!结果华仔当场吐血!1991年华东水灾轰动全国,香港娱乐圈所有的明星大腕云集同一个舞台,上百艺人登台义演,各显神通。有表演飞车的,有表演胸口失孤原型郭振结婚,寻亲团组团恭喜被指蹭热度?今日,失孤原型郭刚堂的儿子郭振结婚,全网祝福,刘德华委派香港导演前来送上祝福,寻亲团也是不远万里去祝贺。有网友觉得,寻亲团的人去道贺,无非就是为了蹭热度罢了。这几年,寻亲成功的消息陈明昊梅婷联袂领衔孟京辉作品第七天首次亮相北京舞台剧照孟京辉戏剧工作室供图中新网北京3月19日电(记者应妮)由余华原著,孟京辉导演,陈明昊梅婷黄湘丽等人共同演绎的第七天正在北京保利剧院上演。第七天以普通人杨飞死后七日的所见所闻,以他是谁卫国平隐瞒的这个真相,藏着他对陈山河内疚的真正原因张译主演的他是谁开播即成爆款,云合数据显示,无论是霸屏榜还是全舆情热度榜,他是谁都排在了Top1。这些数据不仅再次证明了张译的市场号召力,更是对这部剧台前幕后创作人员工作的高度认可为人不识李丰田,阅尽网剧也枉然01。hr中国的网剧创作者,都应该感谢陈凯歌。2005年末,无极的广告铺天盖地,一位叫胡戈的青年被吸引进电影院。看完无极,顿时觉得被忽悠了。一股不能不吐槽的冲动积蓄在胡戈胸中。随着五大卫视重排座次,但已不算华山论剑作者魏妮卡编辑李春晖2023年第一季度电视剧最大的喜剧和悲剧莫过于三个月了,狂飙还在称霸电视端。这莫不是要重演当年卫视重播十几遍甄嬛传换台都能接上剧情的佳话?开年在央八首播,以平均微博之夜内娱大团建微博之夜的热闹(抓马)场面,小编已经能想象到了。比如杨幂刘诗诗杨颖刘亦菲等85花同框比美,还记得上次微博之夜的抓马场面吗?热水袋洗碗手套拍照土味拍照女明星们表面耐心配合再比如让一身EA将发布星战绝地幸存者新故事预告3月21日播出今日(3月18日),EA官方宣布星球大战绝地幸存者官方故事预告片将于北京时间3月21日零点播出,视频时长约1分47秒左右。冒险动作游戏星球大战绝地幸存者由重生工作室制作,并由EA发让房间充满电影音效的DefinitiveTechnologyDymension系列音箱从成立开始至今已经有超过25年,拥有独家双级式扬声器技术的美国音响品牌DefinitiveTechnology狄分尼提2023年2月又宣布发表了全新的一个系列DefinitiveT
社保交了15年,中间停了6年,现在又进厂了,还能不能交,应怎么交?从你已缴费的年限推算,你应是1997年参加社保缴费,缴了15年后,(2012年停缴2018年断缴费6年),2018年又参加单位,继续续缴社保费。问这断缴费是否能补缴?按照社保政策,朋友向我借了20万突然去世,没借条,问她老婆要,不理我怎么办?1。必须有证据证明你朋友生前确实向你借过钱而且你确实交付了20万元钱2。必须有证据证明该笔借款系你朋友夫妻双方共同向你借钱的事实3。必须有证据证明你向他转账的记录就是借钱给你朋友的45岁是什么一种感觉?我45,躺平一年,基本财务自由。天天锻炼读书,身材恢复到大学时了,三高也好多了。基本不参加聚会,也不和同学朋友来往。回首前半生,傻头傻脑,执拗又理想化,乱冲乱拼。对原生家庭问题看得安徽省的平均退休金高吗?为什么感觉许多旅游景点很少见安徽老人?谢谢邀请!安徽省的平均退休养老金在全国属于中下等级的,从目前全省退休人员(企事公务员)的养老金平均人2700元的情况分析看,安徽省虽然与江苏浙江和上海市都属中国地理位置上的华东片区为什么感觉农村老人都在家里去世,而城里老人却都在医院去世?我干风水这一行多年所见的,就是两种区别在于思想观念的不同。相对而言,城里的老人对生命十分看重。只要还有一口气在,都是想要在医院里接受治疗尽量延续生命!他们认为,死在医院里。儿女算是水浒传中,梁山好汉们总吃大鱼大肉,那么,他们的钱从哪里来的?成长新视点,问答分享。水泊梁山,大聚义时好汉有108人,人马有7万之众,是梁上最为顶峰的时期。这个时候,他们的势力已经扩张到附近的几个州县,原来的扈家庄祝家庄等都被收拾。梁山,本身老婆今年退休,之前交的是居民医保可以改为职工医保吗?先提醒一句,有职工医保就不用再交城乡医保了,出事只能赔一个,多买浪费钱!医保是国家给咱们的基本福利,建议人手一份,这点钱不能省。省了这一部分小钱,看病花大钱吃药可能你会承担更多,看接下来5年,在合肥哪个位置买房最好?买房自然要看政府的发展方向。三十年河东三十年河西,当年合肥的顶梁柱瑶海区现在沦落成合肥的老破旧。十五年前合肥人眼中偏得不能再偏的滨湖现在成了香饽饽。所以合肥市往哪发展,自然在那里买有人知道或听说过,有些农村过去发生过哪些不为人知的可怕故事?五十多年前的一天,村里一片竹园开花以后就衰败枯死了,于是生产队安排村民把死竹子连根挖掉。就在一个人挖竹根的时候,突然发现竹根下的泥土里有很多石凳石门槛条还有破碗陶罐等等后来村民陆陆大家都见过什么样的奇葩乞丐骗人的?大约1962年,尽管多地遭受自然灾害,独有我县风调雨顺,五谷丰登,村民安居乐业。那年夏天早晨,忽然有两个陌生男人前来报信,言称阿坤姑父病危,阿坤姑母嫁到郓城县某村,离娘家30多里,社保交满15年了,还有必要继续交下去吗?社会保险(养老医疗失业工伤生育,五险合一简称为社保),如养老保险缴费够15年了,可以选择继续往下交,也可以不交。每年社保缴费基数会调整,大概升幅10左右,偌要缴费,要看个人经济能力
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网