一、Redis根本概念

  • 面试官心思: 靠!手上活都没干完又名我过来面试,这不耽搁我事么,今儿又得加班补活了……..咦,这小伙子简历不错啊,先考考它Redis……….
  • 面试官: 谈谈你对Redis的理解?
  • 我: RedisANSI C言语编写的一个依据内存的高功用键值对(key-value)的NoSQL数据库,一般用于架起在Java程序与数据库之间用作缓存层,为了防止DB磁盘IO功率过低形成的恳求堵塞、呼应缓慢等问题,用来补偿DB与Java程序之间的功用差距,一同,也能够在DB吞吐跟不上体系并发量时,防止恳求直接落入DB然后起到保护DB的作用。
  • Redis一般除了缓存DB数据之外还能够运用它丰厚的数据类型及指令来完结一些其他功用,比方:计数器、用户在线状况、排行榜、session存储等,一同Redis的功用也十分可观,经过官方给出的数据显现能够到达10w/s的QPS处理,可是在生产环境的实测成果大概读取QPS在7-9w/s,写入QPS在6-8w/s左右(注:与机器功用也有关),一同Redis也供给事务、耐久化、高可用等一些机制的支撑。

二、Redis根本数据类型与常用指令

  • 面试官: 刚刚听你说到了能够运用它丰厚的数据类型及指令来完结一些其他功用,那你跟我讲讲Redis的一些常用指令。
  • 我: Redis常用的一些指令的话一般是都是关于根本数据类型的操作指令以及一些全局指令…..叭啦叭啦叭……,如下:
指令 作用
keys * 回来一切键(keys还能用来搜索,比方keys h*:搜索一切以h开端的键)
dbsize 回来键数量,假如存在许多键,线上禁止运用此指令
exists key 查看键是否存在,存在回来 1,不存在回来 0
del key 删去键,回来删去键个数,删去不存在键回来 0
ttl key 查看键存活时刻,回来键剩下过期时刻,不存在回来-1
expire key seconds 设置过期时刻(单位:s),成功回来1,失利回来0
expireat key timestamp 设置key在某个时刻戳(准确到秒)之后过期
pexpire key milliseconds 设置过期时刻(单位:ms),成功回来1,失利回来0
persist key 去掉过期时刻
monitor 实时监听并回来Redis服务器接纳到的一切恳求信息
shutdown 把数据同步保存到磁盘上,并封闭Redis服务
info 查看当时Redis节点信息
……. …….
当然了,一般也是记住一些常用的指令,可是 更多指令参阅:Redis指令大全,因为Redis指令和JVM参数相同,只要记住能够这样做就行了,可是详细的能够去参阅相关文档资料。
  • 面试官: 嗯嗯,不错,那再接着讲讲Redis的根本数据类型以及你是在项目中怎样运用它们的吧!
  • 我: Redis数据类型在之前是五种,可是现在的版别中存在九种,分别为:字符串(strings/string)、散列(hashes/hash)、列表(lists/list)、调集(sets/set)、有序调集(sorted sets/zset)以及后续的四种数据类型:bitmaps、hyperloglogs、地舆空间(geospatial)、音讯(Streams),不过不管是哪种数据类型Redis都不会直接将它放在内存中存储,而是转而内部运用RedisObject来存储以及表明一切类型的key-value(说着说着我拿出了纸和笔,给面试官画了一张图):
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!

    Redis内部运用一个RedisObject目标来表明一切的keyvalueRedisObject最首要的信息如上图所示:type表明一个value目标详细是何种数据类型,encoding是不同数据类型在Redis内部的存储方法。比方:type=string表明value存储的是一个一般字符串,那么encoding能够是raw或许int,而关于其他数据类型的内部编码完结我登时再拿起笔chua~ chua~ chua
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
  • 我接着答复: 下面我再简略讲讲Redis的根本数据类型以及它们的运用场景:
类型 描述 特性 场景
string 二进制安全 能够存储任何元素(数字、字符、音视频、图片、目标…..) 计数器、分布式锁、字符缓存、分布式ID生成、session同享、秒杀token、IP限流等
hash 键值对存储,类似于Map调集 适合存储目标,能够将目标属性一个个存储,更新时也能够更新单个属性,操作某一个字段 目标缓存、购物车等
list 双向链表 增删快 栈、行列、有限调集、音讯行列、音讯推送、堵塞行列等
set 元素不能重复,每次获取无序 添加、删去、查找的杂乱度都是O(1),供给了求交集、并集、差集的操作 抽奖活动、朋友圈点赞、用户(微博好友)重视、相关重视、一同重视、好友引荐(或许认识的人)等
sorted set 有序调集,每个元素有一个对应的分数,不允许元素重复 依据分数进行排序,假如分数持平,以key值的 ascii 值进行排序 产品点评标签(好评、中评、差评等)、排行榜等
bitmaps Bitmaps是一个字节由 8 个二进制位组成 在字符串类型上面界说的位操作 在线用户核算、用户拜访核算、用户点击核算等
hyperloglog Redis2.8.9版别添加了 HyperLogLog结构。Redis HyperLogLog是用来做基数核算的算法。 用于进行基数核算,不是调集,不保存数据,只记载数量而不是详细数据 核算独立UV等
geospatial Redis3.2版别新增的数据类型:GEO对地舆位置的支撑 以将用户给定的地舆位置信息贮存起来, 并对这些信息进行操作 地舆位置核算
stream Redis5.0之后新增的数据类型 支撑发布订阅,一对多消费 音讯行列

PS:HyperLogLog的长处是,在输入元素的数量或许体积十分十分大时,核算基数所需的空间总是固定 的、而且是很小的。在 Redis 里面,每个HyperLogLog键只需求花费12 KB内存,就能够核算接近2^64个不同元素的基数。这和核算基数时,元素越多耗费内存就越多的调集构成鲜明比照。可是,因为HyperLogLog只会依据输入元素来核算基数,而不会贮存输入元素自身,所以HyperLogLog不能像调集那样,回来输入的各个元素(中心是基数估算算法,终究数值存在必定误差误差范围:基数估计的成果是一个带有0.81%规范过错的近似值,耗空间极小,每个hyperloglog key占用了12K的内存用于标记基数,pfadd指令不是一次性分配12K内存运用,会跟着基数的添加内存逐步增大,Pfmerge指令兼并后占用的存储空间为12K,不管兼并之前数据量多少)

