Codis 介绍
简介
Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有显著区别 (有一些命令不支持), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务。
Codis的github:https://github.com/CodisLabs/codis
Codis特性如下
- 最新 release 版本为 codis-3.2,codis-server 基于 redis-3.2.8
- 支持 slot 同步迁移、异步迁移和并发迁移,对 key 大小无任何限制,迁移性能大幅度提升
- 相比0:重构了整个集群组件通信方式,codis-proxy 与 zookeeper 实现了解耦,废弃了codis-config 等
- 元数据存储支持 etcd/zookeeper/filesystem 等,可自行扩展支持新的存储,集群正常运行期间,即便元存储故障也不再影响 codis 集群,大大提升 codis-proxy 稳定性
- 对 codis-proxy 进行了大量性能优化,通过控制GC频率、减少对象创建、内存预分配、引入 cgo、jemalloc 等,使其吞吐还是延迟,都已达到 codis 项目中最佳
- proxy 实现 select 命令,支持多 DB
- proxy 支持读写分离、优先读同 IP/同 DC 下副本功能
- 基于 redis-sentinel 实现主备自动切换
- 实现动态 pipeline 缓存区(减少内存分配以及所引起的 GC 问题)
- proxy 支持通过 HTTP 请求实时获取 runtime metrics,便于监控、运维
- 支持通过 influxdb 和 statsd 采集 proxy metrics
- slot auto rebalance 算法从0 的基于 max memory policy 变更成基于 group 下 slot 数量
- 提供了更加友好的 dashboard 和 fe 界面,新增了很多按钮、跳转链接、错误状态等,有利于快速发现、处理集群故障
- 新增 SLOTSSCAN 指令,便于获取集群各个 slot 下的所有 key
- codis-proxy 与 codis-dashbaord 支持 docker 部署
codis架构如下
codis 组件介绍
codis server:基于redis-3.2.8 分支开发。增加了额外的数据结构,以支持slot有关的操作以及数据迁移指令。具体的修改可以参考文档redis的修改
codis proxy:客户端连接的redis代理服务,实现了redis协议。除部分命令不支持以外(不支持的命令列表),表现的和原生的redis没有区别。
对于同一个业务集群而言,可以同时部署多个codis-proxy实例;
不同codis-proxy之间由codis-dashboard保证状态同步。
codis dashboard:集群管理工具,支持codis-proxy、codis-server的添加、删除、以及数据迁移操作。在集群状态发生改变时,codis-dashboard维护集群下所有codis-proxy的状态一致性。
对于同一个业务集群而言,同一时刻codis-dashboard只能有0个或者1个
所有对集群的修改都必须通过codis-dashboard完成。
codis FE:集群管理界面
多个集群实例共享可以共享同一个前端展示页面;
通过配置文件管理后端codis-dashboard列表,配置文件可自动更新。
storage:
提供namespace概念,不同集群会按照不同product name进行组织;
目前仅提供了zookeeper、etcd、fs三种实现,但是提供了抽象的interface可自行扩展。
codis 是如何分片的
Codis 采用 Pre-sharding 的技术来实现数据的分片, 默认分成 1024 个 slots (0-1023), 对于每个key来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024。
每一个 slot 都会有一个且必须有一个特定的 server group id 来表示这个 slot 的数据由哪个 server group 来提供。数据的迁移也是以slot为单位的。
Codis部署
环境说明
节点编号 | IP地址 | 操作系统 |
node1 | 192.168.28.71 | Centos 7.2 |
node2 | 192.168.28.72 | Centos 7.2 |
node3 | 192.168.28.73 | Centos 7.2 |
Codis部署架构如下图
codis server主从分布如下图
安装部署
zookeeper 安装
codis的元数据存储依赖zookeeper,所以在安装codis之前我们需要安装一个zookeeper环境
安装过程略
go安装
由于codis是go语言写的,所以需要在每台codis主机安装go语言运行环境。
1、安装go
[root@node1 ~]# yum -y install golang
2、设置go运行环境
[root@node1 ~]# mkdir /data/go -p [root@node1 ~]# vim /etc/profile export GOPATH=/data/go export PATH=$PATH:$GOPATH/bin [root@node1 ~]# source /etc/profile
3、安装godep
[root@node1 ~]# yum -y install git [root@node1 ~]# go get -u github.com/tools/godep && which godep /data/go/bin/godep
codis 安装
1、安装依赖
yum install autoconf automake libtool -y
2、编译安装
mkdir -p /data/go/src/github.com/CodisLabs cd /data/go/src/github.com/CodisLabs git clone https://github.com/CodisLabs/codis.git -b release3.2 cd codis/ make
3、配置文件修改
dashboard 配置修改
# vim config/dashboard.toml coordinator_name = "zookeeper" coordinator_addr = "192.168.28.71:2181,192.168.28.72:2181,192.168.28.73:2181" product_name = "xuele-codis"
复制配置文件到另外两个节点
scp config/dashboard.toml 10.1.13.172:/data/go/src/github.com/CodisLabs/codis/config/dashboard.toml scp config/dashboard.toml 10.1.13.173:/data/go/src/github.com/CodisLabs/codis/config/dashboard.toml
4、codis-proxy配置
# vim config/proxy.toml product_name = "xuele-codis" product_auth = "" jodis_name = "zookeeper" jodis_addr = "192.168.28.71:2181,192.168.28.72:2181,192.168.28.73:2181" jodis_auth = "" jodis_timeout = "20s" jodis_compatible = true
复制配置文件到另外两个节点
scp config/proxy.toml 10.1.13.172:/data/go/src/github.com/CodisLabs/codis/config/proxy.toml scp config/proxy.toml 10.1.13.173:/data/go/src/github.com/CodisLabs/codis/config/proxy.toml
5、codis-server配置
[root@c7-node1 redis-3.2.11]# pwd /data/go/src/github.com/CodisLabs/codis/extern/redis-3.2.11 [root@c7-node1 redis-3.2.11]# mkdir /data/redis [root@c7-node1 redis-3.2.11]# cp redis.conf /data/redis/redis-6379.conf [root@c7-node1 redis-3.2.11]# cp redis.conf /data/redis/redis-6380.conf [root@c7-node1 redis-3.2.11]# cp redis.conf /data/redis/redis-6381.conf
6、redis配置文件修改
# vim /data/redis/redis-6379.conf port 6379 pidfile /tmp/redis_6379.pid logfile "/tmp/redis_6379.log" dbfilename dump_6379.rdb maxmemory 1g dir /data/redis/redis_6379
修改6379为6380
# vim /data/redis/redis-6380.conf port 6380 pidfile /tmp/redis_6380.pid logfile "/tmp/redis_6380.log" dbfilename dump_6380.rdb maxmemory 1g dir /data/redis/redis_6380
修改6379为6381
# vim /data/redis/redis-6381.conf port 6381 pidfile /tmp/redis_6381.pid logfile "/tmp/redis_6381.log" dbfilename dump_6381.rdb maxmemory 1g dir /data/redis/redis_6381
复制配置文件到另外两个节点
scp /data/redis/*.conf 10.1.13.172:/data/redis/ scp /data/redis/*.conf 10.1.13.173:/data/redis/
修改完配置文件,还需要创建相关目录
mkdir /data/redis/redis_6379 -p mkdir /data/redis/redis_6380 -p mkdir /data/redis/redis_6381 -p
7、服务启动及初始化集群
启动dashboard
# nohup ./bin/codis-dashboard --ncpu=1 --config=config/dashboard.toml --log=dashboard.log --log-level=WARN >> /var/log/codis_dashboard.log &
启动proxy
nohup ./bin/codis-proxy --ncpu=1 --config=config/proxy.toml --log=proxy.log --log-level=WARN >> /var/log/codis_proxy.log &
启动codis server
nohup ./bin/codis-server /data/redis/redis-6379.conf & nohup ./bin/codis-server /data/redis/redis-6380.conf & nohup ./bin/codis-server /data/redis/redis-6381.conf &
启动codis-fe
nohup ./bin/codis-fe --ncpu=1 --log=fe.log --log-level=WARN --zookeeper=192.168.28.71:2181,192.168.28.72:2181,192.168.28.73:2181 --listen=192.168.28.71:8090 &
访问codis管理界面
web界面配置
1、添加proxy
2、添加group和server
3、通过fe初始化solt
新增的集群 slot 状态是 offline,因此我们需要对它进行初始化(将 1024 个 slot 分配到各个 group),而初始化最快的方法可通过 fe 提供的 rebalance all slots 按钮来做,如下图所示,点击此按钮,我们即快速完成了一个集群的搭建。
测试验证
通过连接codis-proxy进行验证
# redis-cli -h 192.168.28.71 -p 19000 192.168.28.71:19000> INFO # Server redis_version:3.2.11 redis_git_sha1:7191a280 redis_git_dirty:0 redis_build_id:a9271caacf948e7 redis_mode:standalone os:Linux 3.10.0-514.el7.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll gcc_version:4.8.5 process_id:48993 run_id:2f8dff0622b212a2838f232abf8a166c0f0efb25 tcp_port:6379 uptime_in_seconds:1369 uptime_in_days:0 hz:10 lru_clock:7144691 executable:/data/go/src/github.com/CodisLabs/codis/./bin/codis-server config_file:/data/redis/redis-6379.conf # Clients connected_clients:49 client_longest_output_list:0 client_biggest_input_buf:0 blocked_clients:0 # Memory used_memory:5338840 used_memory_human:5.09M used_memory_rss:13819904 used_memory_rss_human:13.18M used_memory_peak:6361912 used_memory_peak_human:6.07M total_system_memory:1912107008 total_system_memory_human:1.78G used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:1000000000 maxmemory_human:953.67M maxmemory_policy:noeviction mem_fragmentation_ratio:2.59 mem_allocator:jemalloc-4.0.3 # Persistence loading:0 rdb_changes_since_last_save:0 rdb_bgsave_in_progress:0 rdb_last_save_time:1533870359 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:1 rdb_current_bgsave_time_sec:-1 aof_enabled:0 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:-1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok # Stats total_connections_received:150 total_commands_processed:7126 instantaneous_ops_per_sec:22 total_net_input_bytes:153606 total_net_output_bytes:2783917 instantaneous_input_kbps:0.36 instantaneous_output_kbps:8.56 rejected_connections:0 sync_full:1 sync_partial_ok:0 sync_partial_err:0 expired_keys:0 evicted_keys:0 keyspace_hits:0 keyspace_misses:0 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:18123 migrate_cached_sockets:0 # Replication role:master connected_slaves:1 slave0:ip=192.168.28.71,port=6380,state=online,offset=1373,lag=1 master_repl_offset:1373 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:1372 # CPU used_cpu_sys:1.65 used_cpu_user:0.72 used_cpu_sys_children:0.31 used_cpu_user_children:0.00 # Cluster cluster_enabled:0 # Keyspace
读写数据验证
192.168.28.71:19000> SET name hehe OK 192.168.28.71:19000> GET name "hehe"
web界面验证key已经正常写入
压测
[root@c7-node1 codis]# ./bin/redis-benchmark -h 192.168.28.71 -p 19000 -c 100 -d 100 -t set -n 10000 -r 10000
上述命令的意思是,使用redis-benchmark压力测试命令连接codis集群,同时并发10000个(-c),测试set操作(-t),每个测试数据集是100字节(-d),请求数是100000(-n),使用使用随机数插入数值(-r)。
测试结果为1秒内完成1万个set操作
====== SET ====== 10000 requests completed in 0.96 seconds 100 parallel clients 100 bytes payload keep alive: 1
压测后可以看到分到后端group的数据还是比较均衡的
基于redis-sentinel实现主备切换
修改sentinel配置文件
[root@c7-node1 codis]# cat /data/redis/sentinel.conf port 26379 bind 0.0.0.0 pidfile "/data/redis/logs/sentinel.pid" logfile "/data/redis/logs/sentinel.log" dir "/data/redis/db" sentinel monitor codis-server-01 192.168.28.71 6379 2 sentinel down-after-milliseconds codis-server-01 5000 sentinel failover-timeout codis-server-01 60000 sentinel parallel-syncs codis-server-01 2 sentinel monitor codis-server-02 192.168.28.72 6380 2 sentinel down-after-milliseconds codis-server-02 5000 sentinel failover-timeout codis-server-02 60000 sentinel parallel-syncs codis-server-02 2 sentinel monitor codis-server-03 192.168.28.73 6381 2 sentinel down-after-milliseconds codis-server-03 5000 sentinel failover-timeout codis-server-03 60000 sentinel parallel-syncs codis-server-03 2
复制sentinel配置到其他节点
scp /data/redis/sentinel.conf 192.168.28.72:/data/redis/sentinel.conf scp /data/redis/sentinel.conf 192.168.28.73:/data/redis/sentinel.conf
创建相关目录
mkdir /data/redis/logs/ mkdir /data/redis/db/
启动sentinel
[root@c7-node1 codis]# pwd /data/go/src/github.com/CodisLabs/codis [root@c7-node1 codis]# nohup ./bin/redis-sentinel /data/redis/sentinel.conf &
codis web页面添加sentinels
配置完成后可以在group中看到明显的HA标志
手动kill 掉一个主节点测试可用性
kill掉192.168.28.72机器上的6380主节点,验证从节点可以正常转移
验证死一台机器(手动关闭192.168.28.73节点),codis集群能否正常提供服务
数据平滑迁移
redis 数据 平滑迁移到codis 使用codis官方提供的redis-port工具。
原理:redis-port本质是以slave的形式挂在到现有redis服务上去的
1、redis会生成RDB dump文件给做为slave的redis-port
2、redis-port分析RDB文件,并拆分成key-value对,通过slotsrester指令发给codis
3、迁移过程中发生的修改,redis会将这些指令在RDB DUMP发送完成后,在发给redis-port,而redis-port收到这些指令后不做处理,而直接转发给codis
redis-port github地址: https://github.com/CodisLabs/redis-port
(1)安装redis-port
cd /data/go/src/github.com/CodisLabs go get -u github.com/CodisLabs/redis-port go get github.com/cupcake/rdb cd redis-port/ make
数据同步命令
./bin/redis-sync -m 192.168.201.3:6381 -t 192.168.28.71:19000
参数说明:
-m 源集群地址和端口
-t 目标集群地址和端口
redis 数据扩容
说明:本次扩容添加3组codis-server,数据扩容测试由于不涉及高可用相关内容,所以扩容的codis server均为单点
(1)按照之前的部署方式,分别在三个节点各自启动了一个codis server,端口分别为6382,6383,6384,并在web管理界面添加到了集群中
(2)通过rebalance all slots来实现集群的slots自动均衡
(3)确定每个新加入的group分配的slots。
(4)分配完成slot显示如下所示
从group看,每组分片的memory空间占用情况也是均衡的
dashboard 故障异机启动
codis 官方仅允许运行一个dashboard节点,dashboard节点启动会在zk上注册自己的节点,同时在程序正常退出的时候删掉对应的节点,但如果异常退出就会导致zk的节点无法删除,在下一次启动的时候会报“zk: node already exists”的错误。
如果dashboard节点出现故障,短时间无法恢复,我们需要在另外一台节点启动dashboard的话,需要在zookeeper中删除节点的注册信息,方法如下:
# ./zkCli.sh delete /codis3/xuele-codis/topom