- Redis 高可用解决方案总结
- 一、主从复制
- 什么是主从复制
- 主从复制过程
- 增量复制
- 断点续传(continue replication)
- PSYNC 核心参数
- 优缺点
- 二、Redis 哨兵 (Redis Sentinel)
- 什么是哨兵
- 基本的故障转移流程
- 注意事项
- 优缺点
- 三、Redis 集群 (Redis Cluster)
- 什么是 Redis 集群
- 集群的模型
- 各个节点之间都传递了什么信息
- Hash 槽(slot)
- 容错
- 优缺点
- 四、Redis 自研高可用架构
- 五、Redis 代理中间件
- Codis
- 什么是 Codis
- Codis 分片原理
- Codis 之间的槽位同步
- Codis 中的扩容
- 自动均衡策略
- Codis 的牺牲
- MGET 的过程
- Codis 集群总结
- Twemproxy 代理
- 什么是 Twemproxy
- Twemproxy 特性
- Codis
- 参考文章
- 一、主从复制
一、主从复制
什么是主从复制
我们正常在项目中对redis
进行应用,一般都不会是单点的。因为,单点的宕机即不可用,不能保证可用性。另外,单点redis
读写指令都会打到同一个服务里面,也会影响性能。在通常的应用中,对redis
的读操作远远多于写操作,所以,我们一般会选择“一主多从”的集群策略。
- 主中的数据有两个副本(
replication
)即从redis1
和从redis2
,即使一台服务器宕机其它两台服务也可以继续提供服务。 - 主中的数据和从上的数据保持实时同步,当主写入数据时通过主从复制机制会复制到两个从服务上。
- 只有一个主
redis
,可以有多个从redis
。 - 主从复制不会阻塞
master
,在同步数据时,master
可以继续处理client
请求。
一个可以即是主又是从,如下图:
主从复制过程
一般当slave
第一次启动连接master
,或者“被认为是第一次连接”,是主从采用全量复制。全量复制的执行流程如下:
-
slave redis
启动. 会从redis.conf
中读取master ip
和host
。 - 定时任务每秒检查是否有新的
mater
需要连接,如果发现就与master
建立socket
连接。 -
slave
发送ping
指令到mater
。 - 如果
mater
配置require pass
,slave
需要发送认证给master
。 -
Salve
会发送sync
命令到Master
。 -
Master
启动一个后台进程,将Redis
中的数据快照rdb
保存到文件中。 - 启动后台进程的同时,
Master
会将保存数据快照期间接收到的写命令缓存起来。 -
Master
完成写文件操作后,将rdb
发送给Salve
。 -
Salve
将rdb
保存到磁盘上,然后加载rdb
到redis
内存中。 - 当
Salve
完成数据快照的恢复后,aster
将这期间收集的写命令发送给Salve
端。 - 后续
Master
收集到的写命令都会通过之前建立的连接. 增量发送给salve
端。
调用流程图如下:
增量复制
当slave
节点与master
全量同步后,master
节点上数据再次发生更新,就会触发增量复制。
当我们在 master
服务器增减数据的时候,就会触发 replicationFeedSalves()
函数,接下来在 Master
服务器上调用的每一个命令都会使用replicationFeedSlaves()
函数来同步到Slave
服务器。当然,在执行此函数之前master
服务器会判断用户执行的命令是否有数据更新,如果有数据更新并且slave
服务器不为空,才会执行此函数,函数主要的工作就是把用户执行的命令发送到所有的 slave
服务器,让slave
服务器执行。
流程如下图:
断点续传(continue replication)
断点续传或者说是断点恢复复制,也就是说 slave 因为某种原因与master
断开连接了一段时间,然后又与master
发生重连。redis2.8
以后对于这种场景进行了优化,开始加入了PSYNC
同步策略。这种策略性能一定是大于全量复制的。
- 从服务器向主服务器发送
PSYNC
命令,携带主服务器的runid
和复制偏移量; - 主服务器验证
runid
和自身runid
是否一致,如不一致,则进行全量复制; - 主服务器验证复制偏移量是否在积压缓冲区内,如不在,则进行全量复制;
- 如都验证通过,则主服务器将保持在积压区内的偏移量后的所有数据发送给从服务器,主从服务器再次回到一致状态。
PSYNC 核心参数
介绍一下,断点续传的几个核心参数,offset
、backlog
、runid
。这三个参数在 PSYNC 中起到了至关重要的作用,下面我们来一一介绍一下。
offet
复制偏移量 ,offset
是用来记录master
和lslave
某个时段的数据版本状态的,slave
每秒会向master
上报offset
,master
保存下来,当触发 PSYNC 时再拿来和master
的offset
数据作对比。说白了,它就是记录数据在某一时刻的快照,用来对比 master 和 slave 数据差异用的。-
backlog
积压缓冲区- 这个也是一个非常核心的参数,它默认大小为
1mb
,复制积压缓冲区是由Master
维护的一个固定长度的FIFO
队列,它的作用是缓存已经传播出去的命令。当Master
进行命令传播时,不仅将命令发送给所有Slave
,还会将命令写入到复制积压缓冲区里面。 - 全量复制的时候,
master
的数据更新(读写操作,主动过期删除等)会临时存放在backlog
中待全量复制完成后增量发到slave,必须为此保留足够的空间。 - 断点续传时,
backlog
会存下slave
断开连接后,master
变更的数据。当然由于它大小有限制,而且先进先出特性,所以达到缓冲大小后会弹出老数据。这样,就可以把它作为一个衡量执行sync
还是psync
的一个标准(backlog = offset : 部分同步,backlog < offset 执行全量同步)
。一般为了避免,大规模全量复制,我们都会给一个恰当的值,根据公式second*write_size_per_second
来估算:其中second
为从服务器断线后重新连接上主服务器所需的平均时间(以秒计算);而write_size_per_second
则是主服务器平均每秒产生的写命令数据量(协议格式的写命令的长度总和);
- 这个也是一个非常核心的参数,它默认大小为
master run id,
master
唯一标示,slave
连接master
时会传runid
,master
每次重启runid
都发生变化,当slave
发现master
的runid
变化时都会触发全量复制流程。
优缺点
优点:
- 高可靠性:一方面,采用双机主备架构,能够在主库出现故障时自动进行主备切换,从库提升为主库提供服务,保证服务平稳运行;另一方面,开启数据持久化功能和配置合理的备份策略,能有效的解决数据误操作和数据异常丢失的问题;
- 读写分离策略:从节点可以扩展主库节点的读能力,有效应对大并发量的读操作。
缺点:
- 故障恢复复杂,如果没有
RedisHA
系统(需要开发),当主库节点出现故障时,需要手动将一个从节点晋升为主节点,同时需要通知业务方变更配置,并且需要让其它从库节点去复制新主库节点,整个过程需要人为干预,比较繁琐; - 主库的写能力受到单机的限制,可以考虑分片;
- 主库的存储能力受到单机的限制,可以考虑
Pika
; - 原生复制的弊端在早期的版本中也会比较突出,如:
Redis
复制中断后,Slave
会发起psync
,此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时可能会造成毫秒或秒级的卡顿;又由于COW
机制,导致极端情况下的主库内存溢出,程序异常退出或宕机;主库节点生成备份文件导致服务器磁盘IO
和CPU
(压缩)资源消耗;发送数GB
大小的备份文件导致服务器出口带宽暴增,阻塞请求,建议升级到最新版本。
二、Redis 哨兵 (Redis Sentinel)
什么是哨兵
Redis Sentinel
是一个分布式架构,其中包含若干个 Sentinel
节点和 Redis
数据节点,每个 Sentinel
节点会对数据节点和其余 Sentinel
节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 Sentinel
节点进行“协商”,当大多数 Sentinel
节点都认为主节点不可达时,它们会选举出一个 Sentinel
节点来完成自动故障转移的工作,同时会将这个变化实时通知给 Redis
应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了 Redis
的高可用问题。
Redis 2.8 版开始正式提供名为Sentinel
的主从切换方案,Sentinel
用于管理多个Redis
服务器实例,主要负责三个方面的任务:
- 监控(
Monitoring
):Sentinel
会不断地检查你的主服务器和从服务器是否运作正常。 - 提醒(
Notification
): 当被监控的某个Redis
服务器出现问题时,Sentinel
可以通过API
向管理员或者其他应用程序发送通知。 - 自动故障迁移(
Automatic failover
): 当一个主服务器不能正常工作时,Sentinel
会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
哨兵(sentinel
) 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel
) 进程,这些进程使用流言协议(gossip protocols
)来接收关于Master
是否下线的信息,并使用投票协议(agreement protocols
)来决定是否执行自动故障迁移,以及选择哪个Slave
作为新的Master
.
每个哨兵(sentinel
) 会向其它哨兵(sentinel
)、master
、slave
定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down
,简称sdown
).
若“哨兵群”中的多数sentinel
,都报告某一master
没响应,系统才认为该master
"彻底死亡"(即:客观上的真正down
机,Objective Down
,简称odown
),通过一定的vote
算法,从剩下的slave
节点中,选一台提升为master
,然后自动修改相关配置.
虽然哨兵(sentinel
) 释出为一个单独的可执行文件 redis-sentinel
,但实际上它只是一个运行在特殊模式下的 Redis
服务器,你可以在启动一个普通 Redis
服务器时通过给定 --sentinel
选项来启动哨兵(sentinel
).
基本的故障转移流程
-
主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
-
每个
Sentinel
节点通过定期监控发现主节点出现了故障
-
多个
Sentinel
节点对主节点的故障达成一致会选举出其中一个节点作为领导者负责故障转移。
-
Sentinel
领导者节点执行了故障转移,整个过程基本是跟我们手动调整一致的,只不过是自动化完成的。
-
故障转移后整个
Redis Sentinel
的结构,重新选举了新的主节点。
注意事项
Sentinel
节点不应该部署在一台物理“机器”上。
这里特意强调物理机是因为一台物理机做成了若干虚拟机或者现今比较流行的容器,它们虽然有不同的IP
地址,但实际上它们都是同一台物理机,同一台物理机意味着如果这台机器有什么硬件故障,所有的虚拟机都会受到影响,为了实现Sentinel
节点集合真正的高可用,请勿将Sentinel
节点部署在同一台物理机器上。部署至少三个且奇数个的
Sentinel
节点。个以上是通过增加
Sentinel
节点的个数提高对于故障判定的准确性,因为领导者选举需要至少一半加 1 个节点,奇数个节点可以在满足该条件的基础上节省一个节点。
优缺点
优点:
-
Redis Sentinel
集群部署简单; - 能够解决
Redis
主从模式下的高可用切换问题; - 很方便实现
Redis
数据节点的线形扩展,轻松突破Redis
自身单线程瓶颈,可极大满足Redis
大容量或高性能的业务需求; - 可以实现一套
Sentinel
监控一组Redis
数据节点或多组数据节点。
缺点:
- 部署相对
Redis
主从模式要复杂一些,原理理解更繁琐; - 资源浪费,
Redis
数据节点中slave
节点作为备份节点不提供服务; -
Redis Sentinel
主要是针对Redis
数据节点中的主节点的高可用切换,对Redis
的数据节点做失败判定分为主观下线和客观下线两种,对于Redis
的从节点有对节点做主观下线操作,并不执行故障转移。 - 不能解决读写分离问题,实现起来相对复杂。
三、Redis 集群 (Redis Cluster)
什么是 Redis 集群
Redis
集群是一个分布式(distributed
)、容错(fault-tolerant
)的 Redis
实现, 集群可以使用的功能是普通单机 Redis
所能使用的功能的一个子集(subset
)。
Redis
集群中不存在中心(central
)节点或者代理(proxy
)节点, 集群的其中一个主要设计目标是达到线性可扩展性(linear scalability
)。
Redis
集群提供了一种运行 Redis
的方式,其中数据在多个 Redis
节点间自动分区。Redis
集群还在分区期间提供一定程度的可用性,即在实际情况下能够在某些节点发生故障或无法通信时继续运行。但是,如果发生较大故障(例如,大多数主站不可用时),集群会停止运行。
集群的模型
- 所有的节点通过服务通道直接相连,各个节点之间通过二进制协议优化传输的速度和带宽。
- 客户端与节点之间通过 ascii 协议进行通信
- 客户端与节点直连,不需要中间 Proxy 层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
- 尽管这些节点彼此相连,功能相同,但是仍然分为两种节点:master 和 slave。
各个节点之间都传递了什么信息
通过上面的图我们可以知道各个节点之间通过 PING-PONG 机制通信,下面是一段关于 PING-PONG 机制的会话”内容”。
节点M:PING,嘿,朋友你好吗?我是 XYZ 哈希槽的 master ,配置信息是 FF89X1JK。
节点N:PONG,我很好朋友,我也是 XYZ 哈希槽的 master ,配置信息是 FF89X1JK。
节点M:我这里有一些关于我最近收到的其他节点的信息 ,A 节点回复了我的 PING 消息,我认为 A 节点是正常的。B 没有回应我的消息,我猜它现在可能出问题了,但是我需要一些 ACK(Acknowledgement) 消息来确认。
节点N:我也想给你分享一些关于其它节点的信息,C 和 D 节点在指定的时间内回应了我, 我认为它们都是正常的,但是 B 也没有回应我,我觉得它现在可能已经挂掉了。
每个节点会向集群中的其他节点发送节点状态信息,如果某个节点挂掉停止了服务,那么会执行投票容错机制,关于这个机制,会在下面讲到。
Hash 槽(slot)
Redis 集群不使用一致的散列,而是一种不同的分片形式,其中每个键在概念上都是我们称之为散列槽的一部分,目的是使数据均匀的存储在诸多节点中。这点类似于 HashMap 中的桶(bucket)。
Redis 集群中有 16384 个散列槽,为了计算给定密钥的散列槽,Redis 对 key 采用 CRC16 算法,以下是负责将键映射到槽的算法:
slot = crc16(key) mod NUMER_SLOTS
例如,你可能有 3 个节点,其中一个集群:
节点 A 包含从 0 到 5500 的散列槽。
节点 B 包含从 5501 到 11000 的散列槽。
节点 C 包含 从 11001 到 16383 的散列槽。
Hash 槽可以轻松地添加和删除集群中的节点。例如,如果我想添加一个新节点 D,我需要将节点 A,B,C 中的一些散列槽移动到 D。同样,如果我想从节点 A 中删除节点 A,可以只移动由 A 服务的散列槽到 B 和 C。当节点 A 为空时,可以将它从群集中彻底删除。
对象保存到 Redis 之前先经过 CRC16 哈希到一个指定的 Node 上,例如 Object4 最终 Hash 到了 Node1 上。
每个 Node 被平均分配了一个 Slot 段,对应着 0-16384,Slot 不能重复也不能缺失,否则会导致对象重复存储或无法存储。
Node 之间也互相监听,一旦有 Node 退出或者加入,会按照 Slot 为单位做数据的迁移。例如 Node1 如果掉线了,0-5640 这些 Slot 将会平均分摊到 Node2 和 Node3 上,由于 Node2 和 Node3 本身维护的 Slot 还会在自己身上不会被重新分配,所以迁移过程中不会影响到 5641-16384Slot 段的使用。
想扩展并发读就添加 Slaver,想扩展并发写就添加 Master,想扩容也就是添加 Master,任何一个 Slaver 或者几个 Master 挂了都不会是灾难性的故障。
简单总结下哈希 Slot 的优缺点:
缺点:每个 Node 承担着互相监听、高并发数据写入、高并发数据读出,工作任务繁重
优点:将 Redis 的写操作分摊到了多个节点上,提高写的并发能力,扩容简单。
容错
- 集群中的节点不断的
PING
其他的节点,当一个节点向另一个节点发送PING
命令, 但是目标节点未能在给定的时限内回复, 那么发送命令的节点会将目标节点标记为PFAIL
(possible failure
,可能已失效)。 - 当节点接收到其他节点发来的信息时, 它会记下那些被其他节点标记为失效的节点。 这被称为失效报告(
failure report
)。 - 如果节点已经将某个节点标记为
PFAIL
, 并且根据节点所收到的失效报告显式, 集群中的大部分其他主节点也认为那个节点进入了失效状态, 那么节点会将那个失效节点的状态标记为FAIL
。 - 一旦某个节点被标记为
FAIL
, 关于这个节点已失效的信息就会被广播到整个集群, 所有接收到这条信息的节点都会将失效节点标记为FAIL
。
简单来说, 一个节点要将另一个节点标记为失效, 必须先询问其他节点的意见, 并且得到大部分主节点的同意才行。
- 如果被标记为
FAIL
的是从节点, 那么当这个节点重新上线时,FAIL
标记就会被移除。 一个从节点是否处于FAIL
状态, 决定了这个从节点在有需要时能否被提升为主节点。 - 如果一个主节点被打上
FAIL
标记之后, 经过了节点超时时限的四倍时间, 再加上十秒钟之后, 针对这个主节点的槽的故障转移操作仍未完成, 并且这个主节点已经重新上线的话, 那么移除对这个节点的FAIL
标记。在不符合上面的条件后,一旦某个主节点进入FAIL
状态, 如果这个主节点有一个或多个从节点存在, 那么其中一个从节点会被升级为新的主节点, 而其他从节点则会开始对这个新的主节点进行复制。
优缺点
优点:
- 无中心架构;
- 数据按照
slot
存储分布在多个节点,节点间数据共享,可动态调整数据分布; - 可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除;
- 高可用性:部分节点不可用时,集群仍可用。通过增加
Slave
做standby
数据副本,能够实现故障自动failover
,节点之间通过gossip
协议交换状态信息,用投票机制完成Slave
到Master
的角色提升; - 降低运维成本,提高系统的扩展性和可用性。
缺点:
-
Client
实现复杂,驱动要求实现Smart Client
,缓存slots mapping
信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。目前仅JedisCluster
相对成熟,异常处理部分还不完善,比如常见的“max redirect exception”
。 - 节点会因为某些原因发生阻塞(阻塞时间大于
clutser-node-timeout
),被判断下线,这种failover
是没有必要的。 - 数据通过异步复制,不保证数据的强一致性。
- 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况。
-
Slave
在集群中充当“冷备”,不能缓解读压力,当然可以通过SDK
的合理设计来提高Slave
资源的利用率。 -
Key
批量操作限制,如使用mset
、mget
目前只支持具有相同slot
值的Key
执行批量操作。对于映射为不同slot
值的Key
由于Keys
不支持跨slot
查询,所以执行mset
、mget
、sunion
等操作支持不友好。 -
Key
事务操作支持有限,只支持多key
在同一节点上的事务操作,当多个Key
分布于不同的节点上时无法使用事务功能。 -
Key
作为数据分区的最小粒度,不能将一个很大的键值对象如hash
、list
等映射到不同的节点。 - 不支持多数据库空间,单机下的
redis
可以支持到 16 个数据库,集群模式下只能使用 1 个数据库空间,即 db 0。 - 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。
- 避免产生
hot-key
,导致主库节点成为系统的短板。 - 避免产生
big-key
,导致网卡撑爆、慢查询等。 - 重试时间应该大于
cluster-node-time
时间。 -
Redis Cluster
不建议使用pipeline
和multi-keys
操作,减少max redirect
产生的场景。
四、Redis 自研高可用架构
Redis 自研的高可用解决方案,主要体现在配置中心、故障探测和 failover 的处理机制上,通常需要根据企业业务的实际线上环境来定制化。
优点:
- 高可靠性、高可用性;
- 自主可控性高;
- 贴切业务实际需求,可缩性好,兼容性好。
缺点:
- 实现复杂,开发成本高;
- 需要建立配套的周边设施,如监控,域名服务,存储元数据信息的数据库等;
- 维护成本高。
五、Redis 代理中间件
Codis
什么是 Codis
Codis
是一个代理中间件,用的是 GO
语言开发的,如下图,Codis
在系统的位置是这样的。
Codis
分为四个部分,分别是Codis Proxy
(codis-proxy
)、Codis Dashboard
(codis-config
)、Codis Redis
(codis-server
)和ZooKeeper/Etcd
.
Codis
就是起着一个中间代理的作用,能够把所有的Redis
实例当成一个来使用,在客户端操作着SDK
的时候和操作Redis
的时候是一样的,没有差别。
因为Codis
是一个无状态的,所以可以增加多个Codis
来提升QPS
,同时也可以起着容灾的作用。
Codis 分片原理
在Codis
中,Codis
会把所有的key
分成 1024 个槽,这 1024 个槽对应着的就是Redis
的集群,这个在Codis
中是会在内存中维护着这 1024 个槽与Redis
实例的映射关系。这个槽是可以配置,可以设置成 2048 或者是 4096 个。看你的Redis
的节点数量有多少,偏多的话,可以设置槽多一些。
Codis
中key
的分配算法,先是把key
进行CRC32
后,得到一个 32 位的数字,然后再hash%1024
后得到一个余数,这个值就是这个key
对应着的槽,这槽后面对应着的就是redis
的实例。(可以思考一下,为什么 Codis 很多命令行不支持,例如 KEYS 操作)
CRC32
:CRC
本身是“冗余校验码”的意思,CRC32
则表示会产生一个32bit
(8 位十六进制数)的校验值。由于CRC32
产生校验值时源数据块的每一个bit
(位)都参与了计算,所以数据块中即使只有一位发生了变化,也会得到不同的CRC32
值。
Codis中Key的算法代码如下
//Codis中Key的算法
hash = crc32(command.key)
slot_index = hash % 1024
redis = slots[slot_index].redis
redis.do(command)
Codis 之间的槽位同步
思考一个问题:如果这个 Codis 节点只在自己的内存里面维护着槽位与实例的关系,那么它的槽位信息怎么在多个实例间同步呢?
Codis 把这个工作交给了 ZooKeeper 来管理,当 Codis 的 Codis Dashbord 改变槽位的信息的时候,其他的 Codis 节点会监听到 ZooKeeper 的槽位变化,会及时同步过来。如图:
Codis 中的扩容
思考一个问题:在 Codis 中增加了 Redis 节点后,槽位的信息怎么变化,原来的 key 怎么迁移和分配?如果在扩容的时候,这个时候有新的 key 进来,Codis 的处理策略是怎么样的?
因为Codis
是一个代理中间件,所以这个当需要扩容Redis
实例的时候,可以直接增加redis
节点。在槽位分配的时候,可以手动指定Codis Dashbord
来为新增的节点来分配特定的槽位。
在Codis
中实现了自定义的扫描指令SLOTSSCAN
,可以扫描指定的slot
下的所有的key
,将这些key
迁移到新的Redis
的节点中(话外语:这个是Codis
定制化的其中一个好处)。
首先,在迁移的时候,会在原来的Redis
节点和新的Redis
里都保存着迁移的槽位信息,在迁移的过程中,如果有key
打进将要迁移或者正在迁移的旧槽位的时候,这个时候Codis
的处理机制是,先是将这个key
强制迁移到新的Redis
节点中,然后再告诉Codis
,下次如果有新的key
的打在这个槽位中的话,那么转发到新的节点。代码策略如下:
slot_index = crc32(command.key) % 1024
if slot_index in migrating_slots:
do_migrate_key(command.key) # 强制执行迁移
redis = slots[slot_index].new_redis
else:
redis = slots[slot_index].redis
redis.do(command)
自动均衡策略
面对着上面讲的迁移策略,如果有成千上万个节点新增进来,都需要我们手动去迁移吗?那岂不是得累死啊。当然,Codis
也是考虑到了这一点,所以提供了自动均衡策略。自动均衡策略是这样的,Codis
会在机器空闲的时候,观察Redis
中的实例对应着的slot
数,如果不平衡的话就会自动进行迁移。
Codis 的牺牲
因为Codis
在Redis
的基础上的改造,所以在Codis
上是不支持事务的,同时也会有一些命令行不支持,在官方的文档上有(Codis
不支持的命令)
官方的建议是单个集合的总容量不要超过 1M,否则在迁移的时候会有卡顿感。在Codis
中,增加了proxy
来当中转层,所以在网络开销上,是会比单个的Redis
节点的性能有所下降的,所以这部分会有些的性能消耗。可以增加proxy
的数量来避免掉这块的性能损耗。
MGET 的过程
思考一个问题:如果熟悉 Redis 中的 MGET、MSET 和 MSETNX 命令的话,就会知道这三个命令都是原子性的命令。但是,为什么 Codis 支持 MGET 和 MSET,却不支持 MSETNX 命令呢?
原因如下:
在Codis
中的MGET
命令的原理是这样的,先是在Redis
中的各个实例里获取到符合的key
,然后再汇总到Codis
中,如果是MSETNX
的话,因为key
可能存在在多个Redis
的实例中,如果某个实例的设值成功,而另一个实例的设值不成功,从本质上讲这是不成功的,但是分布在多个实例中的Redis
是没有回滚机制的,所以会产生脏数据,所以 MSETNX 就是不能支持了。
Codis 集群总结
-
Codis
是一个代理中间件,通过内存保存着槽位和实例节点之间的映射关系,槽位间的信息同步交给ZooKeeper
来管理。 - 不支持事务和官方的某些命令,原因就是分布多个的
Redis
实例没有回滚机制和WAL
,所以是不支持的.
Twemproxy 代理
什么是 Twemproxy
Twemproxy 也叫 nutcraker。是 Twtter 开源的一个 Redis 和 Memcache 代理服务器,主要用于管理 Redis 和 Memcached 集群,减少与 Cache 服务器直接连接的数量。
基于 twemproxy 的高可用架构图
Twemproxy 特性
- 轻量级、快速
- 保持长连接
- 减少了直接与缓存服务器连接的连接数量
- 使用 pipelining 处理请求和响应
- 支持代理到多台服务器上
- 同时支持多个服务器池
- 自动分片数据到多个服务器上
- 实现完整的 memcached 的 ASCII 和再分配协议
- 通过 yaml 文件配置服务器池
- 支持多个哈希模式,包括一致性哈希和分布
- 能够配置删除故障节点
- 可以通过端口监控状态
- 支持 linux, *bsd,os x 和 solaris
参考文章
Redis 主从复制看这篇就够了
(一)redis 主从原理及流程——主从复制
这可能是目前最全的 Redis 高可用技术解决方案总结
Redis 哨兵机制
redis 如何实现高可用【主从复制、哨兵机制】
理解 Redis 集群
三张图秒懂 Redis 集群设计原理
为什么大厂都喜欢用 Codis 来管理分布式集群?