三、Redis缓存及共同性、雪崩、击穿与穿透问题

  • 面试官发问: 那么你们在运用Redis做为缓存层的时分是怎样经过Java操作Redis的呢?

  • 我的心思: 这问题不是送出题吗…..

  • 我: Java操作Redis的客户端有许多,比方springData中的RedisTemplate,也有SpringCache集成Redis后的注解方式,当然也会有一些Jedis、Lettuce、Redisson等等,而咱们运用的是Lettuce以及Redisson........

  • 面试官发问: 那你们在运用Redis作为缓存的时分有没有遇到什么问题呢?

  • 我: 咳咳,是的,的确遇到了以及考虑到了一些问题,比方缓存共同性、雪崩、穿透与击穿,关于RedisMySQL之间的数据共同性问题其实也考虑过许多计划,比方先删后改,延时双删等等许多计划,可是在高并发状况下仍是会形成数据的不共同性,所以关于DB与缓存之间的强共同性必定要确保的话那么就关于这部分数据不要做缓存,操作直接走DB,可是假如这个数据比较热门的话那么仍是会给DB形成很大的压力,所以在咱们的项目中仍是选用先删再改+过期的计划来做的,尽管也会存在数据的不共同,可是牵强也能接受,因为毕竟运用缓存拜访快的一同也能减轻DB压力,而且自身选用缓存就需求接受必定的数据推迟性和时刻短的不共同性,咱们只能采纳适宜的战略来下降缓存和数据库间数据不共同的概率,而无法确保两者间的强共同性。适宜的战略包括适宜的缓存更新战略,适宜的缓存筛选战略,更新数据库后及时更新缓存、缓存失利时添加重试机制等。

  • 面试官话锋一转: 打断一下,你刚刚说到了运用缓存能让拜访变快,那么你能不能讲讲Redis为什么快呢?

  • 我的心思: 好家伙,这一手来的我猝不及防……

  • 硬着头发答复: Redis快的原因嘛其实能够从多个维度来看待:

    • 一、Redis彻底依据内存
    • 二、Redis整个结构类似于HashMap,查找和操作杂乱度为O(1),不需求和MySQL查找数据相同需求产生随机磁盘IO或许全表
    • 三、Redis关于客户端的处理是单线程的,选用单线程处理一切客户端恳求,防止了多线程的上下文切换和线程竞争形成的开支
    • 四、底层选用select/epoll多路复用的高效非堵塞IO模型
    • 五、客户端通信协议选用RESP,简略易读,防止了杂乱恳求的解析开支
  • 面试官露出姨父般的慈笑: 嗯嗯,还不错,那你持续谈谈刚刚的缓存雪崩、穿透与击穿的问题吧

  • 我: 好的,先说缓存雪崩吧,缓存雪崩形成的原因是因为咱们在做缓存时为了确保内存运用率,一般在写入数据时都会给定一个过期时刻,而便是因为过期时刻的设置有或许导致许多的热门key在同一时刻内悉数失效,此刻来了许多恳求拜访这些key,而Redis中却没有这些数据,然后导致一切恳求直接落入DB查询,形成DB呈现瓶颈或许直接被打宕导致雪崩状况的产生。关于处理计划的的话也能够从多个维度来考虑:

    • 一、设置热门数据永不过期,防止热门数据的失效导致许多的相同恳求落入DB
    • 二、错开过期时刻的设置,依据事务以及线上状况合理的设置失效时刻
    • 三、运用分布式锁或许MQ行列使得恳求串行化,然后防止同一时刻恳求许多落入DB(功用会遭到很大的影响)
  • 面试官: 那缓存穿透呢?指的是什么?又该怎样处理?

  • 我喝了口水接着答复: 缓存穿透这个问题是因为恳求参数不合理导致的,比方对外暴露了一个接口getUser?userID=xxx,而数据库中的userID是从1开端的,当有黑客经过这个接口带着不存在的ID恳求时,比方:getUser?userID=-1,恳求会先来到Redis中查询缓存,可是发现没有对应的数据然后转向DB查询,可是DB中也无此值, 所以也无法写入数据到缓存,而黑客就经过这一点运用“肉鸡”等手段疯狂恳求这个接口,导致呈现许多Redis不存在数据的恳求落入DB,然后导致DB呈现瓶颈或许直接被打宕机,整个体系陷入瘫痪。

  • 面试官: 嗯,那又该假如防止这种状况呢?

  • 我: 处理计划也有好几种呢:

    • 一、做IP限流与黑名单,防止同一IP一会儿发送许多恳求
    • 二、关于恳求做不合法校验,关于带着不合法参数的恳求直接过滤
    • 三、关于DB中查询不存在的数据写入Redis“Not Data”并设置时刻短的过期时刻,下次恳求能够直接被拦截在Redis而不会落入DB
    • 四、布隆过滤器
  • 面试官: 那接下来的缓存击穿呢?又是怎样回事?怎样处理?

  • 我: 这个简略,缓存击穿和缓存雪崩有点类似,都是因为恳求的key过期导致的问题,可是不同点在于失效key的数量,关于雪崩而言指的是许多的key失效导致许多恳求落入DB,而关于击穿而言,指的是某一个热门key忽然过期,而这个时分又忽然又许多的恳求来查询它,可是在Redis中却并没有查询到成果然后导致一切恳求悉数打向DB,导致在这个时刻DB直接被打穿。处理计划的话也是有多种:

    • 一、设置热门key永不过期
    • 二、做好Redis监控,恳求串行化拜访(功用较差)
    • 运用mutex锁机制:便是在缓存失效的时分(判别拿出来的值为空),不是当即去load db,而是先运用缓存东西的某些带成功操作回来值的操作(比方RedisSETNX或许MemcacheADD)去set一个mutex key,当操作回来成功时,再进行load db的操作并回设缓存;不然,就重试整个get缓存的方法,代码完结如下:
public Result get(int ID){
    RedisResult = Redis.get(ID);
     if(RedisResult != null){
         return RedisResult;
     }
     if(Redis.setnx("update:" + ID) != "0"){
         DBResult = DB.selectByID(ID);
         if(DBResult != null){ // 防止缓存穿透
             Redis.set(ID,DBResult);
             Redis.del("update:" + ID);
             return DBResult;
         }
         Redis.set(ID,"Not Data");
         return "抱歉,当时查询暂时没有找到数据......";
     }
     Thread.sleep(2);
     return get(ID);
}

四、Redis八种筛选战略与三种删去战略

4.1. 八种键筛选(过期)战略

  • 面试官: 你前面说到过,Redis的数据是悉数放在内存中的,那么有些数据我也没有设置过期时刻,导致了许多的内存糟蹋,当我有新的数据需求写入内存不够用了怎样办?
  • 我的心里: 好家伙,问个Redis筛选战略这么拐弯抹角…….
  • 我: 我想你是想问内存筛选战略吧,Redis在5.0之前为咱们供给了六种筛选战略,而5.0为咱们供给了八种,可是大体上来说这些lru、lfu、random、ttl四种类型,如下:
战略 概述
volatile-lru 从已设置过期时刻的数据会集挑选最近最少运用的数据筛选,没有设置过期时刻的key不会被筛选,这样就能够在添加内存空间的一同确保需求耐久化的数据不会丢掉。
volatile-ttl 从已设置过期时刻的数据会集挑选即将过期的数据筛选,ttl值越小,越先被筛选。
volatile-random 从已设置过期时刻的数据会集任意挑选数据筛选
volatile-lfu 从已设置过期时刻的数据集挑选运用频率最低的数据筛选
allkeys-lru 从数据会集挑选最近最少运用的数据筛选,该战略要筛选的key面向的是全体key调集,而非过期的key调集(运用最广泛的战略)。
allkeys-lfu 从数据会集挑选运用频率最低的数据筛选
allkeys-random 从数据集(server.db[i].dict)中任意挑选数据筛选
no-enviction(驱逐) 禁止驱逐数据,这也是默许战略。意思是当内存不足以容纳新入数据时,新写入操作就会报错,恳求能够持续进行,线上任务也不能持续进行,选用no-enviction战略能够确保数据不被丢掉。
  • 我喘了口气接着说:
    • 一、在Redis中,数据有一部分拜访频率较高,其他部分拜访频率较低,或许无法预测数据的运用频率时,设置allkeys-lru是比较适宜的。
    • 二、假如一切数据拜访概率大致持平时,能够挑选allkeys-random
    • 三、假如研制者需求经过设置不同的ttl来判别数据过期的先后次序,此刻能够挑选volatile-ttl战略。
    • 四、假如希望一些数据能长期被保存,而一些数据能够被筛选掉时,挑选volatile-lruvolatile-random都是比较不错的。
    • 五、因为设置expire会耗费额外的内存,假如计划防止Redis内存在此项上的糟蹋,能够选用allkeys-lru战略,这样就能够不再设置过期时刻,高效运用内存了。
    • maxmemory-policy:参数装备筛选战略。maxmemory:约束内存巨细。

