关于程序员:吃透这些Redis知识点面试横着走附思考题

45次阅读

共计 6486 个字符,预计需要花费 17 分钟才能阅读完成。

Redis 是一种开源(BSD 许可)、数据结构存储在内存中的零碎,用作数据库、缓存和音讯队列。

Redis 提供了诸如字符串、散列、列表、汇合、带范畴查问的排序汇合、位图、超级日志、天文空间索引和流等数据结构。

Redis 内置复制、Lua 脚本、LRU 驱赶、事务和不同级别的磁盘长久化,并通过 Redis Sentinel 和 Redis Cluster 主动分区提供高可用性。

1 集群的劣势

上面是 redis 集群的几个显著劣势。

1.1 伸缩性,数据规模一直增大的时候,容易扩容

  • 单实例模式:只能垂直扩大,增大机器内存的容量;
  • 集群模式:反对垂直扩大,也反对程度扩大,有更好的灵活性,也能够反对更大的容量;

1.2 高可用,服务故障的状况,影响范畴小

  • 单实例模式:故障转移前 100% 不可用(slave 转换为 master 之前);
  • 集群模式:故障转移前局部不可用(集群规模越大,故障影响越小);

1.3 高性能,查问和写入的性能 =

  • 单实例模式:查问能够扩散在多个 slave,写入却只有一个 master;
  • 集群模式:查问有多个 master 和多个 slave,写入也有多个 master;

2 数据分片,一致性 hash

实现 redis 集群的外围点,是针对数据的分片,这里的一致性 hash 算法就十分要害。

2.1 一般的 hash 算法

node=hash(key)%number
数量变动和 node 程序变动,导致 node 抉择的差异性微小,造成微小的缓存生效。

2.2 一致性 hash

hash(node) 造成虚构节点环,hash(key)落在虚构节点环,找到对应的 node。

因为 hash(node)的稳定性,与 node 程序无关。node 变更只影响一小部分数据。

2.3 redis cluster 的 hash slot 算法

关系: cluster > node > slot > key

Redis Cluster 在设计中没有应用一致性哈希(Consistency Hashing),而是应用数据分片引入哈希槽(hash slot)来实现。

一个 Redis Cluster 蕴含 16384(0~16383)个哈希槽,存储在 Redis Cluster 中的所有键都会被映射到这些 slot 中。

集群中的每个键都属于这 16384 个哈希槽中的一个,集群应用公式 slot=CRC16(key)/16384 来计算 key 属于哪个槽,其中 CRC16(key)语句用于计算 key 的 CRC16 校验和。

依照槽来进行分片,通过为每个节点指派不同数量的槽,能够管制不同节点负责的数据量和申请数。

3 集群元数据的一致性

3.1 比照:集中式存储元数据

依赖内部的集中式存储服务,比方:zookeeper, etcd 等,会减少运维累赘和零碎复杂度。

集中式的益处在于,元数据的读取和更新,时效性十分好,一旦元数据呈现了变更,就立刻更新到集中式的存储中,其它节点读取的时候就能够感知到;

不好在于,所有的元数据的更新压力全副集中在一个中央,可能会导致元数据的存储有压力。

3.2 gossip 协定来播送本人的状态以及本人对整个集群认知的扭转

ping / pong 音讯来确认节点的存活和同步全副的集群元数据。

集群元数据,包含:master/slave node 列表和状态, slot 与 node 关系。

每个 master node 每秒会执行 10 次 ping,每次会抉择 5 个最久没有通信的其它节点。

定时查看全副节点,如果发现某个节点通信延时达到了 cluster_node_timeout / 2,那么立刻发送 ping,防止数据交换延时过长。

3.3 减少新节点

命令 CLUSTER MEET <ip> <port>

向一个节点 node 发送 CLUSTER MEET 命令,能够让 node 节点与 ip 和 port 所指定的节点进行握手(handshake),当握手胜利时,node 节点就会将 ip 和 port 所指定的节点增加到 node 节点以后所在的集群中。

依照 gossip 协定,播送 meet 音讯,全副节点接管新节点。

从新 hash(node),调配和转移相应的 slot 给到新节点。

4 客户端如何调用

4.1 hash slot 信息

