Codis介绍
专注于为中小企业提供成都网站制作、成都网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业广饶免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上1000+企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
Codis 是一种redis集群的实现方案,与Redis社区的Redis cluster类似,基于slot的分片机制构建一个更大的Redis节点集群,对于连接到codis的Redis客户端来说, 除了部分不支持的命令外,与连接开源的 Redis Server 没有明显的区别, 客户端代码基本需要进行修改,Codis-proxy会根据访问的key进行slot的计算,然后转发请求到对应的Redis-server,对于客户端来说,中间的codis-proxy是不可见的,因此根据客户业务的需要,可以使用codis构建大规模的Redis 服务,或者仅仅是用于把请求分担多个Redis-server提高系统的吞吐量。
与业界著名的twproxy相比,除了支持Redis的转发,coids还支持不停机的数据迁移,使用户可以在容量或者吞吐量要求有变化时,轻松进行节点的增减,本文主要对codis的迁移原理进行分析,并提出一个可行的优化点。
本文是基于codis3.0版本。
(图片来自网络)
Codis迁移实现原理
Codis-dashboard在启动时,运行了4个后台线程(goroutine),包括后台redis状态同步、proxy状态同步、slot事件处理、sync事件处理,并提供了slot相关的RestFUL API进行slot与Redis-group归属关系的定义、迁移的定义和触发。
如下结构定义一个slot与Redis-group的归属关系和迁移关系,GroupId表示索引为Id的slot所属的redis-group,而Action用于表示一次迁移,Action.TargetId表示该slot要迁移的目标redis-group的Id,Action.State表示迁移的状态,主要有Pending、Preparing、Prepared、Migrating、Finished几种状态。
type SlotMapping struct {
Id int `json:"id"`
GroupId int `json:"group_id"`
Action struct {
Index int `json:"index,omitempty"`
State string `json:"state,omitempty"`
TargetId int `json:"target_id,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"`
} `json:"action"`
}
手动进行一次迁移过程,可以用如下命令来触发:
codis-admin --dashboard=ADDR -slot-action --create --sid=ID --gid=ID,比如把slot 10迁移到group 5,则可以执行” codis-admin --dashboard=ADDR -slot-action --create --sid=10 --gid=5”
如果是把多个slot迁移到同一个server,则可以使用如下命令,一次性来定义若干个迁移操作,codis-admin --slot-action --create-range --beg=ID --end=ID --gid=ID,比如把slot 10~15迁移到group 5,则可以执行” codis-admin --dashboard=ADDR -slot-action –create--range --beg=10 –end=15 --gid=5”。
一次迁移的执行过程中,slot的Action的状态会发生变化,过程为:
也可以触发codis进行rebalance,命令为:codis-admin --dashboard=ADDR –rebalance --confirm,codis会自动把slot往一些新加入的节点进行迁移,使各个节点负责的slot均衡。
Codis迁移的测试
经测试,对于一个64G规模的集群(由8个节点组成,每个节点8G),使用redis-benchmark写满数据,每个key的value长度为32字节,总共写入341446298(3.4亿)条数据,扩容到128G,即对其中的512个slot进行迁移。
测试结果如下:
从测试结果来看,迁移速度非常慢,每迁移一个slot需要花费基本1个小时,因此使用codis时,需要监控数据量,当数据不够时,需要进行及时的扩容,否则当空间不够时的故障处理和恢复时间可能影响线上业务。
Codis迁移代码分析及瓶颈分析
从测试结果来看,迁移速度确实非常慢,极端情况下可能会影响线上业务,因此对迁移过程进行分析和优化就很有必要,下边对关键的实现代码handleSlotRebalance 、StartDaemonRoutines、ProcessSlotAction进行解读,并分析优化改进的地方。
01
handleSlotRebalance实现分析
这个函数的主要逻辑分为三部分:
1)找到需要迁移的slot;
2)为每个新节点分配slot;
3)生成迁移操作;
上面的代码的逻辑是:
1)根据节点个数和slot槽数(固定的1024),计算每个节点上应该负责的slot槽数,表示为bound;
2)对每个redis-group,找到需要迁移出去的slot,表示为pending;
生成迁移计划:
1)遍历所有的redis-group,对于已有的slot小于应该负责的slot槽数的,就要迁移一些槽进来;
2)所有的redis-group,决定需要迁移进来的slot列表,表示为plans;
遍历迁移计划,使用create actionRange生成一系列的slot action,并保存到etcd,下一步就需要由后台线程去etcd中取出slot操作进行分别处理。
02
StartDaemonRoutines
这个代码是在dashboard启动时就启动的后台任务,每隔5秒钟触发一次slot操作,且只会运行一个slot操作任务。
03
ProcessSlotAction实现分析
分为两步Topom.SlotActionPrepare和Topom.processSlotAction。
从上面代码可以看出:
下边再分析processSlotAction的实现:
可以看出:
04
瓶颈分析
从上面的分析可以得出:
这个设计的好处是,迁移过程对客户业务的影响很小,但是也有一些明显的缺点:
由于扩容一般会有一定的提前量,且会选在业务低峰期进行,因此可以对该迁移方案进行优化,可以在不对业务访问造成太大的影响的前提下提高迁移效率。
Codis代码优化
根据上面对迁移实现的分析,优化的思路为:
1、Slot迁移并行化
从代码实现的分析,有2个点可以选择:
最终处理代码简单化的考虑,选择了方案2,同时考虑到如下几点:
如下优化代码,启动至多10个线程进行slot事件的处理。
同时修改SlotActionPrepare,选择一个状态为Pending且没有归属于同一个redis-server的slot,进行处理。
2、Multikey迁移
修改redis-server的迁移指令,支持一次迁移多个key,为了灵活性,把迁移的个数从外部传入,代码比较显而易见,参考如下:
Codis迁移优化测试结果
经过验证,对于一个64G规模的集群,使用redis-benchmark写满数据,每个key的value长度为32字节,总共写入341446298(3.4亿)条数据,扩容到128G,即对其中的512个slot进行迁移。最终测试结果为:
因此,经过优化后迁移性能有极大的提升。当然当前的配置也是考虑到了尽量不影响客户的业务访问,一次迁移的数据量并不是最大化的,在某些情况下,可以修改配置,一次迁移更多的key,可以更加快速的完成迁移。