4.2. 三种键删去战略

  • 面试官:RedisKey删去战略有了解过吗?
  • 我: 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两种方式耐久化,可是因为AOFRDB都存在各自的缺陷,所以在4.x版别之后Redis还供给一种新的耐久化机制:混合型耐久化(可是终究生成的文件仍是.AOF)。
  • 面试官: 那你仔细讲讲这几种耐久化机制吧
  • 我: 好的,RDB耐久化把内存中当时进程的数据生成快照(.rdb)文件保存到硬盘的进程,有手动触发和主动触发:
    • 主动触发:
      • Redis RDB耐久化默许敞开
        save 900 1 — 900s内存在1个写操作
        save 300 10 — 300s内存在10个写操作
        save 60 10000 — 60s内存在10000个写操作
        如上是RDB的主动触发的默许装备,当操作满足如上条件时会被触发。
    • 手动触发:
      • save:堵塞当时 Redis,直到RDB耐久化进程完结为止,若内存实例比较大 会形成长时刻堵塞,线上环境不主张用它
      • bgsaveRedis 进程履行fork操作创立子进程,由子进程完结耐久化,堵塞时 间很短(微秒级),是save的优化,在履行Redis-cli shutdown封闭Redis服务时或履行flushall指令时,假如没有敞开AOF耐久化,主动履行bgsave,bgsave履行流程如下:
        Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!

        而且RDB 是在某个时刻点将数据写入一个临时文件,耐久化完毕后,用这个临时文件替换上次耐久化的文件,重启时加载这个文件到达数据康复。
  • RDB优缺陷:
    • 长处:运用单独子进程来进行耐久化,主进程不会进行任何 IO 操作,确保了 Redis 的高功用;而且RDB文件存储的是压缩的二进制文件,适用于备份、全量仿制,可用于灾祸备份,一同RDB文件的加载速度远超于AOF文件。
    • 缺陷:RDB是距离一段时刻进行耐久化,假如耐久化之间的时刻内产生毛病,会呈现数据丢掉。所以这种方法更适合数据要求不谨慎的时分,因为RDB无法做到实时耐久化,而且每次都要创立子进程,频频创立成本过高;备份时占用内存,因为Redis 在备份时会独立创立一个子进程,将数据写入到一个临时文件(需求的内存是原本的两倍);还有一点,RDB文件保存的二进制文件存在新老版别不兼容的问题。

5.1. AOF耐久化

  • 我:AOF耐久化方法能很好的处理RDB耐久化方法形成的数据丢掉,AOF耐久化到硬盘中的并不是内存中的数据快照,而是和MySQLbinlog日志相同记载写入指令,AOF的耐久化战略也有三种:
    • appendfsync always:同步耐久化方式,每次产生数据更改都将指令追加到AOF文件,因为每次写入时都记载会产生许多磁盘IO,然后功用会遭到影响,可是数据最安全。
    • appendfsync everysecRedis敞开AOF后的缺省装备,异步操作,每秒将写入指令追加到AOF文件,假如在刚耐久化之后的一秒内宕机,会形成1S的数据丢掉。
    • appendfsync noRedis并不直接调用文件同步,而是交给操作体系来处理,操作体系能够依据buffer填充状况/通道闲暇时刻等择机触发同步;这是一种一般的文件操作方法。功用较好,在物理服务器毛病时,数据丢掉量会因OS装备有关。
    • AOF耐久化机制优缺陷:
      • 长处:依据不同的fsync战略能够确保数据丢掉风险降到最低,数据能够确保是最新的,fsync是后台线程在处理,所以关于处理客户端恳求的线程并不影响。
      • 缺陷:文件体积因为保存的是一切指令会比RDB大上许多,而且数据康复时也需求从头履行指令,在重启时康复数据的时刻往往会慢许多。尽管fsync并不是共用途理客户端恳求线程的资源来处理的,可是这两个线程仍是在同享同一台机器的资源,所以在高并发场景下也会必定遭到影响。
  • AOF机制重写: 跟着Redis在线上运转的时刻越来越久,客户端履行的指令越来越多,AOF的文件也会越来越大,当AOF到达必定程度巨细之后再经过AOF文件康复数据是反常缓慢的,那么关于这种状况Redis在敞开AOF耐久化机制的时分会存在AOF文件的重写,缺省装备是当AOF文件比上一次重写时的文件巨细添加100%而且文件巨细不小于64MB时会对整个AOF文件进行重写然后到达“瘦身”的目的(这里的100%64MB能够经过auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb来调整)。而AOF rewrite操作便是“压缩”AOF文件的进程,当然 Redis 并没有选用“依据原aof文件”来重写的方法,而是采纳了类似snapshot的方法:依据copy-on-write,全量遍历内存中数据,然后逐个序列到aof文件中。因此AOF rewrite能够正确反应当时内存数据的状况,这正是咱们所需求的;*rewrite进程中,关于新的变更操作将仍然被写入到原 AOF文件中,一同这些新的变更操作也会被 Redis 收集起来(buffer,copy-on-write方法下,最极端的或许是一切的key都在此期间被修正,将会耗费2倍内存),当内存数据被悉数写入到新的aof文件之后,收集的新的变更操作也将会一同追加到新的aof文件中,此后将会重命名新的aof文件为appendonly.aof, 此后一切的操作都将被写入新的aof文件。假如在rewrite进程中,呈现毛病,将不会影响原AOF文件的正常作业,只要当rewrite完结之后才会切换文件,因为rewrite进程是比较牢靠的,触发rewrite的时机能够经过装备文件来声明,一同Redis中能够经过bgrewriteaof指令人工干预。
    • AOF耐久化进程如下:
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
  • 面试官: 那你项目中Redis选用的是那种耐久化方法呢?
  • 我: 在咱们项目中考虑到了Redis中不仅仅仅仅用来做缓存,其间还存储着一些MySQL中不存在的数据,所以数据的安全性要求比较高,而RDB因为并不是实时的耐久化,会呈现数据丢掉,可是选用AOF方式在重启、灾备、搬迁的时分进程反常耗时,也并不理想,所以在咱们线上是一同选用两种方式的,而AOF+RDB两种方式一同敞开时Redis重启又该加载谁呢?(说着说着我又掏出了纸笔给面试官画了如下一幅图):
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!

