前言 Codis 3.x稳定版本已经很久没更新了,虽然有缺点也称不上完美但确实可以有效解决横向扩展问题。Redis 5.0因为所谓的政治正确
把master-slave名字修改为master-replica上了开源社区热议排行榜,客户端SDK的API完全兼容redis-cluster在Redis 6.0
官方推出redis-cluster-proxy
集群代理方案前成本很高,目前也需要等待社区验证新方案的稳定性,所以大家在选择Redis集群方案的时候除了自研和Codis
或者Pika
以外依然没有太多的选择余地。我们使用Codis的原因也很简单,Redis主从模式内存从128GB一路增加到1TB后硬件终于受不鸟了,要么像数据库借鉴“拆”的奥义做到庖丁解牛一般,不然摆在眼前的路基本只剩下相对成熟可靠的Codis。本文分享了Redis高可用技术解决方案选型的参考文章和Codis集群搭建的过程,希望对大家有帮助。
Redis(Codis)分布式集群部署实践
更新历史 2020年07月13日 - 增加Codis HA搭建方案,codis-dashboard迁移和codis-server扩容心得 2020年06月21日 - 增加Codis手动编译安装 2019年07月23日 - 初稿
阅读原文 - https://wsgzao.github.io/post/codis/
扩展阅读
Redis Codis Pika
Codis简介 Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有显著区别 (不支持的命令列表 ), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务。
Compared with Twemproxy and Redis Cluster
Codis
Twemproxy
Redis Cluster
resharding without restarting cluster
Yes
No
Yes
pipeline
Yes
Yes
No
hash tags for multi-key operations
Yes
Yes
Yes
multi-key operations while resharding
Yes
-
No(details )
Redis clients supporting
Any clients
Any clients
Clients have to support cluster protocol
“Resharding” means migrating the data in one slot from one redis server to another, usually happens while increasing/decreasing the number of redis servers.
为什么要选择Codis
Redis获得动态扩容/缩容的能力,增减redis实例对client完全透明、不需要重启服务,不需要业务方担心 Redis 内存爆掉的问题. 也不用担心申请太大, 造成浪费. 业务方也不需要自己维护 Redis.
Codis支持水平扩容/缩容,扩容可以直接界面的 “Auto Rebalance” 按钮,缩容只需要将要下线的实例拥有的slot迁移到其它实例,然后在界面上删除下线的group即可。
Codis 使用文档 Codis FAQ Codis 不支持的命令列表 redis 修改部分(增加若干指令) Performance (Benchmark)
Codis架构
集群配置前需要了解架构,集群分片主要分三种:
客户端分片:这个需要自己开发,对客户端要求严格,集群很难扩容
代理端分片:如 codis,对客户端几乎无要求,集群容易扩容
服务端分片:如 redis 集群,需要智能客户端支持集群协议的,集群容易扩容
Codis 3.x 由以下组件组成:
Codis Server :基于 redis-3.2.8 分支开发。增加了额外的数据结构,以支持 slot 有关的操作以及数据迁移指令。具体的修改可以参考文档 redis 的修改 。
Codis Proxy :客户端连接的 Redis 代理服务, 实现了 Redis 协议。 除部分命令不支持以外(不支持的命令列表 ),表现的和原生的 Redis 没有区别(就像 Twemproxy)。
对于同一个业务集群而言,可以同时部署多个 codis-proxy 实例;
不同 codis-proxy 之间由 codis-dashboard 保证状态同步。
Codis Dashboard :集群管理工具,支持 codis-proxy、codis-server 的添加、删除,以及据迁移等操作。在集群状态发生改变时,codis-dashboard 维护集群下所有 codis-proxy 的状态的一致性。
对于同一个业务集群而言,同一个时刻 codis-dashboard 只能有 0个或者1个;
所有对集群的修改都必须通过 codis-dashboard 完成。
Codis Admin :集群管理的命令行工具。
可用于控制 codis-proxy、codis-dashboard 状态以及访问外部存储。
Codis FE :集群管理界面。
多个集群实例共享可以共享同一个前端展示页面;
通过配置文件管理后端 codis-dashboard 列表,配置文件可自动更新。
Storage :为集群状态提供外部存储。
提供 Namespace 概念,不同集群的会按照不同 product name 进行组织;
目前仅提供了 Zookeeper、Etcd、Fs 三种实现,但是提供了抽象的 interface 可自行扩展。
Codis部署 Codis官方的GitHub教程已经写的比较详细了,这里重点分享Ansible自动化部署方案
基于官方的简化版ansible一键部署codis
基于组件的模块化ansible部署codis
Redis Codis 部署安装
使用codis-admin搭建codis集群
Codis 3.x集群搭建与使用
Codis手动编译安装 安装 Go 运行环境 Getting Started
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 yum install -y net-tools vim telnet wget git gcc autoconf automake m4 wget https://dl.google.com/go/go1.13.12.linux-amd64.tar.gz tar -C /usr/local -xzf go1.13.12.linux-amd64.tar.gz vim /etc/profile export PATH=$PATH :/usr/local/go/binsource /etc/profilego version go version go1.13.12 linux/amd64
编译Codis
以Codis为例,官方步骤可能有坑
Codis 使用文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 vim /etc/profile export PATH=$PATH :/usr/local/go/binexport GOPATH=/usr/local/codis/gopathexport PATH=$PATH :$GOPATH /binsource /etc/profilemkdir -p $GOPATH /src/github.com/CodisLabscd $_ && git clone https://github.com/CodisLabs/codis.git -b release3.2cd $GOPATH /src/github.com/CodisLabs/codis/extern/redis-3.2.11/depsmake hiredis jemalloc linenoise lua geohash-int cd $GOPATH /src/github.com/CodisLabs/codismake =============================================================================== go build -i -o bin/codis-dashboard ./cmd/dashboard go build -i -tags "cgo_jemalloc" -o bin/codis-proxy ./cmd/proxy go build -i -o bin/codis-admin ./cmd/admin go build -i -o bin/codis-ha ./cmd/ha go build -i -o bin/codis-fe ./cmd/fe cat bin/versionversion = 2018-11-04 16:22:35 +0800 @de1ad026e329561c22e2a3035fbfe89dc7fef764 @3.2.2-12-gde1ad02 compile = 2020-06-17 06:39:48 -0400 by go version go1.13.12 linux/amd64 mkdir -p /opt/codis/{config,bin}cp -r $GOPATH /src/github.com/CodisLabs/codis/bin /opt/codiscp -r $GOPATH /src/github.com/CodisLabs/codis/config /opt/codis
Codis手动安装 dashboard.toml
调整为zookeeper,强依赖
保持多个instance的product_name唯一性
1 2 3 4 5 6 7 8 cd /opt/codis/configvim dashboard.toml coordinator_name = "zookeeper" coordinator_addr = "127.0.0.1:2181" product_name = "codis-demo"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 coordinator_name = "zookeeper" coordinator_addr = "127.0.0.1:2181" product_name = "codis-demo" product_auth = "" admin_addr = "0.0.0.0:18080"
proxy.toml
调整为zookeeper,强依赖
保持多个instance的product_name唯一性
1 product_name = "codis-demo"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 product_name = "codis-demo" product_auth = "" session_auth = "" admin_addr = "0.0.0.0:11080" proto_type = "tcp4" proxy_addr = "0.0.0.0:19000" jodis_name = "" jodis_addr = "" jodis_auth = "" jodis_timeout = "20s" jodis_compatible = false proxy_datacenter = "" proxy_max_clients = 1000 proxy_max_offheap_size = "1024mb" proxy_heap_placeholder = "256mb" backend_ping_period = "5s" backend_recv_bufsize = "128kb" backend_recv_timeout = "30s" backend_send_bufsize = "128kb" backend_send_timeout = "30s" backend_max_pipeline = 20480 backend_primary_only = false backend_primary_parallel = 1 backend_replica_parallel = 1 backend_keepalive_period = "75s" backend_number_databases = 16 session_recv_bufsize = "128kb" session_recv_timeout = "30m" session_send_bufsize = "64kb" session_send_timeout = "30s" session_max_pipeline = 10000 session_keepalive_period = "75s" session_break_on_failure = false metrics_report_server = "" metrics_report_period = "1s" metrics_report_influxdb_server = "" metrics_report_influxdb_period = "1s" metrics_report_influxdb_username = "" metrics_report_influxdb_password = "" metrics_report_influxdb_database = "" metrics_report_statsd_server = "" metrics_report_statsd_period = "1s" metrics_report_statsd_prefix = ""
redis.conf
每台机器创建两个redis实例,对应端口6379和6380
按需求修改redis.conf配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 mkdir -p /opt/codis/datacp redis.conf redis-6379.confvim redis-6379.conf protected-mode no port 6379 pidfile "/opt/codis/data/redis_6379.pid" logfile "/opt/codis/data/redis_6379.log" dbfilename "dump_6379.rdb" dir "/opt/codis/data" cp redis-6379.conf redis-6380.conf sed -i 's/6379/6380/g' redis-6380.conf
sentinel.conf
如果只是test可以不配置
线上环境做HA建议分布在3台不同节点
1 2 3 4 5 6 7 vim sentinel.conf dir "/opt/codis/data" daemonize yes loglevel notice logfile "/opt/codis/data/setinel.log"
Codis启停脚本
测试环境可以使用nohup在后台运行
线上环境建议使用Supervisord
或者Monit
来管理
注意这里最后一个fe参数是你要访问的前端地址,但是因为zookeeper已经占用了8080端口,所以你可以改成别的端口。而且为了你在任何地址都可以访问,你可以设置监听ip为0.0.0.0,因为FE是不用密码的,所以端口最好设置一个不常见的,避免被不怀好意的人看到前端页面之后对你的codis集群做出不好的事情,或者通过防火墙和SSO限制访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 mkdir -p /opt/codis/datacd /opt/codisnohup ./bin/codis-dashboard --ncpu=1 --config=config/dashboard.toml --log =log /dashboard.log --log-level=WARN &nohup ./bin/codis-proxy --ncpu=1 --config=config/proxy.toml --log =log /proxy.log --log-level=WARN &./bin/codis-server config/redis-6379.conf ./bin/codis-server config/redis-6380.conf nohup ./bin/codis-fe --ncpu=1 --log =log /fe.log --log-level=WARN --zookeeper=127.0.0.1:2181 --listen=0.0.0.0:8081 &./bin/codis-server config/sentinel.conf --sentinel http://192.168.184.131:8081/ New Proxy: 192.168.184.131:11080 New Group: 1 Add Server: 192.168.184.131:6379 to 1 192.168.184.131:6380 to 1 Auto-Rebalance: Rebalance All Slots
codis命令行
具体用法在上面的步骤中已经列出,使用命令行可以方便自动化运维管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 cd /data/codis/binassets codis-admin codis-dashboard codis-fe codis-ha codis-proxy codis-server redis-benchmark redis-cli redis-sentinel version ./codis-dashboard -h Usage: codis-dashboard [--ncpu=N] [--config=CONF] [--log =FILE] [--log-level=LEVEL] [--host-admin=ADDR] [--pidfile=FILE] [--zookeeper=ADDR|--etcd=ADDR|--filesystem=ROOT] [--product_name=NAME] [--product_auth=AUTH] [--remove-lock] codis-dashboard --default-config codis-dashboard --version Options: --ncpu=N set runtime.GOMAXPROCS to N, default is runtime.NumCPU(). -c CONF, --config=CONF run with the specific configuration. -l FILE, --log =FILE set path/name of daliy rotated log file. --log-level=LEVEL set the log-level, should be INFO,WARN,DEBUG or ERROR, default is INFO. ./codis-fe -h Usage: codis-fe [--ncpu=N] [--log =FILE] [--log-level=LEVEL] [--assets-dir=PATH] [--pidfile=FILE] (--dashboard-list=FILE|--zookeeper=ADDR [--zookeeper-auth=USR:PWD]|--etcd=ADDR [--etcd-auth=USR:PWD]|--filesystem=ROOT) --listen=ADDR codis-fe --version Options: --ncpu=N set runtime.GOMAXPROCS to N, default is runtime.NumCPU(). -d FILE, --dashboard-list=FILE set list of dashboard, can be generated by codis-admin. -l FILE, --log =FILE set path/name of daliy rotated log file. --log-level=LEVEL set the log-level, should be INFO,WARN,DEBUG or ERROR, default is INFO. --listen=ADDR set the listen address. ./codis-proxy -h Usage: codis-proxy [--ncpu=N [--max-ncpu=MAX]] [--config=CONF] [--log =FILE] [--log-level=LEVEL] [--host-admin=ADDR] [--host-proxy=ADDR] [--dashboard=ADDR|--zookeeper=ADDR [--zookeeper-auth=USR:PWD]|--etcd=ADDR [--etcd-auth=USR:PWD]|--filesystem=ROOT|--fillslots=FILE] [--ulimit =NLIMIT] [--pidfile=FILE] [--product_name=NAME] [--product_auth=AUTH] [--session_auth=AUTH] codis-proxy --default-config codis-proxy --version Options: --ncpu=N set runtime.GOMAXPROCS to N, default is runtime.NumCPU(). -c CONF, --config=CONF run with the specific configuration. -l FILE, --log =FILE set path/name of daliy rotated log file. --log-level=LEVEL set the log-level, should be INFO,WARN,DEBUG or ERROR, default is INFO. --ulimit =NLIMIT run 'ulimit -n' to check the maximum number of open file descriptors. ./codis-admin -h Usage: codis-admin [-v] --proxy=ADDR [--auth=AUTH] [config|model|stats|slots] codis-admin [-v] --proxy=ADDR [--auth=AUTH] --start codis-admin [-v] --proxy=ADDR [--auth=AUTH] --shutdown codis-admin [-v] --proxy=ADDR [--auth=AUTH] --log-level=LEVEL codis-admin [-v] --proxy=ADDR [--auth=AUTH] --fillslots=FILE [--locked] codis-admin [-v] --proxy=ADDR [--auth=AUTH] --reset-stats codis-admin [-v] --proxy=ADDR [--auth=AUTH] --forcegc codis-admin [-v] --dashboard=ADDR [config|model|stats|slots|group|proxy] codis-admin [-v] --dashboard=ADDR --shutdown codis-admin [-v] --dashboard=ADDR --reload codis-admin [-v] --dashboard=ADDR --log-level=LEVEL codis-admin [-v] --dashboard=ADDR --slots-assign --beg=ID --end=ID (--gid=ID|--offline) [--confirm] codis-admin [-v] --dashboard=ADDR --slots-status codis-admin [-v] --dashboard=ADDR --list-proxy codis-admin [-v] --dashboard=ADDR --create-proxy --addr=ADDR codis-admin [-v] --dashboard=ADDR --online-proxy --addr=ADDR codis-admin [-v] --dashboard=ADDR --remove-proxy (--addr=ADDR|--token=TOKEN|--pid=ID) [--force] codis-admin [-v] --dashboard=ADDR --reinit-proxy (--addr=ADDR|--token=TOKEN|--pid=ID|--all) [--force] codis-admin [-v] --dashboard=ADDR --proxy-status codis-admin [-v] --dashboard=ADDR --list-group codis-admin [-v] --dashboard=ADDR --create-group --gid=ID codis-admin [-v] --dashboard=ADDR --remove-group --gid=ID codis-admin [-v] --dashboard=ADDR --resync-group [--gid=ID | --all] codis-admin [-v] --dashboard=ADDR --group-add --gid=ID --addr=ADDR [--datacenter=DATACENTER] codis-admin [-v] --dashboard=ADDR --group-del --gid=ID --addr=ADDR codis-admin [-v] --dashboard=ADDR --group-status codis-admin [-v] --dashboard=ADDR --replica-groups --gid=ID --addr=ADDR (--enable |--disable ) codis-admin [-v] --dashboard=ADDR --promote-server --gid=ID --addr=ADDR codis-admin [-v] --dashboard=ADDR --sync-action --create --addr=ADDR codis-admin [-v] --dashboard=ADDR --sync-action --remove --addr=ADDR codis-admin [-v] --dashboard=ADDR --slot-action --create --sid=ID --gid=ID codis-admin [-v] --dashboard=ADDR --slot-action --remove --sid=ID codis-admin [-v] --dashboard=ADDR --slot-action --create-some --gid-from=ID --gid-to=ID --num-slots=N codis-admin [-v] --dashboard=ADDR --slot-action --create-range --beg=ID --end=ID --gid=ID codis-admin [-v] --dashboard=ADDR --slot-action --interval=VALUE codis-admin [-v] --dashboard=ADDR --slot-action --disabled=VALUE codis-admin [-v] --dashboard=ADDR --rebalance [--confirm] codis-admin [-v] --dashboard=ADDR --sentinel-add --addr=ADDR codis-admin [-v] --dashboard=ADDR --sentinel-del --addr=ADDR [--force] codis-admin [-v] --dashboard=ADDR --sentinel-resync codis-admin [-v] --remove-lock --product=NAME (--zookeeper=ADDR [--zookeeper-auth=USR:PWD]|--etcd=ADDR [--etcd-auth=USR:PWD]|--filesystem=ROOT) codis-admin [-v] --config-dump --product=NAME (--zookeeper=ADDR [--zookeeper-auth=USR:PWD]|--etcd=ADDR [--etcd-auth=USR:PWD]|--filesystem=ROOT) [-1] codis-admin [-v] --config-convert=FILE codis-admin [-v] --config-restore=FILE --product=NAME (--zookeeper=ADDR [--zookeeper-auth=USR:PWD]|--etcd=ADDR [--etcd-auth=USR:PWD]|--filesystem=ROOT) [--confirm] codis-admin [-v] --dashboard-list (--zookeeper=ADDR [--zookeeper-auth=USR:PWD]|--etcd=ADDR [--etcd-auth=USR:PWD]|--filesystem=ROOT) Options: -a AUTH, --auth=AUTH -x ADDR, --addr=ADDR -t TOKEN, --token=TOKEN -g ID, --gid=ID
Codis常见问题 在官方文档中列举了2个案例,Codis Admin(命令行工具)
注意:使用 codis-admin 是十分危险的。
codis-dashboard 异常退出的修复 1 2 3 [ERROR] store: acquire lock of codis-demo failed [error]: zk: node already exists
当 codis-dashboard 启动时,会在外部存储上存放一条数据,用于存储 dashboard 信息,同时作为 LOCK 存在。当 codis-dashboard 安全退出时,会主动删除该数据。当 codis-dashboard 异常退出时,由于之前 LOCK 未安全删除,重启往往会失败。因此 codis-admin 提供了强制删除工具:
确认 codis-dashboard 进程已经退出(很重要 );
运行 codis-admin 删除 LOCK:
1 $ ./bin/codis-admin --remove-lock --product=codis-demo --zookeeper=127.0.0.1:2181
codis-proxy 异常退出的修复 通常 codis-proxy 都是通过 codis-dashboard 进行移除,移除过程中 codis-dashboard 为了安全会向 codis-proxy 发送 offline
指令,成功后才会将 proxy 信息从外部存储中移除。如果 codis-proxy 异常退出,该操作会失败。此时可以使用 codis-admin 工具进行移除:
确认 codis-proxy 进程已经退出(很重要 );
运行 codis-admin 删除 proxy:
1 $ ./bin/codis-admin --dashboard=127.0.0.1:18080 --remove-proxy --addr=127.0.0.1:11080 --force
选项 --force
表示,无论 offline
操作是否成功,都从外部存储中将该节点删除。所以操作前,一定要确认该 codis-proxy 进程已经退出。
codis-dashboard和codis-proxy迁移小技巧 负责过codis-fe/dashboard迁移,也做过正常的codis-server scale-out横向扩容,踩过不少坑其中有2点需要提醒下大家
如果codis-dashboard使用Supervisord
或者Monit
来管理,需要注意添加--remove-lock
参数,原因上面已经写过了
1 2 /data/codis/bin/codis-dashboard --remove-lock --config=/data/codis/config/dashboard.toml --log =/data/codis/log/codis-dashboard.log --log-level=DEBUG
需要注意搭建codis-proxy时是否有添加--dashboard
参数,因为添加后启动proxy就会主动加入对应IP的dashboard,如果不添加这个参数则需要通过dashboard UI或者codis-admin命令行添加
1 2 /data/codis/bin/codis-proxy --config=/data/codis/config/proxy-auth.toml --log =/data/codis/log/codis-proxy-auth.log --log-level=DEBUG --dashboard=10.71.14.112:18092 --ncpu=8
Codis Zookeeper数据结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 cd /opt/zookeeper/bin./zkCli.sh -server 127.0.0.1:2181 ZooKeeper -server host:port cmd args stat path [watch] set path data [version] ls path [watch] delquota [-n|-b] path ls2 path [watch] setAcl path acl setquota -n|-b val path history redo cmdno printwatches on|off delete path [version] sync path listquota path rmr path get path [watch] create [-s] [-e] path data acl addauth scheme auth quit getAcl path close connect host:port [zk: 127.0.0.1:2181(CONNECTED) 4] ls /codis3 [gop-codis-auth, gop-codis-pay] [zk: 127.0.0.1:2181(CONNECTED) 8] ls /codis3/gop-codis-auth [proxy, slots, topom, group] [zk: 127.0.0.1:2181(CONNECTED) 9] ls /codis3/gop-codis-auth/proxy [proxy-62fba8c56577980aca48b869d6c1059d, proxy-bf965c6421da0ae004d4f142bec2dc45] [zk: 127.0.0.1:2181(CONNECTED) 12] get /codis3/gop-codis-auth/proxy/proxy-bf965c6421da0ae004d4f142bec2dc45 { "id" : 3, "token" : "bf965c6421da0ae004d4f142bec2dc45" , "start_time" : "2020-06-25 17:20:49.129561877 +0800 +08 m=+0.023604373" , "admin_addr" : "10.71.49.88:11082" , "proto_type" : "tcp4" , "proxy_addr" : "10.71.49.88:19082" , "product_name" : "gop-codis-auth" , "pid" : 65948, "pwd" : "/data/codis" , "sys" : "Linux sg-gop-10-71-49-88 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux" , "hostname" : "sg-gop-10-71-49-88" , "datacenter" : "" } cZxid = 0x3869 ctime = Thu Jun 25 17:25:09 SGT 2020 mZxid = 0x386a mtime = Thu Jun 25 17:25:09 SGT 2020 pZxid = 0x3869 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 519 numChildren = 0 [zk: 127.0.0.1:2181(CONNECTED) 13] get /codis3/gop-codis-auth/proxy/proxy-62fba8c56577980aca48b869d6c1059d { "id" : 2, "token" : "62fba8c56577980aca48b869d6c1059d" , "start_time" : "2020-06-23 11:10:54.526348352 +0800 +08 m=+0.024387970" , "admin_addr" : "10.71.49.89:11082" , "proto_type" : "tcp4" , "proxy_addr" : "10.71.49.89:19082" , "product_name" : "gop-codis-auth" , "pid" : 24684, "pwd" : "/data/codis" , "sys" : "Linux sg-gop-10-71-49-89 3.10.0-957.27.2.el7.x86_64 #1 SMP Mon Jul 29 17:46:05 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux" , "hostname" : "sg-gop-10-71-49-89" , "datacenter" : "" } cZxid = 0x4d ctime = Tue Jun 23 11:12:31 SGT 2020 mZxid = 0x4e mtime = Tue Jun 23 11:12:31 SGT 2020 pZxid = 0x4d cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 519 numChildren = 0 [zk: 127.0.0.1:2181(CONNECTED) 21] get /codis3/gop-codis-auth/group/group-0001 { "id" : 1, "servers" : [ { "server" : "10.71.49.88:6379" , "datacenter" : "" , "action" : {}, "replica_group" : false }, { "server" : "10.71.49.88:6380" , "datacenter" : "" , "action" : { "state" : "synced" }, "replica_group" : false } ], "promoting" : {}, "out_of_sync" : false } cZxid = 0x1f ctime = Tue Jun 23 10:34:10 SGT 2020 mZxid = 0x25 mtime = Tue Jun 23 10:34:25 SGT 2020 pZxid = 0x1f cversion = 0 dataVersion = 6 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 440 numChildren = 0
Codis HA Codis 的架构本身分成 Proxy 集群 + Redis 集群,Proxy 集群的高可用,可以基于 Zookeeper 来做故障转移,而 Redis 集群的高可用是借助于 Redis Sentinel 开源的哨兵集群来实现,那边 Codis 作为非 Redis 组件,需要解决的一个问题就是如何集成 Redis 哨兵集群。
我分享2种实践方案,都是经历过线上 12000000 qps 考验的架构,重点看下HA方案
前置任务:内部流量均使用CoreDNS转发请求至LVS VIP 此时codis-proxy作为realserver客户端,或者直接分配给codis-proxy但DNS无故障检测机制
codis-server使用keepalived HA架构类似Redis主从同步HA方案,codis-dashboard group server填写VIP地址,依赖少但需要使用额外计算资源
codis-server使用sentinel保障HA高可用切换,这是codis官方的推荐方案,但由于codis目前已经处于停滞状态,如果出现组件bug影响也会非常大
Codis in Kubernetes
好处是上线很快,坏处是出了问题你就直接嗝屁了,debug困难推荐先上测试后上生产
https://github.com/CodisLabs/codis/tree/release3.2/kubernetes
Codis监控 Codis的监控主要分为3部分,衍生部分还包括Zookeeper,这里就不展开了
Codis Proxy
Codis Redis Cluster
Codis Sentinels
Redis迁移至Codis 分两种情况:
原来使用 twemproxy 的用户: 可以, 使用codis项目内的redis-port工具, 可以实时的同步 twemproxy 底下的 redis 数据到你的 codis 集群上. 搞定了以后, 只需要你修改一下你的配置, 将 twemproxy 的地址改成 codis 的地址就好了. 除此之外, 你什么事情都不用做.
原来使用 Redis 的用户: 如果你使用了 doc/unsupported_cmds 中提到的命令,是无法直接迁移到 Codis 上的. 你需要修改你的代码, 用其他的方式实现.
先搭建好 codis 集群并让 codis-proxy 正确运行起来。对线上每一个 redis 实例运行一个 redis-port 来向 codis 导入数据,例如:
1 2 3 4 5 6 7 for port in {6379,6380,6479,6480}; do nohup redis-port sync --ncpu=4 --from=redis-server:${port} \ --target=codis-proxy:19000 > ${port}.log 2>&1 & sleep 5 done tail -f *.log
每个 redis-port 负责将对应的 redis 数据导入到 codis
多个 redis-port 之间不互相干扰,除非多个 redis 上的 key 本身出现冲突
单个 redis-port 可以将负责的数据并行迁移以提高速度,通过 –ncpu 来指定并行数
导入速度受带宽以及 codis-proxy 处理速度限制(本质是大量的 slotsrestore 操作)
完成数据迁移,在适当的时候将服务指向 Codis,并将原 redis 下线,旧 redis 下线时,会导致 reids-port 链接断开,于是自动退出
redis-port RedisShake redis-rdb-tools
Codis扩容 Codis FE, Codis-Dashboard, Codis-Proxy如果没有添加特殊的参数可以算是无状态组件,扩容或迁移相对比较容易
Codis 可以实现在线不停服务进行扩容,具体的步骤如下:
安装配置 codis-server 主从
打开 codis 管理界面,新建 server group 并添加刚刚安装的 redis 实例(注意:codis 默认第一个添加的是 master)
规划 slot 分布,把部分 slot 迁移到新的 server group 中
备注说明
slot 迁移的过程中,Codis 服务可以正常访问,codis 的迁移机制可以保证数据的一致性
迁移时,key 都是单个进行迁移,并且不能同时运行多个迁移任务,所以 codis 的迁移时间会比较长。一定要在扩容前留有足够的时间和空间。
Codis其他经验分享 1.关于 HOT KEY, HOT KEY 很影响 Codis/Redis 的性能,这点如果你监控不到位,你就得花一些力气去找到底是哪组出了问题,再 monitor 看看找出是哪个应用干的,比较费时费力,所以在交付 rd 上线时, 我们就严肃声明坚决不允许存在 HOT KEY,宁可使用笨方法多消耗一些内存,也要降低线上故障的风险。
2.关于 BIG KEY, 这点风险更为巨大:
由于 Codis 支持 “resharding without restarting cluster”,如果迁移失败,所导致的后果也是不可简单衡量的。Redis 是串行提供服务的,所以当迁移该 BIG KEY 时,其他的请求就会被 BLOCK 住,这点是十分危险的,访问该组的请求皆会失败。
由于 Codis-ha 也会依赖该节点的返回来判断 Codis-server 是否挂掉,如果无响应超过设置时间,便会强制提升 SLAVE 至 MASTER,导致整个迁移任务失败。这时如果 Proxy 的信息没有更新的话,并且迁移故障的 KEY 所在 SLOT 可能会存在 KEY 的信息不完整,虽然服务恢复,但是仍有大量 key 失效。
所以一般不推荐使用 Codis 存大的 HASH 表,LIST 等等,并且在迁移之前,至少要对该 Group 做一次检查 BIG KEY 即:redis-cli –bigkeys 查看是否有 BIG KEY 存在,再酌情迁移。
3.关于 Codis-server
一般 Codis-proxy 或者 Codis-dashboard 我们使用 supervisor 管理,在进程退出的情况下立即拉起来重新服务,而 Codis-Server 则不推荐使用该方式,原因是这样的:一般作为 Codis-server,是关闭 rdb dump 的,如果 Codis-server 挂掉,当重新启动时,是没有 rdb 文件的,或者 rdb 文件是上一次切换之前的。如果挂掉立即重新启动,则该 Codis 有可能是空的,或者数据不是最新,而同时,SLAVE 同步,也会清空数据库,或者同步旧数据。
参考文章 Codis作者黄东旭细说分布式Redis架构设计和踩过的那些坑们 史上最全 Redis 高可用技术解决方案大全 深入浅出百亿请求高可用 Redis (codis) 分布式集群揭秘 大规模 codis 集群的治理与实践 避免 Redis (Codis) 的 Timeout 及监控 10 分钟彻底理解 Redis 的持久化和主从复制 Codis AutoRebalance 流程学习 使用codis-admin搭建codis集群 Codis 运维D4 - Codis3详解 4000余字为你讲透Codis内部工作原理 Codis源码分析