一、Redis根本概念
-
面试官心思: 靠!手上活都没干完又名我过来面试,这不耽搁我事么,今儿又得加班补活了……..咦,这小伙子简历不错啊,先考考它
Redis
………. -
面试官: 谈谈你对
Redis
的理解? -
我:
Redis
是ANSI 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
内部运用一个RedisObject
目标来表明一切的key
和value
,RedisObject
最首要的信息如上图所示:type
表明一个value
目标详细是何种数据类型,encoding
是不同数据类型在Redis
内部的存储方法。比方:type=string
表明value
存储的是一个一般字符串,那么encoding
能够是raw
或许int
,而关于其他数据类型的内部编码完结我登时再拿起笔chua~ chua~ chua
: -
我接着答复: 下面我再简略讲讲
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
作为缓存的时分有没有遇到什么问题呢? -
我: 咳咳,是的,的确遇到了以及考虑到了一些问题,比方缓存共同性、雪崩、穿透与击穿,关于
Redis
与MySQL
之间的数据共同性问题其实也考虑过许多计划,比方先删后改,延时双删等等许多计划,可是在高并发状况下仍是会形成数据的不共同性,所以关于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
,而是先运用缓存东西的某些带成功操作回来值的操作(比方Redis
的SETNX
或许Memcache
的ADD
)去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-lru
或volatile-random
都是比较不错的。 - 五、因为设置
expire
会耗费额外的内存,假如计划防止Redis
内存在此项上的糟蹋,能够选用allkeys-lru
战略,这样就能够不再设置过期时刻,高效运用内存了。 -
maxmemory-policy
:参数装备筛选战略。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
)文件保存到硬盘的进程,有手动触发和主动触发:- 主动触发:
-
Redis
RDB耐久化默许敞开
save 900 1
— 900s内存在1个写操作
save 300 10
— 300s内存在10个写操作
save 60 10000
— 60s内存在10000个写操作
如上是RDB的主动触发的默许装备,当操作满足如上条件时会被触发。
-
- 手动触发:
-
save
:堵塞当时Redis
,直到RDB
耐久化进程完结为止,若内存实例比较大 会形成长时刻堵塞,线上环境不主张用它 -
bgsave
:Redis
进程履行fork
操作创立子进程,由子进程完结耐久化,堵塞时 间很短(微秒级),是save
的优化,在履行Redis-cli shutdown
封闭Redis
服务时或履行flushall
指令时,假如没有敞开AOF
耐久化,主动履行bgsave,bgsave
履行流程如下:
而且RDB 是在某个时刻点将数据写入一个临时文件,耐久化完毕后,用这个临时文件替换上次耐久化的文件,重启时加载这个文件到达数据康复。
-
- 主动触发:
- RDB优缺陷:
- 长处:运用单独子进程来进行耐久化,主进程不会进行任何 IO 操作,确保了
Redis
的高功用;而且RDB
文件存储的是压缩的二进制文件,适用于备份、全量仿制,可用于灾祸备份,一同RDB
文件的加载速度远超于AOF
文件。 - 缺陷:
RDB
是距离一段时刻进行耐久化,假如耐久化之间的时刻内产生毛病,会呈现数据丢掉。所以这种方法更适合数据要求不谨慎的时分,因为RDB
无法做到实时耐久化,而且每次都要创立子进程,频频创立成本过高;备份时占用内存,因为Redis
在备份时会独立创立一个子进程,将数据写入到一个临时文件(需求的内存是原本的两倍);还有一点,RDB
文件保存的二进制文件存在新老版别不兼容的问题。
- 长处:运用单独子进程来进行耐久化,主进程不会进行任何 IO 操作,确保了
5.1. AOF耐久化
-
我: 而
AOF
耐久化方法能很好的处理RDB
耐久化方法形成的数据丢掉,AOF
耐久化到硬盘中的并不是内存中的数据快照,而是和MySQL
的binlog
日志相同记载写入指令,AOF
的耐久化战略也有三种:-
appendfsync always
:同步耐久化方式,每次产生数据更改都将指令追加到AOF
文件,因为每次写入时都记载会产生许多磁盘IO,然后功用会遭到影响,可是数据最安全。 -
appendfsync everysec
:Redis
敞开AOF
后的缺省装备,异步操作,每秒将写入指令追加到AOF
文件,假如在刚耐久化之后的一秒内宕机,会形成1S的数据丢掉。 -
appendfsync no
:Redis
并不直接调用文件同步,而是交给操作体系来处理,操作体系能够依据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 100
与auto-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耐久化进程如下:
- AOF耐久化进程如下:
-
面试官: 那你项目中
Redis
选用的是那种耐久化方法呢? -
我: 在咱们项目中考虑到了
Redis
中不仅仅仅仅用来做缓存,其间还存储着一些MySQL
中不存在的数据,所以数据的安全性要求比较高,而RDB
因为并不是实时的耐久化,会呈现数据丢掉,可是选用AOF
方式在重启、灾备、搬迁的时分进程反常耗时,也并不理想,所以在咱们线上是一同选用两种方式的,而AOF+RDB
两种方式一同敞开时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
装备敞开)。
– 混合型耐久化长处:结合了RDB
和AOF
的长处,使得数据康复的功率大幅进步
– 混合型耐久化缺陷:兼容性不好,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_ratio
在1.03
左右是比较健康的状况(关于jemalloc
分配器来说),因为在实践运用中,Redis
的数据量会比较大,此刻进程运转占用的内存与Redis
数据量和内存碎片比较,都会小得多,mem_fragmentation_ratio
便成了衡量Redis
内存碎片率的参数 -
mem_allocator:
Redis
运用的内存分配器,在编译时指定;能够是libc 、jemalloc或tcmalloc
,默许是jemalloc
-
used_memory(单位:字节):
-
我接着说: 而
Redis
作为内存数据库,在内存中存储的内容首要是数据,但除了数据以外,Redis
的其他部分也会占用内存。Redis
的内存占用能够划分为以下几个部分:-
数据: 作为数据库,数据是最首要的部分;这部分占用的内存会核算在
used_memory
中 -
进程自身运转需求的内存:
Redis
主进程自身运转必定需求占用内存,如代码、常量池等等,这部分内存大约几兆,在大多数生产环境中与Redis
数据占用的内存比较能够忽略。这部分内存不是由jemalloc
分配,因此不会核算在used_memory
中。除了主进程外,Redis
创立的子进程运转也会占用内存,如Redis
履行AOF、RDB
重写时创立的子进程。当然,这部分内存不归于Redis
进程,也不会核算在used_memory
和used_memory_rss
中。 -
缓冲内存: 缓冲内存包括客户端缓冲区、仿制积压缓冲区、AOF缓冲区等;其间,客户端缓冲存储客户端衔接的输入输出缓冲;仿制积压缓冲用于部分仿制功用;AOF缓冲区用于在进行AOF重写时,保存最近的写入指令。在了解相应功用之前,不需求知道这些缓冲的细节;这部分内存由
jemalloc
分配,因此会核算在used_memory
中。 -
内存碎片: 内存碎片是
Redis
在分配、收回物理内存进程中产生的。例如,假如对数据的更改频频,而且数据之间的巨细相差很大,或许导致Redis
开释的空间在物理内存中并没有开释,但Redis
又无法有用运用,这就构成了内存碎片。内存碎片不会统 计在used_memory
中。- 内存碎片的产生与对数据进行的操作、数据的特色等都有关;此外,与运用的内存分配器也有联系:假如内存分配器设计合理,能够尽或许的削减内存碎片的产生。假如
Redis
服务器中的内存碎片已经很大,能够经过安全重启的方法减小内存碎片:因为重启之后,Redis
从头从备份文件中读取数据,在内存中进行重排,为每个数据从头挑选适宜的内存单元,减小内存碎片。
- 内存碎片的产生与对数据进行的操作、数据的特色等都有关;此外,与运用的内存分配器也有联系:假如内存分配器设计合理,能够尽或许的削减内存碎片的产生。假如
-
数据: 作为数据库,数据是最首要的部分;这部分占用的内存会核算在
-
面试官: 那
Redis
的同享目标你有了解过吗? - 在
RedisObject
目标中有一个refcount
,refcount
记载的是该目标被引用的次数,类型为整型。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协议吧 -
我: 这个比较简略,
RESP
是Redis
序列化协议,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能够经过对从节点的线性扩容来进步): - 面试官: 那你能详细说下主从数据同步的进程吗?
-
我: 能够的,
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
:仿制缓冲区,用来存储增量数据指令 - 主从数据同步详细进程如下:
-
-
我: 当然
psync
指令除了支撑全量仿制之外还支撑部分仿制,因为在做主从数据同步时会导致主从机器网络带宽开支十分大,而在2.8之前Redis
仅支撑全量仿制,这样十分简单导致Redis
在线上呈现网络瓶颈,而在2.8之后的增量(部分)仿制,用于处理在主从仿制中因网络闪断等原因形成的数据丢掉场景,当slave
再次连上master
后,假如条件允许,master
会补发丢掉数据给slave
。因为补发的数据远远小于全量数据,能够有用防止全量仿制的过高开支。部分仿制流程图如下(仿制缓存区溢出也会导致全量仿制): -
PS:
psync[runid][offset]
指令三种回来值:-
FULLRESYNC
:第一次衔接,进行全量仿制 -
CONTINUE
:进行部分仿制 -
ERR
:不支撑psync
指令,进行全量仿制
-
- 面试官: 那你觉得主从机制有什么好处?存在什么问题?
-
我: 主从机制其实也是为后续的一些高可用机制打下了根底,可是自身也存在一些缺陷,当然在后续的高可用机制中得到了处理,详细如下:
- 长处:
- 能够为后续的高可用机制打下根底
- 在耐久化的根底上能够将数据同步到其他机器,在极端状况下做到灾备的作用
- 能够经过主写从读的方式完结读写别离进步
Redis
全体吞吐,而且读的功用能够经过对从节点进行线性扩容无限进步
- 缺陷:
- 全量数据同步时假如数据量比较大,在之前会导致线上时刻短性的卡顿
- 一旦主节点宕机,从节点晋升为主节点,一同需求修正运用方的主节点地址,还需求指令一切从节点去仿制新的主节点,整个进程需求人工干预
- 写入的QPS功用遭到主节点约束,尽管主从仿制能够经过读写别离来进步全体功用,可是只要从节点能够做到线性扩容升吞吐,写入的功用仍是遭到主节点约束
- 木桶效应,整个
Redis
节点群能够存储的数据容量遭到一切节点中内存最小的那台约束,比方一主两从架构:master=32GB、slave1=32GB、slave2=16GB
,那么整个Redis
节点群能够存储的最大容量为16GB
- 长处:
10.2. 岗兵机制
- 面试官: 你刚刚说到过后续的高可用机制能处理这些问题,你说的是岗兵吗?那你再说说岗兵机制
-
我: 好的,岗兵机制的确能够处理之前主从存在的一些问题,如图:
上图所示是现在企业中常用的Redis
架构,一主两从三岗兵架构,Redis Sentinel
(岗兵)首要功用包括主节点存活检测、主从运转状况检测、主动毛病搬运、主从切换。Redis Sentinel
最小装备是一主一从。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
装备文件中的down-after-milliseconds
设置了判别片面下线的时刻长度,假如实例在down-after-milliseconds
毫秒内,回来的都是无效回复,那么Sentinel
会以为该实例已(片面)下线,修正其flags
状况为SRI_S_DOWN
。假如多个Sentinel
监督一个服务,有或许存在多个Sentinel
的down-after-milliseconds
装备不同,这个在实践生产中要注意(片面下线:所谓片面下线,便是单个Sentinel
以为某个实例下线(有或许是接纳不到订阅,之间的网络不通等等原因)) - 五、当片面下线的节点是主节点时,此刻该岗兵3节点会经过指令
sentinel is-masterdown-by-addr
寻求其它岗兵节点对主节点的判别,假如其他的岗兵也以为主节点片面下线了,则当以为片面下线的票数超越了quorum
(推举)个数,此刻岗兵节点则以为该主节点的确有问题,这样就客观下线了,大部分岗兵节点都同意下线操作,也就说是客观下线,一般状况下,每个Sentinel
会以每10秒一次的频率向它已知的一切主服务器和从服务器发送INFO
指令,当一个主服务器被标记为客观下线时,Sentinel
向下线主服务器的一切从服务器发送INFO
指令的频率,会从10秒一次改为每秒一次 - 六、
Sentinel
和其他Sentinel
洽谈客观下线的主节点的状况,假如处于SDOWN
状况,则主动选出新的主节点,将剩下从节点指向新的主节点进行数据仿制 - 新主推举原理(主动毛病搬运):
Sentinel
状况数据结构中保存了主服务的一切从服务信息,领头Sentinel
依照如下的规矩从从服务列表中挑选出新的主服务:- 过滤掉片面下线的节点
- 挑选
slave-priority
最高的节点,假如有则回来没有就持续挑选 - 挑选出仿制偏移量最大的系节点,因为仿制廉价量越大则数据仿制的越完整,假如有就回来了,没有就持续下一步
- 挑选
run_id
最小的节点 - 经过
slaveof no one
指令,让选出来的从节点成为主节点;并经过slaveof
指令让其他节点成为其从节点 - 将已下线的主节点设置成新的主节点的从节点,当其回复正常时,仿制新的主节点,变成新的主节点的从节点,同理,当已下线的服务从头上线时,
Sentinel
会向其发送slaveof
指令,让其成为新主的从
- 岗兵
lerder
推举流程:假如主节点被断定为客观下线之后,就要选取一个岗兵节点来完结后边的毛病搬运作业,推举出一个leader
的流程如下:- 每个在线的岗兵节点都能够成为领导者,当它确认主节点下线时,会向其它岗兵发
is-master-down-by-addr
指令,寻求判别并要求将自己设置为领导者,由领导者处理毛病搬运 - 当其它岗兵收到此指令时,能够同意或许拒绝它成为领导者
- 假如寻求投票的岗兵发现自己在推举的票数大于等于
num(sentinels)/2+1
时,将成为领导者,假如没有超越,持续重复推举…………
- 每个在线的岗兵节点都能够成为领导者,当它确认主节点下线时,会向其它岗兵发
- 服务下线注意事项:
- 片面下线:单个岗兵节点以为某个节点毛病时呈现的状况,一般呈现片面下线的节点为从节点时,不需求与其他岗兵洽谈,当时岗兵可直接对改节点完结下线操作
- 客观下线:当一个节点被岗兵断定为片面下线时,这个节点是主节点,那么会和其他岗兵洽谈完结下线操作的状况被称为客观下线(客观下线只存在于主节点)
- 一、每个岗兵节点每10秒会向主节点和从节点发送
- 面试官: 那你觉得岗兵真实的完结了高可用吗?或许说你以为岗兵机制完美了嘛?
-
我: 刚刚在之前我说到过,岗兵处理了之前主从存在的一些问题,详细如下:
- 岗兵机制长处:
- 处理了之前主从切换需求人工干预问题,确保了必定意义上的高可用
- 岗兵机制缺陷:
- 全量数据同步仍然会导致线上呈现时刻短卡顿
- 写入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
源码,并称之为Codis Server
,Codis
选用预先分片(Pre-Sharding
)机制,事前规矩好了,分红1024个slots
(也便是说,最多能支撑后端1024个CodisServer
),这些路由信息保存在ZooKeeper
中。 不足之处有对Redis
源码进行了修正,以及署理完结自身会有的问题。
-
-
我: 实则署理分片的原理也很简略,类似于署理式的分库分表的完结,之前咱们是直接衔接
Redis
,然后对Redis
进行读写操作,现在则是衔接署理,读写操作悉数交由署理来处理分发到详细的Redis
实例,而集群的组成就很好的打破了之前的一主多从架构,构成了多主多从的方式,每个节点由一个个主从来构建,每个节点存储不同的数据,每个节点都能够供给读写服务,然后做到真实意义上的高可用,详细结构如下:
- 面试官: 嗯,那么为什么现在一般公司在考虑技能选型的时分为什么不考虑这两种计划呢?
-
我: 因为运用署理之后能够去处理岗兵存在的问题,可是凡事有利必有弊,署理式集群详细状况如下:
- 长处:
- 打破了传统的一主多从模型,允许多主存在,写入QPS不再遭到单机约束
- 数据分片存储,每个节点存储的数据都不同,处理之前主从架构存在的存容问题
- 每个节点都是独立的主从,数据同步并不是真实的“全量”,每个节点同步数据时都仅仅同步该节点上
master
担任的一部分数据
- 缺陷:
- 因为运用了署理层来打破之前的架构模型,署理层需求承担一切作业
- 署理层需求保护,确保高可用
- 署理层需求完结服务动态感知、注册与监听
- 署理层需求承载一切客户端流量
- 署理层需求处理一切分发恳求
- 因为数据并不存在与同一台机器,
Redis
的许多指令不再完美支撑,如set的交集、并集、差集等
- 长处:
10.4. 去中心化分片式集群
- 面试官: 那么已然署理分片式的集群存在这么多需求考虑处理的问题,现在假如让你做架起,做技能选型你会考虑那种计划呢?
-
我: 我会考虑
Redis3.x
之后的Redis-cluster
去中心化分片式集群,Redis-cluster
在Redis3.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)
语句用于核算key
的CRC16
校验和。 集群中的每个主节点(Master)都担任处理16384个哈希槽中的一部分,当集群处于安稳状况时,每个哈希槽都只由一个主节点进行处理,每个主节点能够有一个到N个从节点(Slave),当主节点呈现宕机或网络断线等不可用时,从节点能主动进步为主节点进行处理。
假定我此刻向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
优化,新的UNLINK
与DEL
作用相同,FLUSHALL/FLUSHDB
中添加了ASYNC
选项,Redis
现在能够在不同的线程中删去后台的key而不会堵塞服务器 - 慢日志记载客户端来历IP地址,这个小功用关于毛病排查很有用途
- 混合
RDB + AOF
格局 - 新的办理指令:
-
MEMORY
:能够履行不同类型的内存分析:内存问题的毛病扫除(运用MEMORY DOCTOR
,类似于LATENCYDOCTOR
),陈述单个键运用的内存量,更深化地陈述Redis
内存运用状况 -
SWAPDB
:能够彻底当即(无推迟)替换同实例下的两个Redis
数据库(现在咱们事务没啥用)
-
- 内存运用和功用改善:
-
Redis
现在运用更少的内存来存储相同数量的数据 -
Redis
现在能够对运用的内存进行碎片整理,并逐步收回空间
-
- 主从数据同步机制:4.0之前仅支撑
-
Redis5.x
:- 新的流数据类型(
Stream data type
) - 新的
Redis
模块API:守时器、集群和字典API -
RDB
可存储LFU
和LRU
信息 -
Redis-cli
中的集群办理器从Ruby (redis-trib.rb)
移植到了C
言语代码。履行redis-cli --cluster help
指令以了解更多信息 - 新的有序调集(
sorted set
)指令:ZPOPMIN/MAX
和堵塞变体(blocking variants
) - 升级
Active defragmentation
至v2
版别 - 增强
HyperLogLog
的完结 - 更好的内存核算陈述
- 许多包括子指令的指令现在都有一个
HELP
子指令 - 优化客户端频频衔接和断开衔接时,使功用体现更好
- 升级
Jemalloc
至5.1
版别 - 引进
CLIENT UNBLOCK
和CLIENT 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多线程的设计大体如下:
- 东西支撑
Cluster
集群:Redis6.0
版别后redis/src
目录下供给的大部分东西开端支撑Cluster
集群 -
Modules API
:Redis 6
中模块API开发进展十分大,因为Redis Labs
为了开发杂乱的功用,从一开端就用上Redis
模块。Redis
能够变成一个结构,运用Modules
来构建不同体系,而不需求从头开端写然后还要BSD
答应。Redis
一开端便是一个向编写各种体系敞开的渠道 -
Disque
:Disque
作为一个RedisModule
运用足以展现Redis
的模块体系的强大。集群音讯总线API、屏蔽和回复客户端、计时器、模块数据的AOF和RDB等等
-
-
-
面试官: 嗯嗯,小伙子你前途无量呀,今日晚上方便入职吗?
-
我: ……….