5.3. 4.x之后的混合型耐久化

当然在Redis4.x之后推出了混合型耐久化机制,因为RDB尽管加载快可是存在数据丢掉,AOF数据安全可是加载缓慢,Redis为了处理这个问题,带来了一个新的耐久化选项——混合耐久化。将RDB文件的内容和增量的AOF日志文件存在一同。这里的AOF日志不再是全量 的日志,而是自耐久化开端到耐久化完毕的这段时刻产生的增量AOF日志,通常这部分AOF日志很小。Redis重启的时分,能够先加载RDB的内容,然后再重放增量AOF日志,就能够彻底代替之前的AOF全量文件重放,康复功率因此大幅得到进步(混合型耐久化终究生成的文件后缀是.aof,能够经过redis.conf文件中aof-use-rdb-preamble yes装备敞开)。 – 混合型耐久化长处:结合了RDBAOF的长处,使得数据康复的功率大幅进步 – 混合型耐久化缺陷:兼容性不好,Redis-4.x新增,尽管终究的文件也是.aof格局的文件,但在4.0之前版别都不辨认该aof文件,一同因为前部分是RDB格局,阅览性较差

六、Redis的事务机制

  • 面试官: 已然Redis是数据库,那么它支不支撑事务呢?
  • 我: Redis作为数据库当然是支撑事务的,只不过Redis的事务机制是弱事务,相对来说比较鸡肋,官方给出如下几个指令来进行Redis的事务操控:
    • MULTI:标记一个事务块的开端
    • DISCARD:撤销事务,放弃履行事务块内的一切指令
    • EXEC:履行一切事务块内的指令
    • UNWATCH:撤销WATCH指令对一切key的监督
    • WATCH key [key ...]:监督一个(或多个)key,假如在事务履行之前这个(或这些)key被其他指令所改动,那么事务将被打断

七、Redis内存模型及内存划分

  • 面试官: 嗯嗯,挺不错,那你关于Redis的内存模型以及内存的划分有去了解过嘛?
  • 我: 了解过的,Redis的内存模型咱们能够经过客户端衔接之后运用内存核算指令info memory去查看,如下:
    • used_memory(单位:字节): Redis分配器分配的内存总量,包括运用的虚拟内存(稍后会详解)
    • used_memory_rss(单位:字节): Redis进程占有操作体系的内存;除了分配器分配的内存之外,used_memory_rss还包括进程运转自身需求的内存、内存碎片等,可是不包括虚拟内存
    • 阐明: used_memory是从Redis角度得到的量,used_memory_rss是从操作体系角度得到的量。二者之所以有所不同,一方面是因为内存碎片和Redis进程运转需求占用内存,使得used_memory_rss或许更大;另一方面虚拟内存的存在,使得used_memory或许更大
    • mem_fragmentation_ratio: 内存碎片比率,该值是used_memory_rss / used_memory;一般大于1,且该值越大,内存碎片份额越大。而小于1,阐明Redis运用了虚拟内存,因为虚拟内存的媒介是磁盘,比内存速度要慢许多,当这种状况呈现时,应该及时排查,假如内存不足应该及时处理,如添加Redis节点、添加Redis服务器的内存、优化运用等;一般来说,mem_fragmentation_ratio1.03左右是比较健康的状况(关于jemalloc分配器来说),因为在实践运用中,Redis的数据量会比较大,此刻进程运转占用的内存与Redis数据量和内存碎片比较,都会小得多,mem_fragmentation_ratio便成了衡量Redis内存碎片率的参数
    • mem_allocator: Redis运用的内存分配器,在编译时指定;能够是libc 、jemalloc或tcmalloc,默许是jemalloc
  • 我接着说:Redis作为内存数据库,在内存中存储的内容首要是数据,但除了数据以外,Redis的其他部分也会占用内存。Redis的内存占用能够划分为以下几个部分:
    • 数据: 作为数据库,数据是最首要的部分;这部分占用的内存会核算在used_memory
    • 进程自身运转需求的内存: Redis主进程自身运转必定需求占用内存,如代码、常量池等等,这部分内存大约几兆,在大多数生产环境中与Redis数据占用的内存比较能够忽略。这部分内存不是由jemalloc分配,因此不会核算在used_memory中。除了主进程外,Redis创立的子进程运转也会占用内存,如Redis履行AOF、RDB重写时创立的子进程。当然,这部分内存不归于Redis进程,也不会核算在used_memoryused_memory_rss中。
    • 缓冲内存: 缓冲内存包括客户端缓冲区、仿制积压缓冲区、AOF缓冲区等;其间,客户端缓冲存储客户端衔接的输入输出缓冲;仿制积压缓冲用于部分仿制功用;AOF缓冲区用于在进行AOF重写时,保存最近的写入指令。在了解相应功用之前,不需求知道这些缓冲的细节;这部分内存由jemalloc分配,因此会核算在used_memory中。
    • 内存碎片: 内存碎片是Redis在分配、收回物理内存进程中产生的。例如,假如对数据的更改频频,而且数据之间的巨细相差很大,或许导致Redis开释的空间在物理内存中并没有开释,但Redis又无法有用运用,这就构成了内存碎片。内存碎片不会统 计在used_memory中。
      • 内存碎片的产生与对数据进行的操作、数据的特色等都有关;此外,与运用的内存分配器也有联系:假如内存分配器设计合理,能够尽或许的削减内存碎片的产生。假如Redis服务器中的内存碎片已经很大,能够经过安全重启的方法减小内存碎片:因为重启之后,Redis从头从备份文件中读取数据,在内存中进行重排,为每个数据从头挑选适宜的内存单元,减小内存碎片。
  • 面试官:Redis的同享目标你有了解过吗?
  • RedisObject目标中有一个refcountrefcount记载的是该目标被引用的次数,类型为整型。refcount的作用,首要在于目标的引用计数和内存收回。当创立新目标时,refcount初始化为1;当有新程序运用该目标时,refcount加1;当目标不再被一个新程序运用时,refcount减1;当refcount变为0时,目标占用的内存会被开释。 Redis中被屡次运用的目标(refcount>1),称为同享目标。Redis为了节约内存,当有一些目标重复呈现时,新的程序不会创立新的目标,而是仍然运用本来的目标。这个被重复运用的目标,便是同享目标。现在同享目标仅支撑整数值的字符串目标。
    • 同享目标的详细完结:
      Redis的同享目标现在只支撑整数值的字符串目标。之所以如此,实践上是对内存和CPU(时刻)的平衡:同享目标尽管会下降内存耗费,可是判别两个目标是否持平却需求耗费额外的时刻。关于整数值,判别操作杂乱度为O(1);关于一般字符串,判别杂乱度为O(n);而关于哈希、列表、调集和有序调集,判别的杂乱度为O(n^2)。 尽管同享目标只能是整数值的字符串目标,可是5种类型都或许运用同享目标(如哈希、列表等的元素能够运用)。
      就现在的完结来说,Redis服务器在初始化时,会创立10000个字符串目标,值分别是0-9999的整数值;当Redis需求运用值为0-9999的字符串目标时,能够直接运用这些同享目标。10000这个数字能够经过调整参数Redis_SHARED_INTEGERS(4.0中是OBJ_SHARED_INTEGERS)的值进行改动。
      同享目标的引用次数能够经过object refcount指令查看。