当客户端连贯任何一个实例,实例就将哈希槽与实例的映射关系响应给客户端,客户端就会将哈希槽与实例映射信息缓存在本地。

4.2 申请数据

当客户端申请时,会计算出键所对应的哈希槽,在通过本地缓存的哈希槽实例映射信息定位到数据所在实例上,再将申请发送给对应的实例。

4.3 重定向机制

客户端将申请发送到实例上,这个实例没有相应的数据,该 Redis 实例会通知客户端更新本地的哈希槽与映射信息的缓存,将申请发送到其余的实例上。

5 新的节点退出集群(扩容)

同 3.3 减少新节点的前序步骤是一样的。

这里具体理解下从新扩容时 slot 迁徙和数据。

5.1 每个 slot 的迁徙过程如下所示:

对指标节点发送 cluster setslot {slot_id} importing {sourceNodeId}命令,指标节点的状态被标记为 ”importing”,筹备导入这个 slot 的数据。

对源节点发送 cluster setslot {slot_id} migrating {targetNodeID}命令,源节点的状态被标记为 ”migrating”,筹备迁出 slot 的数据。

源节点执行 cluster getkeysinslot {slot_id} {count}命令,获取这个 slot 的所有的 key 列表(分批获取,count 指定一次获取的个数),而后针对每个 key 进行迁徙。

在源节点执行 migrate {targetIp} {targetPort} “” 0 {timeout} keys {keys}命令,把一批批 key 迁徙到指标节点(redis-3.0.6 之前一次只能迁徙一个 key),具体来说,源节点对迁徙的 key 执行 dump 指令失去序列化内容,而后通过客户端向指标节点发送携带着序列化内容的 restore 指令,指标节点进行反序列化后将接管到的内容存入本人的内存中,指标节点给客户端返回 ”OK”,而后源节点删除这个 key,这样,一个 key 的迁徙过程就完结了。

所有的 key 都迁徙实现后,一个 slot 的迁徙就完结了。

迁徙所有的 slot(应该被迁徙的那些),所有的 slot 迁徙实现后,新的集群的 slot 就重新分配实现了,向集群内所有 master 发送 cluster setslot {slot_id} node {targetNodeId}命令,告诉他们哪些槽被迁徙到了哪些 master 上,让它们更新本人的信息。

5.2 slot 迁徙的其余阐明

迁徙过程是同步的,在指标节点执行 restore 指令到原节点删除 key 之间,原节点的主线程处于阻塞状态,直到 key 被删除胜利。

如果迁徙过程忽然呈现网路故障,整个 slot 迁徙只进行了一半,这时两个节点依然会被标记为两头过滤状态,即 ”migrating” 和 ”importing”,下次迁徙工具连贯上之后,会持续进行迁徙。

在迁徙过程中,如果每个 key 的内容都很小,那么迁徙过程很快,不会影响到客户端的失常拜访。

如果 key 的内容很大,因为迁徙一个 key 的迁徙过程是阻塞的,就会同时导致原节点和指标节点的卡顿,影响集群的稳定性,所以,集群环境下,业务逻辑要尽可能的防止大 key 的产生。

5.3 slot 编号

无需要求每个 master 的 slot 编号是间断的,只有每个 master 治理的 slot 的数量平衡就能够。

5.4 缩小节点(缩容)

缩容的过程与扩容相似,只是 slot 和数据从下线的节点内转移到其余的节点上。

6 集群中机器呈现故障

6.1 故障检测,节点生效

如果一个节点认为另外一个节点宕机,那么就是 pfail,主观宕机;
如果超过半数的节点都认为另外一个节点宕机了,那么就是 fail,主观宕机;

6.2 故障转移,节点选举

每个 slave 节点,都依据本人对 master 复制数据的 offset,来设置一个选举工夫,offset 越大(复制数据越多)的从节点,选举工夫越靠前,优先进行选举。

所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票,如果大部分 master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点能够切换成 master。

从节点执行主备切换,从节点切换为主节点。

7 主从同步以及高可用

redis 的主从同步在 cluster 版本之前就存在了,既能够提供更高的查问效率(多 slave 能够查问),又能够减少服务的可用性(master 挂机后能够启用 slave 成为 master)。

