Codis是一套用go语言编写的,为了应对高并环境下的redis集群软件,原理是对一个redis key操作前,先把这个key通过crc32算法,分配到不同redis的某一个slot上,实现并发读写功能.而且能通过zookeeper调用redis-sentinel来实现故障切换功能.现在最新版本是3.2.1,依托于redis3.2.9开发出来.
优点:实现高并发读写,数据一致性高.
缺点:性能有较大损耗,故障切换无法保证不丢key,无法进行读写分离.
架构介绍
1.需要用到的软件有:
codis3.2.1
描述:codis集群套件,里面含有redis相关程序,和集群专用程序,主要功能程序解析:
codis-server:属于redis-server优化版,基于 redis-3.2.9 分支开发。增加了额外的数据结构,以支持 slot 有关的操作以及数据迁移指令。
codis-proxy:客户端连接的 Redis 代理服务, 实现了 Redis 协议。 除部分命令不支持以外(例如:keys *,flush ),表现的和原生的 Redis 没有区别(就像 Twemproxy)。
redis-sentinel:可以实现对Redis的监控、通知、自动故障转移。如果Master不能工作,则会自动启动故障转移进程,将其中的一个Slave提升为Master,其他的Slave重新设置新的Master服务。Sentinel的配置由 codis-dashboard和zookeeper一起控制,不需要手工填写.
codis-dashboard:集群管理工具,支持 codis-proxy、codis-server 的添加、删除,以及据迁移等操作。在集群状态发生改变时,codis-dashboard 维护集群下所有 codis-proxy 的状态的一致性。
codis-fe:集群web管理界面。
go1.10.1
描述:codis依赖语言包
jdk1.8
描述:zookeeper依赖语言包
zookeeper-3.4.11
描述:用于存放数据配置路由表。zookeeper简称zk。在生产环境中,zk部署越多,其可靠性越高。由于zk集群是以宕机个数过半才会让整个集群宕机,因此,奇数个zk更佳。
2. 逻辑架构如下:
访问层:访问方式可以是类似keepalived集群的vip方式,或者是通过java代码调用jodis控件再连接上zookeeper集群,然后查找到可用的proxy端,进而连接调用不同的codis-proxy地址来实现高可用的LVS和HA功能.
代理层:中间层由codis-proxy和zookeeper处理数据走向和分配,通过crc32算法,把key平均分配在不同redis的某一个slot中.实现类似raid0的条带化,在旧版本的codis中,slot需要手工分配,在codis3.2之后,只要点一个按钮slot会自动分配,相当方便,但是也可以手动分配,需要另外调用codis-admin命令.
数据层:最后codis-proxy把数据存进真实的redis-server主服务器上,由于codis的作者黄东旭相当注重数据一致性,不允许有数据延时造成的数据不一致,所以架构从一开始就没考虑主从读写分离.从服务器仅仅是作为故障切换的冗余架构,由codis-dashboard监控各服务的状态,然后通过改写zookeeper数据和调用redis-sentinel实现故障切换功能.
3.因为机器有限,部署的架构如下:
zookeeper集群:
192.168.119.128:2181
192.168.119.129:2181
192.168.119.130:2181
codis-config和codis-dashboard:
192.168.119.129:18087
192.168.119.130:8090
codis-proxy:
192.168.119.129:19000
192.168.119.130:19000
codis-server:
192.168.119.128:6379(主),192.168.119.128:6380(从)
192.168.119.129:6379(主),192.168.119.129:6380(从)
192.168.119.130:6379(主),192.168.119.130:6380(从)
安装部署
1. 下载程序代码
1)下载golang语言程序包,
按正常途径是要×××的,不过国内地址也有人放出来了,因为codis3.2要求至少是1.7或1.8以上版本的,那干脆下最新版吧.
https://dl.google.com/go/go1.11.4.linux-amd64.tar.gz 或者通过本站附件下载 :点击下载
2)下载java语言程序包,
Java的下载地址一直在变,所以最好自己上去看着来下载
3)下载zookeeper程序
直接就是程序包,不用编译了,好方便
http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.11/zookeeper-3.4.11.tar.gz
4)下载codis3.2.1
直接就是程序包,不用编译了,好方便
https://github.com/CodisLabs/codis/releases/download/3.2.1/codis3.2.1-go1.7.6-linux.tar.gz
2. 安装程序
1) 安装java
#解压程序包 tar xf jdk-8u144-linux-x64.tar.gz #移动到指定目录 mv jdk1.8.0_144/ /usr/local/ #进入指定目录,并创建程序软连接 cd /usr/local/ ln -sf jdk1.8.0_144/ jdk #创建环境变量文件 echo “export JAVA_HOME=/usr/local/jdk export JRE_HOME=/usr/local/jdk/jre export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib export PATH=$PATH:$JAVA_HOME/bin ”> /etc/profile.d/java.sh #重载环境变量 source /etc/profile #测试检查是否安装完成 java -version java version "1.8.0_144" Java(TM) SE Runtime Environment (build 1.8.0_144-b01) Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
安装完毕
2) 安装golang
#解压程序包 tar xf go1.11.4.linux-amd64.tar.gz #移动到指定目录 mv go /usr/local/ #把程序包里的命令软连接到系统默认命令目录 ln -sf /usr/local/go/bin/* /usr/bin/ #测试检查是否安装完成 go version go version go1.11.4 linux/amd64
安装完成
3) 安装zookeeper
#解压程序包 tar xf zookeeper-3.4.11.tar.gz #移动到指定目录 mv zookeeper-3.4.11 /usr/local/ #进入指定目录,并创建程序软连接 cd /usr/local/ ln -sf zookeeper-3.4.11/ zookeeper
安装完成,等候配置.
4) 安装codis(版本号根据实际而定)
#解压程序包 tar xf codis3.2.1-go1.7.6-linux.tar.gz #移动到指定目录 mv codis3.2.1-go1.7.6-linux /usr/local/ #进入指定目录,并创建程序软连接 cd /usr/local/ ln -sf codis3.2.1-go1.7.6-linux/ codis
安装完成,等候配置,因为我们用的都是二进制程序包,只要依赖包有正常安装,就不会报错,直接就能用,所以安装就很简单.
3. 配置程序
1) 配置zookeeper,3台一起都是这么配置
#设置hosts跳转规则,好像不这么设置的话,不能顺利启动 echo “192.168.119.128 zookeeper-node1 192.168.119.129 zookeeper-node2 192.168.119.130 zookeeper-node3” >> /etc/hosts #创建程序目录 mkdir -p /data/zookeeper #创建配置文件,文件夹里有一个模板,有兴趣可以看看 vim /usr/local/zookeeper/conf/zoo.cfg #最大连接数设置(单ip限制). 注:默认60,设成0即无限制. maxClientCnxns=500 #一个周期(tick)的时长(单位:毫秒). tickTime=28800 #初始化同步阶段最多耗费tick个数. 注:可用默认值 initLimit=10 #等待应答的最大间隔tick个数. 注:可用默认值 syncLimit=5 #数据存储目录,刚才创建那个. 注:勿放在/tmp目录 dataDir=/data/zookeeper/ #通信端口. 注:可用默认值 clientPort=2181 server.1=zookeeper-node1:2888:3888 server.2=zookeeper-node2:2888:3888 server.3=zookeeper-node3:2888:3888
生成ID,这里需要注意, myid对应的zoo.cfg的server.ID.比如zookeeper-node2对应的myid应该是2,不按规定设置,zookeeper集群将无法启动.
echo "1"> /data/zookeeper/myid
#zoo.cfg最后三行特别说明
说明:server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
#最后启动,因为zookeeper的server是有顺序的,最好是按顺序启动,先启动server.1再启动server2,最后启动server.3这样 /usr/local/zookeeper/bin/zkServer.sh start #查看状态,会有follower和leader的区别,他们自己会选谁是leader /usr/local/zookeeper/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg Mode: follower
配置并启动完毕.
2) 配置codis-server,3台一起都是这么配置
注意:codis-server就是redis-server程序,属于codis优化版本,配合codis集群使用.
所以就是配置个redis的主从结构,实际生产环境不要搭在一起
#创建redis数据目录,配置文件目录,日志目录 mkdir -p /data/redis/data/config/ mkdir -p /data/redis/data/logs/ #创建主库的配置文件,暂时只配置这些,其他先默认 vim /data/redis/data/config/redis_6379.conf #允许后台运行 daemonize yes #设置端口,最好是非默认端口 port 6379 #绑定登录IP,安全考虑,最好是内网 bind * #命名并指定当前redis的PID路径,用以区分多个redis pidfile "/data/redis/data/config/redis_6379.pid" #命名并指定当前redis日志文件路径 logfile "/data/redis/data/logs/redis_6379.log" #指定RDB文件名,用以备份数据到硬盘并区分不同redis,当使用内存超过可用内存的45%时触发快照功能 dbfilename "dump_6379.rdb" #指定当前redis的根目录,用来存放RDB/AOF文件 dir "/data/redis/data" #当前redis的认证密钥,redis运行速度非常快,这个密码要足够强大, #所有codis-proxy集群相关的redis-server认证密码必须全部一致 requirepass "123" #当前redis的最大容量限制,建议设置为可用内存的45%内,最高能设置为系统可用内存的95%, #可用config set maxmemory 去在线修改,但重启失效,需要使用config rewrite命令去刷新配置文件 #注意,使用codis集群,必须配置容量大小限制,不然无法启动 maxmemory 100000kb #LRU的策略,有四种,看情况选择 maxmemory-policy allkeys-lru #如果做故障切换,不论主从节点都要填写密码且要保持一致 masterauth "123"
#创建从库的配置文件,暂时只配置这些,其他先默认 vim /data/redis/data/config/redis_6380.conf #允许后台运行 daemonize yes #设置端口,最好是非默认端口 port 6380 #绑定登录IP,安全考虑,最好是内网 bind * #命名并指定当前redis的PID路径,用以区分多个redis pidfile "/data/redis/data/config/redis_6380.pid" #命名并指定当前redis日志文件路径 logfile "/data/redis/data/logs/redis_6380.log" #指定RDB文件名,用以备份数据到硬盘并区分不同redis,当使用内存超过可用内存的45%时触发快照功能 dbfilename "dump_6380.rdb" #指定当前redis的根目录,用来存放RDB/AOF文件 dir "/data/redis/data" #当前redis的认证密钥,redis运行速度非常快,这个密码要足够强大 #所有codis-proxy集群相关的redis-server认证密码必须全部一致 requirepass "123" #当前redis的最大容量限制,建议设置为可用内存的45%内,最高能设置为系统可用内存的95%, #可用config set maxmemory 去在线修改,但重启失效,需要使用config rewrite命令去刷新配置文件 #注意,使用codis集群,必须配置容量大小限制,不然无法启动 maxmemory 100000kb #LRU的策略,有四种,看情况选择 maxmemory-policy allkeys-lru #如果做故障切换,不论主从节点都要填写密码且要保持一致 masterauth "123" #配置主节点信息 slaveof 10.0.2.5 6379
除了端口号不同带来的文件名不同.实际上从库配置只是多了最后一行,指定了主库地址
#然后就可以启动了,我一开始就说过codis-server就是redis-server /usr/local/codis/codis-server /data/redis/data/config/redis_6379.conf /usr/local/codis/codis-server /data/redis/data/config/redis_6380.conf #验证一下 ss -ntplu |grep codis-server tcp LISTEN 0 128 *:6379 *:* users:(("codis-server",pid=2192,fd=4)) tcp LISTEN 0 128 *:6380 *:* users:(("codis-server",pid=2197,fd=4))
启动方式和redis-server一样,指定配置文件就可以启动.这就配置并启动成功了.
3) 配置redis-sentinel,3台一起都是这么配置
正确来说,redis-sentinel是要配置主从架构才能生效,但是在codis集群中并不一样,因为他的配置由zookeeper来维护,所以,这里codis使用的redis-sentinel只需要配置一些基本配置就可以了.
#我们把配置放到redis数据目录的配置文件目录 vim /data/redis/data/config/sentinel.conf bind 0.0.0.0 protected-mode no port 26379 dir "/data/redis/data" pidfile "/data/redis/data/config/sentinel_26379.pid" logfile "/data/redis/data/logs/sentinel_26379.log" daemonize yes
#然后就可以启动了 /usr/local/codis/redis-sentinel /data/redis/data/config/sentinel.conf
#验证一下 /usr/local/codis/redis-cli -p 26379 -c info Sentinel # Sentinel sentinel_masters:3 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=codis-test1-3,status=ok,address=10.0.2.7:6380,slaves=1,sentinels=3 master1:name=codis-test1-1,status=ok,address=10.0.2.5:6379,slaves=1,sentinels=3 master2:name=codis-test1-2,status=ok,address=10.0.2.6:6379,slaves=1,sentinels=3
配置并启动成功.
注意:没有配置好codis-dashboard会没最后那几行,因为还不受zookeeper控制,所以是正常的,配置好之后才会自动加载进来.
4) 配置codis-proxy,这次只有两台要配置,当然你也可以配三台
这个是codis集群的核心,实际上他也没配主从架构,配置也是从zookeeper拿来用的,所以,直接来看配置吧
#配置很多,我们先生成一下默认的配置文件 /usr/local/codis/codis-proxy --default-config | tee ./proxy.conf #然后我们把配置放到redis数据目录的配置文件目录,再更改关键位置,其他默认即可 vim /data/redis/data/config/proxy.conf #项目名称,会登记在zookeeper里,如果你想一套zookeeper管理多套codis,就必须区分好 product_name = "codis-test1" # 设置登录dashboard的密码(与真实redis中requirepass一致) product_auth = "123" #客户端(redis-cli)的登录密码(与真实redis中requirepass不一致),是登录codis的密码 session_auth = "123456" #管理的端口,0.0.0.0即对所有ip开放,基于安全考虑,可以限制内网 admin_addr = "0.0.0.0:11080" #用那种方式通信,假如你的网络支持tcp6的话就可以设别的 proto_type = "tcp4" #客户端(redis-cli)访问代理的端口,0.0.0.0即对所有ip开放 proxy_addr = "0.0.0.0:19000" #外部配置存储类型,我们用的就是zookeeper,当然也是还有其他可以支持,这里不展开说 jodis_name = "zookeeper" #配置zookeeper的连接地址,这里是三台就填三台 jodis_addr = "10.0.2.5:2181,10.0.2.6:2181,10.0.2.7:2181" #zookeeper的密码,假如有的话 jodis_auth = "" #codis代理的最大连接数,默认是1000,并发大要调大 proxy_max_clients = 1000 #假如并发太大,你可能需要调这个pipeline参数,大多数情况默认就够了 session_max_pipeline = 10000
#然后就可以启动了, /usr/local/codis/codis-proxy --ncpu=1 --config=/data/redis/data/config/proxy.conf --log=/data/redis/data/logs/proxy.log &
#验证一下 ss -ntplu |grep codis-proxy tcp LISTEN 0 128 *:19000 *:* users:(("codis-proxy",pid=2075,fd=4)) tcp LISTEN 0 128 :::11080 :::* users:(("codis-proxy",pid=2075,fd=6))
--ncpu 指定使用多少个cpu,我的是虚拟机,所以就1了,如果你是多核,那就填多个
--config 指定配置文件,就是刚才的配置文件
--log 指定输出日志文件
配置并启动成功,但是暂时还用不了,因为还需要配置codis-dashboard才能最终完成.
5) 配置codis-dashboard,只需要配置一台机上
这个属于管理配置codis集群信息的工具,配置完之后的配置信息会自动加载到zookeeper集群,即使这个服务挂了,配置都还在zookeeper上,所以不用考虑高可用,单点就足够了,大不了重新启动一下也不是特别麻烦,配置界面由codis-fe来实现.因为限制于zookeeper的命名空间,通常是和proxy一套配置.
#我们也可以用程序来生成一下默认配置文件 /usr/local/codis/codis-dashboard --default-config | tee ./dashboard.conf #然后我们把配置放到redis数据目录的配置文件目录,再更改关键位置,其他默认即可 vim /data/redis/data/config/dashboard.conf #外部配置存储类型,我们用的就是zookeeper,当然也是还有其他可以支持,这里不展开说 coordinator_name = "zookeeper" #配置zookeeper的连接地址,这里是三台就填三台 coordinator_addr = "10.0.2.5:2181,10.0.2.6:2181,10.0.2.7:2181" #项目名称,会登记在zookeeper里,如果你想一套zookeeper管理多套codis,就必须区分好 product_name = "codis-test1" #所有redis的登录密码(与真实redis中requirepass一致),因为要登录进去修改数据 product_auth = "123" #codis-dashboard的通信端口,0.0.0.0表示对所有开放,最好使用内网地址 admin_addr = "0.0.0.0:18080" #如果想要在codis集群在故障切换功能上执行一些脚本,可以配置以下两个配置 sentinel_notification_script = "" sentinel_client_reconfig_script = ""
#然后就可以启动了, /usr/local/codis/codis-dashboard --ncpu=1 --config=/data/redis/data/config/dashboard.conf --log=/data/redis/data/logs/codis_dashboard.log --log-level=WARN & #验证一下 ss -ntplu |grep codis-dashboard tcp LISTEN 0 128 :::18080 :::* users:(("codis-dashboard",pid=2021,fd=5))
--ncpu 指定使用多少个cpu
--config 指定配置文件
--log 指定输出日志文件
--log-level 指定日志等级,有INFO,WARN,DEBUG,ERROR
安装完成,就差最后一步就可以开始配置.
由于codis-dashboard本身是不需要密码登录的,所以这将会非常危险,强烈建议使用内网地址,而作者表示将会在下个版本考虑增加codis-dashboard的认证密码.
6) 配置codis-fe,只需要配置一台机上
这个是属于web界面操作codis-dashboard配置的工具,web代码文件在codis安装文件夹的目录下,具体是:/usr/local/codis/assets/这个目录.可多台dashboard共用.
这个工具本身不需要配置文件就能启动,只需要指定codis-dashboard的ip和端口就可以了,但是我为了方便管理,还是生成一个配置文件的好.
#生成一下配置文件,其实也就是codis-dashboard的ip和端口 /usr/local/codis/codis-admin --dashboard-list --zookeeper=192.168.119.130:2181 >codis.json #然后我们把配置放到redis数据目录的配置文件目录,看一下 cat /data/redis/data/config/codis.json [ { "name": "codis-test1", "dashboard": "192.168.119.130:18080" } ] #然后就可以启动了, /usr/local/codis/codis-fe --ncpu=1 --log=/data/redis/data/logs/fe.log --log-level=WARN --dashboard-list=/data/redis/data/config/codis.json --listen=0.0.0.0:8090 &
--ncpu 指定使用多少个cpu
--log 指定输出日志文件
--log-level 指定日志等级,有INFO,WARN,DEBUG,ERROR
--dashboard-list 指定dashboard的地址和项目名称,这里因为生成了文件,所以就指定成文件了
--listen 指定codis-fe的web登录端口,也就是我们通过8090来访问这个管理端了,0.0.0.0即对来访IP无限制,其实最好是限制内网
验证一下
全
全套安装完成,成功启动.开始下一步.
使用举例
因为有了web界面,基本上就都是界面操作了,非常方便,配置会直接加载到zookeeper里面去.
首先,我们先添加codis-proxy地址和端口
按顺序:
第一步,先添加codis-proxy的地址和管理端口,上面设置的是11080.
第二步,点击左方的橙色按钮,然后就添加完毕.
第三步,看到下方出现该有的codis-proxy地址就算完成了,然后看到右方的SYNC字样的颜色是绿色,则代表配置正常.
如果要删除记录,点击最右方的红色按钮即可.
然后,我们添加真实redis-server(也是codis-server)地址和端口
按顺序:
第一步,先创建一个组,准备把相关的一组主从放进去
第二步,点击按钮生成这个分组
第三步,添加真实redis-server地址,并选定一个分组,例如刚才创建的分组1
第四步,点击按钮生成配置
第五步,可以看到配置已经登记好,注意sync状态.
第六步,点击重新平衡所有slots数据块(任何添加和删除新旧节点都需要点击这个)
在旧版本中slots需要手动配置,但是3.2版本之后就改成自动分配了,所以已经不需要配置,点击一下就可以了.当然你也可以手动去分配.
最后配置sentinel的地址和端口
按顺序:
第一步,添加真实的sentinel地址和端口
第二步,点击按钮添加
第三步,查看状态,这里有点不一样,他会自动添加当前主从组架构由多少台,控制切换
也正如我之前说的,他们自动去改配置文件,可以去看看sentinel的配置文件证实一下,这里不展开来说了
都配置好了,就可以使用了,连接其中一个codis-proxy测一下,注意区分好登录的地址和端口,还有密码
/usr/local/codis/redis-cli -h 10.0.2.5 -p 19000 -a 123456 10.0.2.5:19000> info # Server redis_version:3.2.9 redis_git_sha1:f8bc4e32 redis_git_dirty:0 redis_build_id:2bdb8aa56be3fbc2 redis_mode:standalone os:Linux 4.10.0-19-generic x86_64 arch_bits:64 multiplexing_api:epoll gcc_version:4.8.4 process_id:2032 run_id:98e2364d837990dfb47be050901ef9e36ea113fa tcp_port:6379 uptime_in_seconds:16312 uptime_in_days:0 hz:10 lru_clock:3634855 executable:/usr/local/codis/codis-server config_file:/data/redis/data/config/redis_6379.conf # Clients connected_clients:71 client_longest_output_list:0 client_biggest_input_buf:0 blocked_clients:0 # Memory used_memory:61878808 used_memory_human:59.01M used_memory_rss:76623872 used_memory_rss_human:73.07M used_memory_peak:63148384 used_memory_peak_human:60.22M total_system_memory:1529741312 total_system_memory_human:1.42G used_memory_lua:37888 used_memory_lua_human:37.00K maxmemory:102400000 maxmemory_human:97.66M maxmemory_policy:allkeys-lru mem_fragmentation_ratio:1.24 mem_allocator:jemalloc-4.0.3
可以使用了.
压力测试
1.性能测试
先用自带的redis-benchmark来压测性能,模拟500个并发和100万个请求.注意区分好登录的地址和端口,还有密码
先压测codis-proxy的性能
/usr/local/codis/redis-benchmark -h 10.0.2.5 -p 19000 -a 123456 -c 500 -n 1000000 -q
再压测单节点的性能
/usr/local/codis/redis-benchmark -h 10.0.2.5 -p 6379 -a 123 -c 500 -n 1000000 -q
redis-benchmark参数解析:
-h ip地址
-p redis端口
-a 认证密码
-c 设定多少个并发连接
-n 总共多少个请求
-q 显示模式:简要模式
然后看图
可以看到,有些操作相差不大,有些相差甚远,性能损耗明显,不过作为集群应用,主要应对的是高并发环境,性能损耗是可以接受的,何况对于redis这种内存型高速应用来说,性能损耗基本没什么太大感知.
2.数据分布测试
然后是读写分布测试:
我写了个脚本来测试:
cat t-redis.sh #!/bin/bash hos="10.0.2.7" pot="19000" pawd="123456" cli="/usr/local/codis/redis-cli" keyset="keytest2" valueset="jlasdnfnsdfsdf;sdfhlkjahsdjlkfadfjkasdbbcjhdgasfyuefkbadjkhflk" dbname=2 a=0 for i in `seq 1 300000` do $cli -h $hos -p $pot -a $pawd -n $dbname 'set' ${keyset}${a} "${valueset}${a}" >/dev/null #echo $a let a++ done
脚本很简单,就是不断向codis集群写垃圾数据而已,执行脚本.
bash t-redis.sh
然后结果可以看web界面,因为你连接codis-proxy用info来看,其实是不准确的,那个显示的只是单台的数据.
可以看到,每一个组都分布得比较均匀,把压力都分到三台redis-server主服务器去了.
3.故障切换测试
然后来看故障切换,继续执行那个脚本
bash t-redis.sh
进入其中一台codis-server,例如10.0.2.5,此时状态是正常的.
开始模拟操作
#查找主库进程 ss -ntplu |grep codis-server tcp LISTEN 0 128 *:6379 *:* users:(("codis-server",pid=2032,fd=4)) tcp LISTEN 0 128 *:6380 *:* users:(("codis-server",pid=2037,fd=4)) #杀掉主库进程 kill 2032
此时从库接管了主库的进程,sentinels有提示信息.
可能有人发现组1的sync按钮变成了红色,也就是说主从失效,点一下就变回正常.
现在等待数据写完,我先把旧的主进程6379端口从新起来
/usr/local/codis/codis-server /data/redis/data/config/redis_6379.conf
然后看看:
看状态是恢复正常了,但是有计算机的同学可以算一下,我的脚本执行的总共是30万个key,但是现在少了几千个.
坑---1:
上面丢key的问题是由于redis-sentinel故障切换期间,整个codis集群并不会关闭对此故障redis-server的连接,所以codis-proxy依然会发送数据给当前故障的redis-server,而显然此时的redis-server是无法存储数据的,这就造成了丢key现象了.如果整个主从挂了,就会丢掉所有发送到此redis-server的key了,除非手工剔除故障节点.
虽然codis还自带有一种故障切换程序codis-ha,他属于一个守护进程,会连接codis-dashboard查看各节点状态,
#执行一下命令启动codis-ha,端口是codis-dashboard的端口 /usr/local/codis/codis-ha --dashboard=10.0.2.6:18080 --log=/data/redis/data/logs/ha.log --log-level=WARN &
--dashboard 指定dashboard的地址和端口
--log 指定日志文件
--log-level 指定日志等级,有INFO,WARN,DEBUG,ERROR
但是这个软件也是有缺陷,他会自动连接上dashboard检测各主从结构的健康信息,检测间隔很快(默认3秒,可修改参数--interval),检测到故障后,会将故障主库或者从库强制下线并删除在dashboard登记的信息.
虽然切换速度非常快,只会有很少的丢key现象(3秒还是会丢一些),但是后面会把故障旧主库强制下线,需要手动修改配置并重新启动redis-server(codis-server),还要再在codis-fe界面添加配置才行.
显然这是做不到全自动管理,有点麻烦了,而且也会让redis-sentinel变得没有意义了,所以只能两个方式选其一.
虽然看上去丢key现象是少了,但是依然还是有丢key的情况,只能说是50步笑100步,而且该组内其他 slave 实例是不会自动改变状态的,这些 slave 仍将试图从旧的 master 上同步数据,因而会导致组内新的 master 和其他 slave 之间的数据不一致。因此当出现主从切换时,需要管理员手动创建新的 sync action 来完成新 master 与 slave 之间的数据同步,这样反而增加了手动操作的工作量,各位对于codis-ha和redis-sentinel的集群的选择还是需要多考虑一些实际情况.
所以,说到底就是codis的故障切换没有做好,如果对丢key可以容忍的,就开redis-sentinel就足够了,对于数据一致性要求高的,就开codis-ha加脚本来实现比较好,各取所需.
实时添加删除节点
codis的另一个卖点就是可以在线添加/删除redis-server(codis-server)节点,做到实时扩容和更换问题节点,对于单点redis而言优势明显,不用重启服务就能有更大的空间,也可以在线切换掉有问题的节点.不过要注意,可以实时扩容和故障切换,并不代表没有性能损耗,真的要做也是要注意线上压力,避免性能压力导致的服务不可用.
实现的原理是因为codis集群把各个redis-server节点都用规则分成了多个slots数据块(总共1024个).需要扩容只要把新的节点添加完成后,在把这些slots信息从新分配就可以达到扩容效果,需要故障切换则把问题redis-server节点的slots迁移走,然后就可以把这个节点下架了,对于线上环境可以说是几乎没感知,试验过程中也没发现有丢key现象,就是性能有所下降,但是我测试的环境下性能下降还能接受,大概只有10%-30%的性能损耗.
开始试验,首先我们假设添加两个新的redis-server节点,都直接是主库,没有从库环境(因为这次不需要测故障切换):
10.0.2.5:16379
10.0.2.6:16379
1.添加一个节点,等于是扩容,这还是比较简单
第一步,和之前差不多,先创建一个新组,点击按钮确认添加
第二步,把新地址添加到新的组,点击按钮确认添加
第三步,确认新的地址已成功添加进去
第四步,点击按钮,从新分配所有slots数据块
这里唯一问题就是最后一步,重新分配会耗费一定资源,codis会自动平衡数据块的分布,所以会有数据迁移过程,但是据我测试的结果来看,并不很严重,大概在20%左右.
根据它自带的监控来看的话,
可以看到之前正常情况的qps接近1500,刚点重新平衡下降比较严重一些,后面就大概有20%的性能损耗那样子,最后迁移完毕就恢复正常了.当然这是数据量少的情况,如果数据量多,这个迁移时间就恐怕不是那么简单了.
2.切换一个节点,并下架
正常下架只需要点击这个按钮
但是因为里面还有数据,是不允许直接下架的
所以我们要先迁移数据,如下所示:
第一步,确认一个需要迁移的组的数据块的编号,例如这里499-512的块是数据组4的,我现在要迁移组4,就选定这个
第二步,把刚才获取到的信息填进去,就是把500这个编号的数据块从组4迁移到组5,点击按钮执行
然后你就会看到,
显然组4的信息消失了,codis把组4的数据块都迁移到了组5去了,
这个时候,这个redis-server节点就可以删除了
至于还需不需要重新填补,这个问题则需要自身考虑.如果不需要填补,最好再点一下重新平衡slots比较好.
可以看到,又重新平衡了.
故障处理
1.codis-dashboard无法启动,并提示:
[ERROR] store: acquire lock of codis-test1 failed
[error]: zk: node already exists
由于是测试环境,我经常强制关机,导致codis-dashboard没有正常关闭.直接造成zookeeper里面的状态没有更新,最终新启动的codis-dashboard不能注册进zookeeper,一直提示已存在而被强制关闭.
修复方法也不难,就是删除这个lock的状态键值就可以了
#输入项目名和zookeeper地址 /usr/local/codis/codis-admin --remove-lock --product=codis-test1 --zookeeper=10.0.2.6:2181
然后,codis-dashboard又可以正常启动了.
codis-admin是可以全权控制codis集群的工具,所有添加/删除/修改的工作都可以用他来实现,参数很多,这里只是举例了一个方法,详细可以参照codis-admin --help