八、Redis虚拟内存

  • 面试官: 刚刚听你说到过Redis的虚拟内存,那你能详细讲讲它是怎样会事吗?
  • 我: 首要阐明下Redis的虚拟内存与操作体系虚拟内存不是一码事,可是思路和目的都是相冋的。便是暂时把不常常拜访的数据从內存交流到磁盘中,然后腾出宝贵的内存空间。关于Redis这样的内存数据库,内存总是不够用的。除了能够将数据分割到多个Redis实例以外。别的的能够进步数据库容量的方法便是运用虚拟内存技能把那些不常常拜访的数据交流到磁盘上。假如咱们存储的数据总是有少部分数据被常常拜访,大部分数据很少被拜访,关于网站来说的确总是只要少量用户常常活跃。当少量数据被常常拜访时,运用虚拟内存不但能进步单台 Redis数据库服务器的容量,而且也不会对功用形成太多影响Redis没有运用操作体系供给的虚拟内存机制而是自己在用户态完结了自己的虚拟内存机制。首要的理由有以下两点:
    • 一、操作体系的虚拟内存是以4k/页为最小单位进行交流的。而Redis的大多数目标都远小于4k,所以一个操作体系页上或许有多个Redis目标。别的 Redis的调集目标类型如list,set或许行在于多个操作体系页上。终究或许形成只要10%的key被常常拜访,可是一切操作体系页都会被操作体系以为是活跃的,这样只要内存真实耗尽时操作体系才会进行页的交流
    • 二、比较操作体系的交流方法,Redis能够将被交流到磁盘的目标进行压缩,保存到磁盘的目标能够去除指针和目标元数据信息。一般压缩后的目标会比内存中的目标小10倍。这样Redis的虛拟内存会比操作体系的虚拟内存少做许多I0操作
  • 我: 而关于Redis虚拟内存的装备也存在于redis.conf文件中,如下:
    • vm-enabled ves:#敞开虚拟内存功用
    • vm-swap-file ../redis.swap:#交流出来value保存的文件途径
    • Vm-max-memory 268435456:# Redis运用的最大内存上限(256MB),超越上限后Redis开端交流value到磁盘swap文件中。主张设置为体系闲暇内存的60%-80%
    • vm-page-size 32:#每个 Redis页的巨细32个字节
    • vm-pages 134217728:#最多在文件中运用多少个页,交流文件的巨细
    • vm-max-threads 8:#用于履行value目标换入换出的作业线程数量,0表明不运用作业线程(概况后边介绍)。
  • 我: Redis的虚拟内存在设计上为了确保key的查询速度,只会将value交流到swap文件。假如是因为太多key很小的value形成的内存问题,那么Redis的虚拟内存并不能处理问题。和操作体系相同 Redis也是按页来交流目标的。Redis规矩同一个页只能保存一个目标。可是一个目标能够保存在多个页中。在Redis运用的内存没超越vm-max-memory之前是不会交流任何value的。当超越最大内存约束后,Redis会挑选把较老的目标交流到swap文件中去。假如两个目标相同老会优先交流比较大的目标,准确的交流核算公式swappability=age*1og(size_Inmemory)。关于vm-page-size的设置应该依据自己运用将页的巨细设置为能够容纳大多数目标的尺度。太大了会糟蹋磁盘空间,太小了会形成交流文件呈现过多碎片。关于交流文件中的每个页, Redis会在内存顶用一个1bit值来对应记载页的闲暇状况。所以像上面装备中页数量(vm pages134217728)会占用16MB内存用来记载页的空內状况。vm-max-threads表明用做交流任务的作业线程数量。假如大于0引荐设为服务器的cpu的中心数。假如是0则交流进程在上线程进行。详细作业方式如下:
    • 堵塞方式(vm-max-threads=0):
      • 换出:主线程定期检査发现内存超出最大上限后,会直接以堵塞的方法,将选中的目标保存到swap文件中,并开释目标占用的内存空间,此进程会一直重复直到下面条件满足。
        • 内存运用降到最大约束以下
        • swap文件满了
        • 简直悉数的目标都被交流到磁盘了
      • 换入:当有客户端恳求已经被换出的value时,主线程会以阳塞的方法从swap文件中加载对应的value目标,加载时此刻会堵塞所客户端。然后处理该客户端的恳求
    • 非堵塞方式(vm-max-threads>0):
      • 换出:当主线程检测到运用内存超越最大上限,会将选中要父换的目标信息放到一个行列中父给作业线程后台处理,主线程会持续处理客户端恳求
      • 换入:假如有客户端恳求的key已终被换出了,主线程会先阳塞发出指令的客户端,然后将加载目标的信息放到一个行列中,让作业线程去加载。加载完毕后作业线程告诉主线程。主线程再履行客户端的指令。这种方法只堵塞恳求的value是已经被 换出key的客户端总的来说堵塞方法的功用会好些,因为不需求线程同步、创立线程和康复被堵塞的客户端等开支。可是也相应的牺牡了呼应性。作业线稈方法主线程不会阳塞在磁盘1O上,所以呼应性更好。假如咱们的运用不太常常产生换入换出,而且也不太在意有点推迟的话引荐运用堵塞方法(详细介绍参阅)。

九、Redis客户端通信RESP协议

  • 面试官: 那你再简略讲讲Redis的客户端通信的RESP协议吧
  • 我: 这个比较简略,RESPRedis序列化协议,Redis客户端RESP协议与Redis服务器通信。RESP协议在Redis 1.2中引进,但在Redis 2.0中成为与Redis服务器通信的规范方法。这个通信方法便是Redis客户端完结的协议。RESP实践上是一个序列化协议,它支撑以下数据类型:简略字符串、过错、整数、大容量字符串和数组。当咱们在客户端中像Redis发送操作指令时,比方:set name 竹子爱熊猫 这条指令,不会直接以这种格局的方式发送到Redis Server,而是经过RESP的序列化之后再发送给Redis履行,而AOF耐久化机制耐久化之后生成的AOF文件中也并不是存储set name 竹子爱熊猫这个指令,而是存储RESP序列化之后的指令,RESP的特色如下:
    • 完结简略
    • 能被核算机快速地解析
    • 可读性好能够被人工解析

十、Redis高可用机制:主从仿制、岗兵、署理式/分片式集群

