Proxy-Style Redis集群设计
概述:
为了实现Redis作为内存数据库时的横向扩展和高可用,大致有如下三种方案.
客户端分片
在客户端维护路由规则进而,这种方式的性能最好,几乎没有额外开销,实现最简单.但是缩容和扩容需要手动调整分片规则,运维难度相当大.
Redis-Cluster 去中心化的Redis集群方案
这是官方的集群方案,集群将所有Key映射到一定数量的Slot(虚拟)中.集群成员节点通过P2P方式交换信息.理论的访问性能几乎和单机一致(需要测试),但是其实现复杂度非常高(通信协议,内部状态变换).由于其复杂性,通常也难以运维.虽然是官方方案,但没有大规模成功实施的案例.
Proxy-Style
以Twemproxy和Codis为代表的集群方案,通过独立的Proxy节点对Redis访问进行路由,管理分片,Proxy逻辑和存储逻辑隔离.由于Proxy额外的开销,单机性能通常低于第一种方案,但可通过增加Proxy节点改善.由于Proxy的独立性,状态简单,运维相对也比较容易.
综合对比:
方案 | 实现难度 | 性能 | 运维难度 |
---|---|---|---|
客户端分片 | 低 | 高 | 高 |
Redis-Cluster | 高 | 高 | 高 |
Proxy-Style | 中 | 中 | 低 |
本文主要介绍Proxy-Style的问题及相应的设计方法,该设计来自于Codis
Proxy-Style Design
基本Proxy设计图,Proxy可以通过一致性哈希或普通的哈希算法对请求进行路由.
Proxy与集群中所有Redis服务器建立连接,通过对key进行hash,将Redis访问路由到每台服务器上.
这个结构Proxy是单点的,另外单机Proxy会成为性能瓶颈.通过数据中心(zookeeper)维护Proxy状态,客户端通数据中心获取当前可用的Proxy节点列表.
这种方案客户端可以通过Zookeeper获取Proxy的在线状态,对所有在线Proxy进行轮询.可以通过水平增加Proxy进行负载均衡,扩展Proxy的处理能力.当然也可以使用其他的HA负载均衡方案实现,例如在前面加上Haproxy做反向代理.
此时Redis节点本身是无备份的,一旦崩溃,节点上的数据也就丢失了,这样无法作为可靠的内存数据库使用.存储以主从结构组成一个服务器组
每个RedisGroup中可以由一个Mater和多个Slave组成,组内通过SYNC命令进行复制.
到此为止集群还要解决另外一个问题,Redis缩容/扩容.节点数量的变更,需要对所有的key进行重哈希,分配到所有的节点上.操作需要对整个库加锁,这在线上环境是无法接受的.如果使用一致性哈希,虽然可以避免全局重哈希,但不可避免的会丢失一部分数据.如果集群作为缓存服务器,这不是问题,但如果作为内存数据库使用,数据丢失是不能接受的.
Pre-Sharding 提前将Key分配到若干虚拟Slot中,以Slot为单位进行数据迁移.
为了保持Proxy无状态,Slot和RedisGroup的对应关系状态信息保存在数据中心(Zookeeper)中.这样对key的哈希路由只跟Slot有关,在扩容/缩容时并不会改变Slot的数量,故无须进行重哈希.只需要对部分Slot进行迁移,更新Slot-RedisGroup的对应关系即可.迁移过程由于以Slot为单位,只需要对单个Slot加锁保证过程中的一致性.
根据这个上述设计,使用Zookeeper(zk)保存状态数据,具体实现并不一定需要依赖zk,也可以自行设计方案.这里的zk表示的是一个能保证高可用的数据中心,对所有的Proxy提供一致的视图.例如Codis3.0中,通过dashboard保存状态数据,通过rpc与Proxy通信.
Pre-Sharding的方案写一个Key的时序图:
Codis实现简介
设计是想法的说明书,最终还是需要具体实现的.下面以Codis实现简单分析一下上述设计在实现中的难题.
- 为了让Proxy对Client透明,Codis-Proxy实现Redis的通信协议,对客户端的视图与访问Redis基本无异(有部分指令不支持,例如一些多key操作)
- Codis在为了支持PreSharding,修改Redis源码(Codis-server),增加操作Slot相关的命令,以支持数据热迁移.
- Codis2.0依赖zk/etcd做数据中心保存状.(Slot-Group对应关系,Slot的迁移状态,等等.)
Codis3.0进一步对Redis代码进行修改,在Redis上增加保存Slot信息的数据结构.并通过改进的Dashboard API与Proxy通信传递状态信息. - Codis2中使用codis-config命令来操作集群,例如添加删除RedisGroup,执行Slot迁移,等等.配合Dashboard展示集群状态,手动操作主从切换.
Codis3中废弃了codis-config命令,统一使用Dashboard管理集群.