7.1 应用主从架构时

将只有一个 Master 和多个隶属用于复制。

所有写入都转到主节点,这会在主节点上产生更多负载。

如果 Master 宕机,整个架构容易呈现 SPOF(单点故障)。

当您的用户群增长时,MS 架构无助于扩大。

所以咱们须要一个过程来在产生故障或敞开的状况下监控 Master,那就是 Sentinel。

7.2 Redis 哨兵

故障转移解决

8 主从同步与数据一致性

8.1 主从同步的实现过程

主从同步分为 2 个步骤:同步和命令流传。
数据同步有 sync 和 psync。

sync 全量同步,性能比拟差;psync 增量同步,速度和实时性好很多。

8.2 全量同步 sync

从服务器对主服务的同步操作,须要通过 sync 命令来实现,以下是 sync 命令的执行步骤:

从服务器向主服务器发送 sync 命令

收到 sync 命令后,主服务器执行 bgsave 命令,用来生成 rdb 文件,并在一个缓冲区中记录从当初开始执行的写命令。

bgsave 执行实现后,将生成的 rdb 文件发送给从服务器,用来给从服务器更新数据

主服务器再将缓冲区记录的写命令发送给从服务器,从服务器执行完这些写命令后,此时的数据库状态便和主服务器统一了。

8.3 局部重同步 psync

局部重同步性能由以下 3 局部组成:

  • 主从服务器的复制偏移量
  • 主服务器的复制积压缓冲区
  • 服务器的运行 id(run id)
8.4 心跳检测

当实现了同步之后,主从服务器就会进入命令流传阶段,此时从服务器会以每秒 1 次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset> 其中 replication_offset 是从服务器以后的复制偏移量

发送这个命令次要有三个作用:

  • 检测主从服务器的网络状态
  • 辅助实现 min-slaves 选项
  • 检测命令失落(若失落,主服务器会将失落的写命令从新发给从服务器)
8.5 主从同步总结

发送 SLAVEOF 命令能够进行主从同步,比方:SLAVEOF 127.0.0.1 6379
主从同步有同步和命令流传 2 个步骤

同步:将从服务器的数据库状态更新成主服务器以后的数据库状态(一个耗费资源的操作)

命令流传:当主服务器数据库状态被批改后,导致主从服务器数据库状态不统一,此时须要让主从数据同步到统一的过程

主从同步分首次复制和断线后反复制两种状况

从 2.8 版本开始,在呈现断线后反复制状况时,主服务器会依据复制偏移量、复制积压缓冲区和 run id,来确定执行残缺重同步还是局部重同步

2.8 版本应用 psync 命令来代替 sync 命令去执行同步操作。目标是为了解决同步(sync 命令)的低效操作

问题 1:集群的规模是否无限大,比方:1w 台机器?

答案是否定的,redis 官网给的 Redis Cluster 的规模下限是 1000 个实例。

限度的起因,关键在于实例间的通信开销,集群中的每个节点都保留所有哈希槽与节点对应关系信息(Slot 映射到节点的表),以及本身的状态信息。

参照 3.2gossip 协定播送形式,节点越多,播送风暴对于网络以及服务器压力也就越大。

尽管能够设置播送音讯同步的超时工夫,然而节点增多、超时工夫变长之后,数据一致性的音讯同步延时也会更大,呈现元数据不统一的可能性也会减少。

问题 2:从库的应用,以及如何衡量?

从库的作用,一是进步可用性,当主库宕机之后,能够立刻启用从库作为主库提供服务;一是进步伸缩性,进步了数据查问并发能力,从库提供查问服务就减少了服务资源,更多的节点来反对查问。

因为主从同步存在数据一致性问题,所以在应用从库的过程中,相应的也就会遇到一些问题。

比方:因为从库数据同步慢了,这时候主库宕机了,数据不残缺的从库作为主库,就会呈现数据失落的状况。从库用来查问也有相似问题,实时写入的新数据,同步到从库可能会有延时,在数据没有同步到从库的时候查问从库,也会呈现查问无数据的状况。

所以在应用从库的状况下,须要思考到下面的问题。