10.1. 主从仿制

  • 面试官: 假如在整个架构中参加Redis作为缓存层,那么会在Java程序与DB之间多出一层拜访,假定Redis挂了那么Java程序这边又会抛出反常导致一切恳求死在这里然后导致整个体系的不可用,那么怎样防止Redis呈现这类的单点毛病呢?
  • 我: Redis已然这么受欢迎那么这些问题它都供给了相关的处理计划的,Redis有供给了主从、岗兵、署理集群与分片集群的高可用机制来确保呈现单点问题时能够及时的切换机器以保障整个体系不遭到影响。可是后续的三种高可用机制都是依据主从的根底上来完结的,所以我先说说Redis的主从仿制。尽管咱们之前讲到过耐久化机制能够确保数据重启状况下也不丢掉,可是由所以存在于一台服务器上的,假如机器磁盘坏了、机房爆炸(打趣~)等也会导致数据丢掉,而主从仿制能够将数据同步到多台不同机器,也能够确保在主节点宕机时任然对外供给服务,还能够做到经过读写别离的方式进步全体缓存事务群吞吐量。一般在线上环境时咱们去建立主从环境时,为了确保数据共同性,从节点是不允许写的,而是经过仿制主节点数据的方式保障数据同步。所以在整个Redis节点群中只能一同运转存在一台主,其他的全为从节点,示意图如下(读的QPS能够经过对从节点的线性扩容来进步):
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
  • 面试官: 那你能详细说下主从数据同步的进程吗?
  • 我: 能够的,Redis2.8之前运用sync[runId][offset]同步指令,Redis2.8之后运用psync[runId][offset]指令。两者不同在于,sync指令仅支撑全量仿制进程,psync支撑全量和部分仿制。介绍同步之前,先介绍几个概念:
    • runId:每个Redis节点启动都会生成仅有的uuid,每次Redis重启后,runId都会产生改变
    • offset:主节点和从节点都各自保护自己的主从仿制偏移量offset,当主节点有写入指令时,offset=offset+指令的字节长度。从节点在收到主节点发送的指令后,也会添加自己的offset,并把自己的offset发送给主节点。这样,主节点一同保存自己的offset和从节点的offset,经过比照offset来判别主从节点数据是否共同
    • repl_back_buffer:仿制缓冲区,用来存储增量数据指令
    • 主从数据同步详细进程如下:
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
  • 我: 当然psync指令除了支撑全量仿制之外还支撑部分仿制,因为在做主从数据同步时会导致主从机器网络带宽开支十分大,而在2.8之前Redis仅支撑全量仿制,这样十分简单导致Redis在线上呈现网络瓶颈,而在2.8之后的增量(部分)仿制,用于处理在主从仿制中因网络闪断等原因形成的数据丢掉场景,当slave再次连上master后,假如条件允许,master会补发丢掉数据给slave。因为补发的数据远远小于全量数据,能够有用防止全量仿制的过高开支。部分仿制流程图如下(仿制缓存区溢出也会导致全量仿制):
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
  • PS: psync[runid][offset]指令三种回来值:
    • FULLRESYNC:第一次衔接,进行全量仿制
    • CONTINUE:进行部分仿制
    • ERR:不支撑psync指令,进行全量仿制
  • 面试官: 那你觉得主从机制有什么好处?存在什么问题?
  • 我: 主从机制其实也是为后续的一些高可用机制打下了根底,可是自身也存在一些缺陷,当然在后续的高可用机制中得到了处理,详细如下:
    • 长处:
      • 能够为后续的高可用机制打下根底
      • 在耐久化的根底上能够将数据同步到其他机器,在极端状况下做到灾备的作用
      • 能够经过主写从读的方式完结读写别离进步Redis全体吞吐,而且读的功用能够经过对从节点进行线性扩容无限进步
    • 缺陷:
      • 全量数据同步时假如数据量比较大,在之前会导致线上时刻短性的卡顿
      • 一旦主节点宕机,从节点晋升为主节点,一同需求修正运用方的主节点地址,还需求指令一切从节点去仿制新的主节点,整个进程需求人工干预
      • 写入的QPS功用遭到主节点约束,尽管主从仿制能够经过读写别离来进步全体功用,可是只要从节点能够做到线性扩容升吞吐,写入的功用仍是遭到主节点约束
      • 木桶效应,整个Redis节点群能够存储的数据容量遭到一切节点中内存最小的那台约束,比方一主两从架构:master=32GB、slave1=32GB、slave2=16GB,那么整个Redis节点群能够存储的最大容量为16GB

10.2. 岗兵机制

  • 面试官: 你刚刚说到过后续的高可用机制能处理这些问题,你说的是岗兵吗?那你再说说岗兵机制
  • 我: 好的,岗兵机制的确能够处理之前主从存在的一些问题,如图:
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!

    上图所示是现在企业中常用的Redis架构,一主两从三岗兵架构,Redis Sentinel(岗兵)首要功用包括主节点存活检测、主从运转状况检测、主动毛病搬运、主从切换。Redis Sentinel最小装备是一主一从。RedisSentinel体系能够用来办理多个Redis节点,该体系能够履行以下四个任务:
    • 监控:不断查看主服务器和从服务器是否正常运转
    • 告诉:当被监控的某个Redis服务器呈现问题,Sentinel经过API脚本向办理员或许其他运用程序发出告诉
    • 主动毛病搬运:当主节点不能正常作业时,Sentinel会开端一次主动的毛病搬运操作,它会将与失效主节点是主从联系的其间一个从节点升级为新的主节点,而且将其他的从节点指向新的主节点,这样就不需求人工干预进行主从切换
    • 装备供给者:在Sentinel方式下,客户端运用在初始化时衔接的是Sentinel节点调集,从中获取主节点的信息
  • 面试官: 那你能讲讲岗兵机制原理吗?
  • 我: 能够的,岗兵的作业原理如下:
    • 一、每个岗兵节点每10秒会向主节点和从节点发送info指令获取最级联结构图,岗兵装备时只要装备对主节点的监控即可,经过向主节点发送info,获取从节点的信息,并当有新的从节点参加时能够马上感知到
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
    • 二、每个岗兵节点每隔2秒会向Redis数据节点的指定频道上发送该岗兵节点关于主节点的判别以及当时岗兵节点的信息,一同每个岗兵节点也会订阅该频道,来了解其它岗兵节点的信息及对主节点的判别,其实便是经过音讯publishsubscribe来完结的
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
    • 三、隔1秒每个岗兵依据自己info获取的级联结构信息,会向主节点、从节点及其他岗兵节点发送一次ping指令做一次心跳检测,这个也是岗兵用来判别节点是否正常的重要依据
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
    • 四、Sentinel会以每秒一次的频率向一切与其建立了指令衔接的实例(master、salve、其他Sentinel)发ping指令,经过判别ping回复是有用回复仍是无效回复来判别实例是否在线/存活(对该Sentinel来说是“片面在线”),Sentinel装备文件中的down-after-milliseconds设置了判别片面下线的时刻长度,假如实例在down-after-milliseconds毫秒内,回来的都是无效回复,那么Sentinel会以为该实例已(片面)下线,修正其flags状况为SRI_S_DOWN。假如多个Sentinel监督一个服务,有或许存在多个Sentineldown-after-milliseconds装备不同,这个在实践生产中要注意(片面下线:所谓片面下线,便是单个Sentinel以为某个实例下线(有或许是接纳不到订阅,之间的网络不通等等原因))
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
    • 五、当片面下线的节点是主节点时,此刻该岗兵3节点会经过指令sentinel is-masterdown-by-addr寻求其它岗兵节点对主节点的判别,假如其他的岗兵也以为主节点片面下线了,则当以为片面下线的票数超越了quorum(推举)个数,此刻岗兵节点则以为该主节点的确有问题,这样就客观下线了,大部分岗兵节点都同意下线操作,也就说是客观下线,一般状况下,每个Sentinel会以每10秒一次的频率向它已知的一切主服务器和从服务器发送INFO指令,当一个主服务器被标记为客观下线时,Sentinel向下线主服务器的一切从服务器发送INFO指令的频率,会从10秒一次改为每秒一次
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
    • 六、Sentinel和其他Sentinel洽谈客观下线的主节点的状况,假如处于SDOWN状况,则主动选出新的主节点,将剩下从节点指向新的主节点进行数据仿制
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
    • 新主推举原理(主动毛病搬运):Sentinel状况数据结构中保存了主服务的一切从服务信息,领头Sentinel依照如下的规矩从从服务列表中挑选出新的主服务:
      • 过滤掉片面下线的节点
      • 挑选slave-priority最高的节点,假如有则回来没有就持续挑选
      • 挑选出仿制偏移量最大的系节点,因为仿制廉价量越大则数据仿制的越完整,假如有就回来了,没有就持续下一步
      • 挑选run_id最小的节点
      • 经过slaveof no one指令,让选出来的从节点成为主节点;并经过slaveof指令让其他节点成为其从节点
      • 将已下线的主节点设置成新的主节点的从节点,当其回复正常时,仿制新的主节点,变成新的主节点的从节点,同理,当已下线的服务从头上线时,Sentinel会向其发送slaveof指令,让其成为新主的从
    • 岗兵lerder推举流程:假如主节点被断定为客观下线之后,就要选取一个岗兵节点来完结后边的毛病搬运作业,推举出一个leader的流程如下:
      • 每个在线的岗兵节点都能够成为领导者,当它确认主节点下线时,会向其它岗兵发is-master-down-by-addr指令,寻求判别并要求将自己设置为领导者,由领导者处理毛病搬运
      • 当其它岗兵收到此指令时,能够同意或许拒绝它成为领导者
      • 假如寻求投票的岗兵发现自己在推举的票数大于等于num(sentinels)/2+1时,将成为领导者,假如没有超越,持续重复推举…………
    • 服务下线注意事项:
      • 片面下线:单个岗兵节点以为某个节点毛病时呈现的状况,一般呈现片面下线的节点为从节点时,不需求与其他岗兵洽谈,当时岗兵可直接对改节点完结下线操作
      • 客观下线:当一个节点被岗兵断定为片面下线时,这个节点是主节点,那么会和其他岗兵洽谈完结下线操作的状况被称为客观下线(客观下线只存在于主节点)
  • 面试官: 那你觉得岗兵真实的完结了高可用吗?或许说你以为岗兵机制完美了嘛?
  • 我: 刚刚在之前我说到过,岗兵处理了之前主从存在的一些问题,详细如下:
    • 岗兵机制长处:
      • 处理了之前主从切换需求人工干预问题,确保了必定意义上的高可用
    • 岗兵机制缺陷:
      • 全量数据同步仍然会导致线上呈现时刻短卡顿
      • 写入QPS仍然遭到主节点单机约束,关于写入并发较高的项目无法满足需求
      • 仍然存在主从仿制时的木桶效应问题,存储容量遭到节点群中最小内存机器约束

