Redis 集群解决方案分析
mhr18 2024-11-30 12:27 13 浏览 0 评论
调研比较三个解决方案
- Twemproxy Twitter 是C
- Redis ClusterRedis官方是C
- Codis 豌豆荚 否Go+C
Redis常见集群技术
长期以来,Redis本身仅支持单实例,内存一般最多8~20GB。这无法支撑大型线上业务系统的需求。而且也造成资源的利用率过低——毕竟现在服务器内存动辄100~200GB。
为解决单机承载能力不足的问题,各大互联网企业纷纷出手,“自助式”地实现了集群机制。在这些非官方集群解决方案中,物理上把数据“分片”(sharding)存储在多个Redis实例,一般情况下,每一“片”是一个Redis实例。
包括官方近期推出的Redis Cluster,Redis集群有三种实现机制,分别介绍如下,希望对大家选型有所帮助。
1.1 客户端分片
这种方案将分片工作放在业务程序端,程序代码根据预先设置的路由规则,直接对多个Redis实例进行分布式访问。这样的好处是,不依赖于第三方分布式中间件,实现方法和代码都自己掌控,可随时调整,不用担心踩到坑。
这实际上是一种静态分片技术。Redis实例的增减,都得手工调整分片程序。基于此分片机制的开源产品,现在仍不多见。
这种分片机制的性能比代理式更好(少了一个中间分发环节)。但缺点是升级麻烦,对研发人员的个人依赖性强——需要有较强的程序开发能力做后盾。如果主力程序员离职,可能新的负责人,会选择重写一遍。
所以,这种方式下,可运维性较差。出现故障,定位和解决都得研发和运维配合着解决,故障时间变长。
这种方案,难以进行标准化运维,不太适合中小公司(除非有足够的DevOPS)。
1.2 代理分片
这种方案,将分片工作交给专门的代理程序来做。代理程序接收到来自业务程序的数据请求,根据路由规则,将这些请求分发给正确的Redis 实例并返回给业务程序。
这种机制下,一般会选用第三方代理程序(而不是自己研发),因为后端有多个Redis实例,所以这类程序又称为分布式中间件。
这样的好处是,业务程序不用关心后端Redis实例,运维起来也方便。虽然会因此带来些性能损耗,但对于Redis这种内存读写型应用,相对而言是能容忍的。
这是我们推荐的集群实现方案。像基于该机制的开源产品Twemproxy,便是其中代表之一,应用非常广泛。
基本架构
Twemproxy
增加Proxy层,由Proxy实现一致性哈希算法(支持:KETAMA/取模/随机)
数据分片算法:
采用一致性哈希算法,以KET 为例:
Redis Cluster 官方实现
关键点
各节点维护Key->Server的映射关系
Client可以向任意节点发起请求,节点不会转发请求,只是重定向Client
如果在Client第一次请求和重定向请求之间,Cluster拓扑发生改变,则第二次重定向请求将被再次重定向,直到找到正确的Server为止
数据分片算法:
Key空间被划分为16384个区间,每个Master节点负责一部分区间。
Codis
客户端可以连接到任意的codis-proxy,就和连接原生的Redis Server
由Zookeeper维护数据路由表和 codis-proxy 节点的元信息
数据分片算法:
Key空间被划分为1024个区间, 对于每个key来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024
每一个 slot 都会有一个特定的 server group id 来表示这个 slot 的数据由哪个 server group 来提供
水平扩容
Twemproxy:
不支持运行时水平扩容,需要重启。
根据一致性哈希算法进行数据重新分片。
Redis Cluster:
支持通过运行时增加Master节点来水平扩容,提升存储容量,尽力降低命中率波动
存在节点A,需要迁出其中的部分Key区间。新增节点B,接收由节点A迁出的Key区间。
相应Key区间的请求首先还是会发送给A节点:如果请求为新建Key则直接重定向到B节点;如果请求不是新建Key且A节点存储有对应的Key则直接作出响应,否则重定向到B节点
同时Cluster会调用实用工具redis-trib向A节点发送MIGRATE命令,把迁移区间内的所有Key原子的迁移到B节点:同时锁住A、B节点=》在A节点删除Key=》在B节点新建Key=》解锁
运行时动态迁移大尺寸键值可能造成响应时延
Codis:
支持运行时水平扩容
底层基于Codis Server特殊实现原子的数据迁移指令
主从备份
主从备份是否必须
Twemproxy:
没有数据复制不影响可用节点顶替故障节点
故障发生时,没有数据复制的故障节点的Key会全部丢失
Redis Cluster:
没有主从备份的节点一旦故障,将导致整个集群失败:无法写入/读取任何Key;无法进行数据重新分片。
Codis:
若出现故障,需要手动配置节点,进行故障转移。
如果没有进行故障转移,只故障节点负责的slots 会失败
3.2 主从备份方案
Twemproxy本身不支持出从备份,和Redis Cluster一样,需要引入Redis本身的主备复制功能。
可以设置1主1备或者1主多备
当Slave节点接入Cluster时,就会向配置的Master节点发送SYNC命令。断开重连时,也会再次发送SYNC命令
此后Master将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave服务器在接收到数据库文件数据之后将其存盘并加载到内存中。此后,Master继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。
Redis的数据复制是异步的,无论在Master端还是Slave端都不会阻塞。
Slave会周期性确认收到的备份数据
Twemproxy引入主备复制后的架构更新为:
开启主备复制后的Redis Cluster的架构更新为下图,Client可以向任意节点发起请求,无论是Master节点还是Slave节点。
故障检测与转移
Twemproxy
故障检测
Twemproxy本身通过三个配置项实现:
auto_eject_hosts: true timeout: 400 server_failure_limit: 3
如果Server Pool开启了auto_eject_hosts,则当连续server_failure_limit次访问某Server,都超时timeout无响应,则标记该节点为故障。
故障转移
故障节点将从Hash环上直接取下,之前保存在该Server上的Key将丢失。
故障转移耗时评估
假设配置:timeout=400ms, server_failure_limit=2, 则故障转移需要耗时800ms。
Twemproxy借助其他工具
使用Twemproxy时可以引入Redis Sentinel来进行故障检测。引入Redis Sentinel后Twemproxy的架构更新为:
每个Sentinel节点可以监控一个或多个Master节点,及其所有Slave节点
启动Redis Sentinel
redis-sentinel /path/to/sentinel.conf,其中的配置文件是必须的,配置文件将会被用来存储运行时状态信息。在配置文件中只需要指明要监视的Master节点列表。
无须为运行的每个 Sentinel 分别设监听同一Master的其他 Sentinel 的地址, 因为 Sentinel 可以通过发布与订阅功能来自动发现正在监视相同主服务器的其他 Sentinel
不必手动列出主服务器属下的所有从服务器, 因为 Sentinel 可以通过询问主服务器来获得所有从服务器的信息。
故障检测
每个 Sentinel 以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。
如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 那么这个实例会被 Sentinel 标记为主观下线。
如果一个Master被标记为主观下线, 那么正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认主服务器的确进入了主观下线状态。
如果一个主服务器被标记为主观下线, 并且有足够数量的 Sentinel (至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断, 那么这个主服务器被标记为客观下线。
当没有足够数量的 Sentinel 同意主服务器已经下线, 主服务器的客观下线状态就会被移除。 当主服务器重新向 Sentinel 的 PING 命令返回有效回复时, 主服务器的主观下线状态就会被移除。
故障转移
Redis Sentinel进行故障转移的过程:
某Sentinel节点发现主服务器已经进入客观下线状态。
该Sentinel发起选举,试图当选故障转移主持节点
如果当选失败, 那么在设定的故障迁移超时时间的两倍之后, 重新尝试当选。 如果当选成功, 那么执行以下步骤。
选出一个Slave节点,并将它升级为Master节点
向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为Master节点
通过发布与订阅功能, 将更新后的配置传播给所有其他 Sentinel , 其他 Sentinel 对它们自己的配置进行更新。
向已下线主服务器的Slave节点发送 SLAVEOF 命令, 让它们去复制新的Master节点
Redis Sentinel选择新的Master节点进行故障转移之后,Twemproxy无法找到新的Master节点,此时需要引入第四方工具redis-twemproxy-agent(node.js),更新Twemproxy配置,并重启。
故障转移耗时评估
每个 Sentinel 以每秒钟发送一次PING,配置down-after-milliseconds=2s,则主观下线耗时3s
由主观下线升:数量的 Sentinel (至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断1s
Sentinel当选故障转移主持节点:1s
选出一个Slave节点,并将它升级为Master节点,向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为Master节点:0.5s
通过发布与订阅功能, 将更新后的配置传播给所有其他 Sentinel , 其他 Sentinel 对它们自己的配置进行更新:1s
总计耗时:6.5s
Redis Cluster
4.3.1 故障检测
节点状态的维护:
节点的拓扑结构是一张完全图:对于N个节点的Cluster,每个节点都持有N-1个输入TCP连接和N-1个输出TCP连接。
节点信息的维护:每秒随机选择节点发送PING包(无论Cluster规模,PING包规模是常量);每个节点保证在NODE_TIMEOUT/2 时间内,对于每个节点都至少发送一个PING包或者收到一个PONG包.
在节点间相互交换的PING/PONG包中有两个字段用来发现故障节点:PFAIL(Possible Fail)和FAIL。
PFAIL状态:
当一个节点发现某一节点在长达NODE_TIMEOUT的时间内都无法访问时,将其标记为PFAIL状态。
任意节点都可以将其他节点标记为PFAIL状态,无论它是Master节点还是Slave节点。
FAIL状态:
当一个节点发现另一节点被自己标记为PFAIL状态,并且在(NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT)的时间范围内,与其他节点交换的PING/PONG包中,大部分Master节点都把该节点标记为PFAIL或者FAIL状态,则把该节点标记为FAIL状态,并且进行广播。
4.3.2 故障转移
4.3.2.1 Slave选举的时机
当某一Slave节点发现它的Master节点处于FAIL状态时,可以发起一次Slave选举,试图将自己晋升为Master。一个Master节点的所有Slave节点都可以发起选举,但最终只有一个Slave节点会赢得选举。Slave发起选举的条件:
Slave的Master处于FAIL状态
该MASTER节点存储的Key数量>0
Slave与Master节点失去连接的时间小于阀值,以保证参与选举的Slave节点的数据的新鲜度
Cluster逻辑时钟
Config epoch:
每个Master节点启动时都会为自己创建并维护configEpoch字段,设置初始值为0。Master会在自己的PING/PONG包中广播自己的configEpoch字段。Redis Cluster尽力保持各个Master节点的configEpoch字段取值都不同。算法:
每当一个Master节点发现有别的Master节点的configEpoch字段与自己相同时
并且自己的Node ID比对方小(字母顺序)
则把自己的currentEpoch+1
Slave的PING/PONG包中也包含configEpoch字段,Slave的configEpoch字段取值是它的Master的configEpoch字段取值,由最后一次与Master交换PING/PONG包时取得。
Cluster epoch:
每一个节点启动的时候都会创建currentEpoch字段,无论是Master节点还是Slave节点,并设置初始值为0。每当一个节点收到来自其他节点的PING/PONG包时,若其他节点的currentEpoch字段大于当前节点的currentEpoch字段,则当前节点把自己的currentEpoch字段设置为该新观察到的currentEpoch值。
4.3.2.3 Slave选举的过程
Slave节点递增自己的currentEpoch字段
发送FAILOVER_AUTH_REQUEST数据包给每一个MASTER节点
若MASTER节点投票晋升该SLAVE节点,则回复FAILOVER_AUTH_ACK。某个MASTER节点投过票之后,在NODE_TIMEOUT * 2时间内不能再给同一MASTER的SLAVE选举投票。
若Slave在MAX((2*NODE_TIMEOUT),2)的时间内获得大多数MASTER节点的投票,则赢得选举
其间,所有currentEpoch小于选举发起时取值的MASTER投票都会被丢弃
若没有任何Slave赢得选举,选举可以在MAX(NODE_TIMEOUT * 4,4)的时间后重新举行
Master节点投票逻辑
请求选举的Slave的Master必须处于FAIL状态
Master节点维护lastVoteEpoch字段,每当MASTER给某个选举请求投票时,更新lastVoteEpoch字段为请求的currentEpoch值
currentEpoch<lastVoteEpoch的选举请求都不予投票
currentEpoch<MASTER currentEpoch字段的选举请求都不予投票
选举优先权
当Slave节点发现Master节点处于FAIL状态时,不会立刻试图进行选举,而是会延迟一段时间,延迟时常用以下公式进行计算:
DELAY = 500 milliseconds + random delay between 0 and 500 milliseconds + SLAVE_RANK * 1000 milliseconds
其中,SLAVE_RANK由Slave收到Master数据复制的更新程度来衡量。在发起选举之前,Slave之间交换各自获得Master数据复制的更新排名,最新更新的SLAVE_RANK = 0, 其次更新的SLAVE_RANK = 1,以此类推...
故障转移耗时评估
假设配置NODE_TIMEOUT=2s,FAIL_REPORT_VALIDITY_MULT=3s
标记Master为PFAIL状态耗时NODE_TIMEOUT=2s
升级PFAIL状态为FAIL状态,耗时:NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT = 6s
选举前随机延时期望:1s
收集足够多Master投票:MAX((2*NODE_TIMEOUT),2)=4s
总计耗时约:13s
4.3.3 主备平衡功能
Redis Cluster能够自动的迁移Slave节点,从Slave节点有冗余的Master节点到完全没有Slave节点的Master节点。
具体算法:
首先定义Good Slave:对于某一节点来说,如果另一个Slave节点没有处于FAIL状态,则认为该Slave节点为Good Slave节点。
当有Slave节点发现有Master节点没有Good Slave时开始触发主备平衡迁移。
所有发现有主备平衡需求之后,拥有最多Good Slave节点的Master节点的所有Slave中,Node ID最小的Slave节点真正开始迁移。成为没有没有Good Slave Master新Master。
可以配置cluster-migration-barrier参数,控制主备平衡迁移的时候,迁出Master最少需要拥有的Good Slave数
4.4 Codis
支持故障检测并报警
codis-redis-group中的Slave节点无法自动提升为Master节点
由管理员通过Web界面/命令行来手动操作
5.功能限制
Twemproxy:
不支持多key操作
不支持MULTI/EXEC
不支持EVAL
Redis Cluster:
当Client连接到集群的主体部分时可能有少量的写丢失,当Client连接到集群的小部分时可能有显著的写丢失
复杂的多Key操作(Set求并/求交)不能跨节点操作,可以通过使用Hash Tag使相关Key强制哈希到同一Server,但是在数据重新分片期间,还是可能有时间不可用
不支持MULTI/EXEC
Redis 3.0 正式版时间:2015年2月上旬
Codis:
不支持命令:KEYS, MOVE, OBJECT, RENAME, RENAMENX, SORT, SCAN, BITOP,MSETNX, BLPOP, BRPOP, BRPOPLPUSH, PSUBSCRIBE,PUBLISH, PUNSUBSCRIBE, SUBSCRIBE, UNSUBSCRIBE, DISCARD, EXEC, MULTI, UNWATCH, WATCH, SCRIPT EXISTS, SCRIPT FLUSH, SCRIPT KILL, SCRIPT LOAD, AUTH, ECHO, SELECT, BGREWRITEAOF, BGSAVE, CLIENT KILL, CLIENT LIST, CONFIG GET, CONFIG SET, CONFIG RESETSTAT, DBSIZE, DEBUG OBJECT, DEBUG SEGFAULT, FLUSHALL, FLUSHDB, INFO, LASTSAVE, MONITOR, SAVE, SHUTDOWN, SLAVEOF, SLOWLOG, SYNC, TIME
性能
Twemproxy
通常操作Proxy与直接操作Redis实例性能一样
最坏情况下有20%的性能下降
Redis Cluster
1000个节点内拥有线性的伸缩性:通常情况下与直接操作Redis实例性能相同。
不支持多核心,但是可以多进程
Codis
相对于单Redis实例40%性能损失
支持多核
- 上一篇:Redis数据结构(二)字典数据结构dict
- 下一篇:redis知识整理-进阶部分
相关推荐
- 京东大佬问我,每天新增100w订单数据的分库分表方案
-
京东大佬问我,每天新增100w订单数据的分库分表方案嗯,用户问的是高并发订单系统的分库分表方案,每天新增100万订单。首先,我得理解需求。每天100万订单,那每秒大概是多少呢?算一下,100万除以86...
- MySQL 内存使用构成解析与优化实践
-
在为HULK平台的MySQL提供运维服务过程中,我们常常接到用户反馈:“MySQL内存使用率过高”。尤其在业务高峰期,监控中内存占用持续增长,即便数据库运行正常,仍让人怀疑是否存在异常,甚至...
- 阿里云国际站:怎样计算内存优化型需求?
-
本文由【云老大】TG@yunlaoda360撰写一、内存优化型实例的核心价值内存优化型ECS实例专为数据密集型场景设计,具有以下核心优势:高内存配比:内存与CPU比例可达1:8(如ecs.re6....
- MySQL大数据量处理常用解决方案
-
1、读写分离读写分离,将数据库的读写操作分开,比如让性能比较好的服务器去做写操作,性能一般的服务器做读操作。写入或更新操作频繁可以借助MQ,进行顺序写入或更新。2、分库分表分库分表是最常规有效的一种大...
- 1024程序员节 花了三个小时调试 集合近50种常用小工具 开源项目
-
开篇1024是程序员节了,本来我说看个开源项目花半个小时调试之前看的一个不错的开源项目,一个日常开发常常使用的工具集,结果花了我三个小时,开源作者的开源项目中缺少一些文件,我一个个在网上找的,好多坑...
- 免费全开源,功能强大的多连接数据库管理工具!-DbGate
-
DBGate是一个强大且易于使用的开源数据库管理工具,它提供了一个统一的Web界面,让你能够轻松地访问和管理多种类型的数据库。无论你是开发者、数据分析师还是DBA,DBGate都能帮助你提升工作效率...
- 使用operator部署Prometheus
-
一、介绍Operator是CoreOS公司开发,用于扩展kubernetesAPI或特定应用程序的控制器,它用来创建、配置、管理复杂的有状态应用,例如数据库,监控系统。其中Prometheus-Op...
- java学习总结
-
SpringBoot简介https://spring.io/guideshttp://www.spring4all.com/article/246http://www.spring4all.com/a...
- Swoole难上手?从EasySwoole开始
-
前言有些童鞋感觉对Swoole不从下手,也不知在什么业务上使用它,看它这么火却学不会也是挺让人捉急的一件事情。Swoole:面向生产环境的PHP异步网络通信引擎啥是异步网络通信?10年架构师领你架...
- 一款商用品质的开源商城系统(Yii2+Vue2.0+uniapp)
-
一、项目简介这是一套很成熟的开源商城系统【开店星】,之前推过一次,后台感兴趣的还不少,今天再来详细介绍一下:基于Yii2+Vue2.0+uniapp框架研发,代码质量堪称商用品质,下载安装无门槛,UI...
- Yii2中对Composer的使用
-
如何理解Composer?若使用Composer我们应该先知道这是一个什么东西,主要干什么用的,我们可以把Composer理解为PHP包的管理工具,管理我们用到的Yii2相关的插件。安装Compose...
- SpringBoot实现OA自动化办公管理系统源码+代码讲解+开发文档
-
今天发布的是由【猿来入此】的优秀学员独立做的一个基于springboot脚手架的自动化OA办公管理系统,主要实现了日常办公的考勤签到等一些办公基本操作流程的全部功能,系统分普通员工、部门经理、管理员等...
- 7层架构解密:从UI到基础设施,打造真正可扩展的系统
-
"我们系统用户量暴增后完全崩溃了!"这是多少工程师的噩梦?选择正确的数据库只是冰山一角,真正的系统扩展性是一场全栈战役。客户端层:用户体验的第一道防线当用户点击你的应用时,0.1秒...
- Win11系统下使用Django+Celery异步任务队列以及定时(周期)任务
-
首先明确一点,celery4.1+的官方文档已经详细说明,该版本之后不需要引入依赖django-celery这个库了,直接用celery本身就可以了,就在去年年初的一篇文章python3.7....
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)