面对宕机的时候,数据失落的问题,内存型数据库都会存在的危险,应用 redis 都须要面对这个危险,否则就要就义性能高正数据一致性,redis 数据先长久化再提供服务,这样性能就会降落非常明显了,没法满足内存性数据库的劣势了。

启用从库查问,能够针对一些数据更新的实时性较低,对于脏数据不那么敏感的业务,或者查问量切实太大而能够疏忽局部数据延时的影响。

问题 3:redis 集群化之后,代理的必要性?

代理的性能和稳定性同样是问题所在。

产品的运维难度以及继续的保护,还是官网的 redis cluster 更加牢靠。

有条件的团队,针对 redis cluster 的有余,还会有更深刻的优化,比方咱们本人研发的 tendis。

上面是待解问题小剧场,开发你的脑洞~

问题 1:单 key 的百万 qps 限频问题?(待解)
单 key 的频繁更新,因为单个 key 有且只能落地到一个 master 节点的一个 slot 下面,无奈通过减少节点减少 slave 的办法扩容,性能瓶颈就会受限于机器的 CPU/ 内存的读写能力了。

假如单机最高 10w 的写如速度,那么,要实现接口的 100w 的 qps 限频性能,要怎么实现呢?

请输入你的解答。。。

问题 2:附 - 脑洞:三体人来到地球,要毁灭近半的地球人(待解)

三体人不像灭霸,间接用手套简略粗犷的一个响指就覆灭一半生灵,而是给每个人一个抉择,抉择对的人就有机会生存下来。

这个抉择的办法也特地简略,然而须要开发一套零碎来反对。

那么这套零碎的开发工作就落在了地球人最聪慧的程序员你的头上。

开发好了能够让本人以及家人取得豁免权而生存下来,开发的不好,间接咔嚓。

这个抉择的形容如下

寰球有一个三体人的灯球,默认是燃烧状态。

每个人会有一个三体人的开关,下面会显示以后灯球的状态(燃烧或者亮起),有两个操作按钮,别离是管制灯球的燃烧和亮起。每个人只有一次抉择机会,两个按钮只能抉择一个按钮,按一次。如果一个按钮都不抉择,不按的话,无论灯球最终状态如何,都是要被毁灭。小孩子或者老人、残疾人,能够由监护人来辅助其实现抉择。

零碎实现的前提条件和需要

前提条件阐明
1 寰球 80 亿人口,必须同时在 1 分钟工夫内实现抉择(三体人的开关,寰球实时状态同步,无工夫偏差,无时延),规定工夫范畴之外无奈操作;
2 三体人提供 1000 台 128 核 256G 内存 1T 磁盘的服务器,三体人的开关与服务器的网络是直连的,没有时延,没有网络开销;
3 寰球 80 亿人,每个人都有一个惟一的从 0 开始自增的数字 ID,与三体人的开关也是一一对应的;
4 每个人的 ID%1000 指向特定服务器,申请零碎提供的接口 /vote

零碎需要
性能,开发这个投票接口(所有申请只会在这 1 分钟内申请)
/vote?uid=1111&click=1&t=1234143134134134134

参数
uid 长整型,每个人的惟一 ID
click 枚举值,按钮抉择,0 燃烧,1 亮起
t 长整型,操作工夫,单位纳秒

解决
在同一个时刻(同一纳秒),如果有多集体操作,抉择次数多的失效,如果 2 个抉择次数雷同,状态不变。如:燃烧 2 次,亮起 3 次,这个时刻的状态是亮起。

后果数据
1 最终灯球的状态,是燃烧,还是亮起;
2 抉择正确的人(ID 汇合);
3 抉择谬误的人(ID 汇合);
4 没有做出抉择的人(ID 汇合);

最终执行
调用三体人在服务器上安装的零碎程序,实现地球人毁灭打算。

kill uid
调用三体人的零碎程序无延时,等同于内存读取的效率。
要求在 1 分钟工夫内,把抉择谬误的人和没有做出抉择的人毁灭掉。

模仿测试
1 三体人在 1 分钟内导入测试用例,实现 80 亿人的抉择。
2 1 分钟正确执行实现 kill 调用。
如果无奈实现上述工作,失败。

请输入你的解答。。。

本文由 mdnice 多平台公布

正文完
 0