10.3. 署理式集群

  • 面试官: 嗯嗯,关于类似于淘宝、新浪微博之类的互联网项目,那么怎样做到真实意义上的高可用呢?
  • 我: 之前的岗兵并不算真实意义上的集群,只处理了人工切换问题,假如需求大规模的写入支撑,或许缓存数据量巨大的状况下只能够经过加机器内存的方式来处理,可是长此已久并不是一个好的计划,而在Redis3.0之前官方却并没有相对应的处理计划,不过在Redis3.0之前却有许多其他的处理计划的提出以及落地,比方:
    • TwemProxyTwemProxy是一种署理分片机制,由Twitter开源。Twemproxy作为署理, 可接受来自多个程序的拜访,依照路由规矩,转发给后台的各个Redis服务器,再原路回来。这个计划顺理成章地处理了单个Redis实例承载能力的问题。当然,Twemproxy自身也是单点,需求用Keepalived做高可用计划。这么些年来,Twemproxy是运用范围最广、安稳性最高、 最久经考验的分布式中间件。仅仅,他还有诸多不方便之处。Twemproxy最大的痛点在于,无法滑润地扩容/缩容。这样添加了运维难度:事务量突增,需添加Redis服务器; 事务量菱缩,需求削减Redis服务器。但对Twemproxy而言,根本上都很难操作。或许说,Twemproxy更加像服务器端静态sharding,有时为了规避事务量突增导致的扩容需求,乃至被迫新开一个依据TwemproxyRedis集群。Twemproxy另一个痛点是,运维不友爱,乃至没有操控面板。当然,因为运用了中间件署理,比较客户端直接连服务器方法,功用上有所损耗,实测成果下降20%左右。
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
    • CodisCodis由豌豆英于2014年11月开源,依据Go、C开发,是近期出现的、国人开发的优秀开源软件之一。现已广泛用于豌豆英的各种Redis事务场景,从各种压力测试来看,安稳性符合高效运维的要求。功用更是改善许多,开端比Twemproxy慢20%;现在比Twemproxy快近100% (条件:多实例,-般Value长度)。Codis具有可视化运维办理界面。Codis无疑是 为处理Twemproxy缺陷而出的新处理计划。因此归纳方面会因为Twemproxy许多。现在也越来越多公司挑选CodisCodis引进了Group的概念,每个Group包括1个Master及至少1个Slave,这是和Twemproxy的差异之一。这样做的好处是,假如当时Master有问题,则运维人员可经过Dashboard“自助式”切换到Slave,而不需求小心谨慎地修正程序装备文件。为支撑数据热搬迁(AutoRebalance),出品方修正了RedisServer源码,并称之为Codis ServerCodis选用预先分片(Pre-Sharding)机制,事前规矩好了,分红1024个slots (也便是说,最多能支撑后端1024个CodisServer),这些路由信息保存在ZooKeeper中。 不足之处有对Redis源码进行了修正,以及署理完结自身会有的问题。
      Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
  • 我: 实则署理分片的原理也很简略,类似于署理式的分库分表的完结,之前咱们是直接衔接Redis,然后对Redis进行读写操作,现在则是衔接署理,读写操作悉数交由署理来处理分发到详细的Redis实例,而集群的组成就很好的打破了之前的一主多从架构,构成了多主多从的方式,每个节点由一个个主从来构建,每个节点存储不同的数据,每个节点都能够供给读写服务,然后做到真实意义上的高可用,详细结构如下:
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
  • 面试官: 嗯,那么为什么现在一般公司在考虑技能选型的时分为什么不考虑这两种计划呢?
  • 我: 因为运用署理之后能够去处理岗兵存在的问题,可是凡事有利必有弊,署理式集群详细状况如下:
    • 长处:
      • 打破了传统的一主多从模型,允许多主存在,写入QPS不再遭到单机约束
      • 数据分片存储,每个节点存储的数据都不同,处理之前主从架构存在的存容问题
      • 每个节点都是独立的主从,数据同步并不是真实的“全量”,每个节点同步数据时都仅仅同步该节点上master担任的一部分数据
    • 缺陷:
      • 因为运用了署理层来打破之前的架构模型,署理层需求承担一切作业
      • 署理层需求保护,确保高可用
      • 署理层需求完结服务动态感知、注册与监听
      • 署理层需求承载一切客户端流量
      • 署理层需求处理一切分发恳求
      • 因为数据并不存在与同一台机器,Redis的许多指令不再完美支撑,如set的交集、并集、差集等

10.4. 去中心化分片式集群

  • 面试官: 那么已然署理分片式的集群存在这么多需求考虑处理的问题,现在假如让你做架起,做技能选型你会考虑那种计划呢?
  • 我: 我会考虑Redis3.x之后的Redis-cluster去中心化分片式集群,Redis-clusterRedis3.0中推出,支撑Redis分布式集群部署方式。选用无中心分布式架构。一切的Redis节点彼此互联(PING-PONG机制),内部运用二进制协议优化传输速度和带宽节点的fail是经过集群中超越对折的节点检测失效时才生效.客户端与Redis节点直连,不需求中间proxy层.客户端不需求衔接集群一切节点衔接集群中任何一个可用节点即可,削减了署理层,大大进步了功用。Redis-cluster把一切的物理节点映射到[0-16383]slot上,cluster担任保护node <-> slot <-> key之间的联系。现在Jedis已经支撑Redis-cluster。从核算架构或许功用方面无疑Redis-cluster是最佳的挑选计划。
  • 面试官: 那你能讲讲Redis-cluster集群的原理吗?
  • 我: Redis Cluster在设计中没有运用共同性哈希(ConsistencyHashing),而是运用数据分片(Sharding)引进哈希槽(hashSlot)来完结;一个RedisCluster包括16384(0~16383)个哈希槽,存储在RedisCluster中的一切键都会被映射到这些slot中,集群中的每个键都归于这16384个哈希槽中的一个,集群运用公式slot=CRC16(key)% 16384来核算key归于哪个槽,其间CRC16(key)语句用于核算keyCRC16校验和。 集群中的每个主节点(Master)都担任处理16384个哈希槽中的一部分,当集群处于安稳状况时,每个哈希槽都只由一个主节点进行处理,每个主节点能够有一个到N个从节点(Slave),当主节点呈现宕机或网络断线等不可用时,从节点能主动进步为主节点进行处理。
    Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!

    假定我此刻向Redis发送一条指令:set name 竹子爱熊猫,那么Redis会运用CRC16算法核算KEY值,CRC16(name),类似于一个HASH函数,完结后会得到一个数字,假定此刻核算完name后得到的成果是26384,那么会拿着这个核算完结之后的成果%总槽数,26384%16384得到成果为10000,那么key=name的这个值应该被放入担任10000这个HashSlot存储,如上图中,会被放入到第三个节点存储,当再次get这个缓存时同理(Redis底层的GossIP原理因为本篇篇幅过长则不再阐述)。

十一、Redis版别新特性

  • 面试官: 已然你在前面说到过这么多版别之间都有不同的改变,那么我最终考考你Redis不同的版别之间有什么差异吧

  • 我: (心想:这不便是考我新特性吗,Redis问这么久我都扛不住了,喉咙都冒烟了)好的好的,详细如下:

    • Redis3.x
      • 支撑集群
      • 在2.6根底上再次加大原子性指令支撑
    • Redis4.x
      • 主从数据同步机制:4.0之前仅支撑pync1,4.x之后支撑psync2
      • 线程DEL/FLUSH优化,新的UNLINKDEL作用相同,FLUSHALL/FLUSHDB中添加了ASYNC选项,Redis现在能够在不同的线程中删去后台的key而不会堵塞服务器
      • 慢日志记载客户端来历IP地址,这个小功用关于毛病排查很有用途
      • 混合RDB + AOF格局
      • 新的办理指令:
        • MEMORY:能够履行不同类型的内存分析:内存问题的毛病扫除(运用MEMORY DOCTOR,类似于LATENCYDOCTOR),陈述单个键运用的内存量,更深化地陈述Redis内存运用状况
        • SWAPDB:能够彻底当即(无推迟)替换同实例下的两个Redis数据库(现在咱们事务没啥用)
      • 内存运用和功用改善:
        • Redis现在运用更少的内存来存储相同数量的数据
        • Redis现在能够对运用的内存进行碎片整理,并逐步收回空间
    • Redis5.x
      • 新的流数据类型(Stream data type)
      • 新的 Redis 模块API:守时器、集群和字典API
      • RDB可存储LFULRU信息
      • Redis-cli中的集群办理器从Ruby (redis-trib.rb)移植到了C言语代码。履行redis-cli --cluster help指令以了解更多信息
      • 新的有序调集(sorted set)指令:ZPOPMIN/MAX和堵塞变体(blocking variants)
      • 升级Active defragmentationv2版别
      • 增强HyperLogLog的完结
      • 更好的内存核算陈述
      • 许多包括子指令的指令现在都有一个HELP子指令
      • 优化客户端频频衔接和断开衔接时,使功用体现更好
      • 升级Jemalloc5.1版别
      • 引进CLIENT UNBLOCKCLIENT ID
      • 新增LOLWUT指令
      • 在不存在需求保持向后兼容性的当地,弃用”master/slave”术语
      • 网络层中的差异优化
      • Lua相关的改善:
        • Lua脚本更好地传播到replicas / AOF
        • Lua脚本现在能够超时并在副本中进入-BUSY状况
      • 引进动态的HZ(Dynamic HZ)以平衡闲暇CPU运用率和呼应性
      • Redis中心代码进行了重构并在许多方面进行了改善,许多过错修正和其他方面的改善
    • Redis6.x
      • ACL:在Redis 5版别之前,Redis安全规矩只要暗码操控还有经过rename来调整高危指令比方flushdb/KEYS*/shutdown等。Redis6则供给ACL的功用对用户进行更细粒度的权限操控:
        • 接入权限:用户名和暗码
        • 能够履行的指令
        • 能够操作的KEY
      • 新的Redis通信协议:RESP3
      • Client side caching客户端缓存:依据RESP3协议完结的客户端缓存功用。为了进一步进步缓存的功用,将客户端常常拜访的数据cache到客户端。削减TCP网络交互,进步RT
      • IO多线程:O多线程其实指客户端交互部分的网络IO交互处理模块多线程,而非履行指令多线程。作者不想将履行指令多线程是因为要防止杂乱性、锁的功率低下等等。此次支撑IO多线程的设计大体如下:
        Redis综述篇:与面试官彻夜长谈Redis缓存、持久化、淘汰机制、哨兵、集群底层原理!
      • 东西支撑Cluster集群:Redis6.0版别后redis/src目录下供给的大部分东西开端支撑Cluster集群
      • Modules APIRedis 6中模块API开发进展十分大,因为Redis Labs为了开发杂乱的功用,从一开端就用上Redis模块。Redis能够变成一个结构,运用Modules来构建不同体系,而不需求从头开端写然后还要BSD答应。Redis一开端便是一个向编写各种体系敞开的渠道
      • DisqueDisque作为一个RedisModule运用足以展现Redis的模块体系的强大。集群音讯总线API、屏蔽和回复客户端、计时器、模块数据的AOF和RDB等等
  • 面试官: 嗯嗯,小伙子你前途无量呀,今日晚上方便入职吗?

  • 我: ……….