Redis作为目前最常用的内存NOSQL数据库,使用的场景非常的广泛.但是在3.0以前官方一直都是没有集群的方案的.相当于是一个单机内存数据库.为了其高可用,集群的部署是非常有必要的.因此,各种第三方厂商都推出了自己的集群方案.使用的最多的就是豌豆荚
开源的Codis
,Twitter
开源的Twemproxy
,Netflix
的Dynamo
.
目前Redis是3.0.4
版本,其中已经自带了Redis Cluster
.但是官方的方案有一些架构上的问题.
- 首先,官方方案是完全的去中心化的,依靠自己Server的网络通信来进行同步.那么,在节点比较多的情况下,性能上是有影响的.
- 其二,一个redis进程既负责读写数据又负责集群交互,虽然设计者已经尽可能简化了代码和逻辑,但还是让redis从一个内存NoSQL变成了一个分布式NoSQL.
- 其三,官方方案没有采用一致性哈希,而是预分配slot的形式来进行分片.新节点的加入不是自动的,依赖于外部的ruby脚本.
- 其四,集群版本和单机版本数据不兼容,客户端不兼容.它的集群分发依赖于客户端的驱动. 目前只有JAVA的
Jedis Driver
支持了Redis Cluster
的连接.而spring-data-redis
目前也还没有支持官方方案.因此我们不能直接调用. - 其五,集群至少要分三片,加上主从备份.也就是说至少需要6个节点才能组建
Redis
集群.
从上面可以看出,目前官方的方案.并不适合我们.它现在还不稳定,开源社区的支持也比较少.
从其他三方的方案中进行对比,最后选择Codis
作为我们Redis
集群部署的方案.
官方集群方案的部署
虽然目前我们不会使用官方的方案,但是不排除以后会使用.毕竟是官方的东西,应该要靠谱些.
-
下载 并编译最新的Redis. 由于Redis是C写的.并且网上只有源码.因此,最好是直接下载源码到
类unix
机器上,然后调用sudo make
命令自动的编译出可运行的版本. -
最少集群6个节点,因此在我的磁盘上建立一个
redis-test
文件夹.然后建立6个子文件夹,作为6个节点.123mkdir redis-testcd redis-testmkdir 7000 7001 7002 7003 7004 7005 -
然后拷贝6分编译好的redis到各个目录中去.
-
修改redis.conf文件.
1234567port 7005pidfile /Volumes/EXCHANGE/redis-test/7000/redis.pidcluster-enabled yes# 和端口对应cluster-config-file nodes.confcluster-node-timeout 5000appendonly yes -
修改好后,进入每一个的src文件夹,执行
./redis-server ../redis.conf
启动6个服务. -
随便找一个节点的src目录,找到
redis-trib.rb
文件.然后执行:1./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005其中的
--replicas 1
指的是从服务的数量.执行了这句后,会显示出集群的分布情况.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152>>> Creating clusterConnecting to node 127.0.0.1:7000: OKConnecting to node 127.0.0.1:7001: OKConnecting to node 127.0.0.1:7002: OKConnecting to node 127.0.0.1:7003: OKConnecting to node 127.0.0.1:7004: OKConnecting to node 127.0.0.1:7005: OK>>> Performing hash slots allocation on 6 nodes...Using 3 masters:127.0.0.1:7000127.0.0.1:7001127.0.0.1:7002Adding replica 127.0.0.1:7003 to 127.0.0.1:7000Adding replica 127.0.0.1:7004 to 127.0.0.1:7001Adding replica 127.0.0.1:7005 to 127.0.0.1:7002M: d450eaf8b29ccc57c5ab851868a0e23b41d0f50c 127.0.0.1:7000slots:0-5460 (5461 slots) masterM: b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1b 127.0.0.1:7001slots:5461-10922 (5462 slots) masterM: e81eea0243d2976daca5b349ec0bc2d109ac81d0 127.0.0.1:7002slots:10923-16383 (5461 slots) masterS: 52a634c534ca5db005dbc31494676e08454ebfa4 127.0.0.1:7003replicates d450eaf8b29ccc57c5ab851868a0e23b41d0f50cS: 270c10d1f3b85438b74b8fd5c9d91a3e0ce8a0da 127.0.0.1:7004replicates b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1bS: e6eae15e4883cd408fe889e85565a38f6e030de7 127.0.0.1:7005replicates e81eea0243d2976daca5b349ec0bc2d109ac81d0Can I set the above configuration? (type 'yes' to accept): yes>>> Nodes configuration updated>>> Assign a different config epoch to each node>>> Sending CLUSTER MEET messages to join the clusterWaiting for the cluster to join...>>> Performing Cluster Check (using node 127.0.0.1:7000)M: d450eaf8b29ccc57c5ab851868a0e23b41d0f50c 127.0.0.1:7000slots:0-5460 (5461 slots) masterM: b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1b 127.0.0.1:7001slots:5461-10922 (5462 slots) masterM: e81eea0243d2976daca5b349ec0bc2d109ac81d0 127.0.0.1:7002slots:10923-16383 (5461 slots) masterM: 52a634c534ca5db005dbc31494676e08454ebfa4 127.0.0.1:7003slots: (0 slots) masterreplicates d450eaf8b29ccc57c5ab851868a0e23b41d0f50cM: 270c10d1f3b85438b74b8fd5c9d91a3e0ce8a0da 127.0.0.1:7004slots: (0 slots) masterreplicates b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1bM: e6eae15e4883cd408fe889e85565a38f6e030de7 127.0.0.1:7005slots: (0 slots) masterreplicates e81eea0243d2976daca5b349ec0bc2d109ac81d0[OK] All nodes agree about slots configuration.>>> Check for open slots...>>> Check slots coverage...[OK] All 16384 slots covered.
当出现:Can I set the above configuration? (type 'yes' to accept):
的时候,输入yes
.然后他就会自动的通知各个节点,自发组成集群.
到此,redis集群的官方方案就部署好了.
如果安装过程中出现:/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/core_ext/kernel_require.rb:55:in require': cannot load such file -- redis (LoadError)
.那是ruby缺少redis
的依赖.
在控制台输入gem install redis
即可.
codis集群方案的部署
Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别.上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务.
Codis 由四部分组成:
- Codis Proxy (codis-proxy)
- Codis Manager (codis-config)
- Codis Redis (codis-server)
- ZooKeeper
codis-proxy 是客户端连接的 Redis 代理服务, codis-proxy 本身实现了 Redis 协议, 表现得和一个原生的 Redis 没什么区别 (就像 Twemproxy), 对于一个业务来说, 可以部署多个 codis-proxy, codis-proxy 本身是无状态的.
codis-config 是 Codis 的管理工具, 支持包括, 添加/删除 Redis 节点, 添加/删除 Proxy 节点, 发起数据迁移等操作. codis-config 本身还自带了一个 http server, 会启动一个 dashboard, 用户可以直接在浏览器上观察 Codis 集群的运行状态.
codis-server 是 Codis 项目维护的一个 Redis 分支, 基于 2.8.21 开发, 加入了 slot 的支持和原子的数据迁移指令. Codis 上层的 codis-proxy 和 codis-config 只能和这个版本的 Redis 交互才能正常运行.
Codis 依赖 ZooKeeper 来存放数据路由表和 codis-proxy 节点的元信息, codis-config 发起的命令都会通过 ZooKeeper 同步到各个存活的 codis-proxy.
需要注意的是,codis的proxy是无状态的,当一个分片的master挂掉的时候,codis不会自动的将某个slave升级成master.不过他提供了一个解决方案:codis-ha.这是一个通过codis开放的api实现自动切换主从的工具。该工具会在检测到master挂掉的时候将其下线并选择其中一个slave提升为master继续提供服务。但是只是在每一个分片是一主一从两台的情况下才有效.否则,该分片内其他slave实例是不会自动改变状态的,这些slave仍将试图从旧的master上同步数据,因而会导致分片内新的master和其他slave之间的数据不一致。因为redis的slave of命令切换master时会丢弃slave上的全部数据,从新master完整同步,会消耗master资源。因此建议在知情的情况下手动操作。使用 codis-config server add <group_id> <redis_addr> slave
命令刷新这些节点的状态即可。codis-ha不会自动刷新其他slave的状态。
-
由于codis是由GO语言写的,因此,要运行codis首先需要的就是安装
go
语言环境.在类
unix
机器上都有现成的安装包可以安装.
比如Macos
在终端中输入brew install go
即可. (安装过程中需要访问https://go.googlesource.com/tools.git
,方法你懂的) -
设置GO的workspace路径.在
~/.bash_profiler
下增加export GOPATH="/Volumes/WORKSPACE/openSourceWorkspace/GoWorkspace"
注意$GOPATH是本机所有go项目(包括项目依赖的第三方库)的所在目录,而非单纯codis的所在目录。 - 执行下面的命令下载codis源码并编译.它会自动的下载源码以及依赖包.
1
2
3
4
|
go get -u -d github.com/wandoulabs/codis
cd $GOPATH/src/github.com/wandoulabs/codis
./bootstrap.sh
make gotest
|
-
执行完命令后,会在codis/bin下生成三个文件
codis-config
codis-proxy
codis-server
.assets
文件夹里面存放的是dashboard
服务所需要的静态文件. -
默认情况下 这些命令都会读取
config.ini
里面的配置.配置很简单,按照里面的注释改就可以了. - 启动ZK.
- 启动
dashboard
,执行./bin/codis-config dashboard
,就会启动dashboard - 初始化 slots , 执行
./bin/codis-config slot init
,该命令会在zookeeper上创建slot相关信息. - 开始启动
Codis Redis
.执行./bin/codis-server
,就启动的.这个就和官网的一样. 配置文件也可以跟在后面.比如:./bin/codis-server redis.config
-
添加 Redis Server Group , 每一个 Server Group 作为一个 Redis 服务器组存在, 只允许有一个 master, 可以有多个 slave, group id 仅支持大于等于1的整数.这个步骤可以通过
dashboard
来增加,也可以使用命令行.比如:1234bin/codis-config server add 1 localhost:6379 masterbin/codis-config server add 1 localhost:6380 slavebin/codis-config server add 2 localhost:6479 masterbin/codis-config server add 2 localhost:6480 slave这样就增加了4个节点,并且分成两片,每片一主一从.
-
设置 server group 服务的 slot 范围 Codis 采用 Pre-sharding 的技术来实现数据的分片, 默认分成 1024 个 slots (0-1023), 对于每个key来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024 每一个 slot 都会有一个且必须有一个特定的 server group id 来表示这个 slot 的数据由哪个 server group 来提供.
12$ bin/codis-config slot range-set 0 511 1 online$ bin/codis-config slot range-set 512 1023 2 online同样,可以在界面上进行操作
-
启动 codis-proxy (注意,如果zk的session时间设置太短的话,proxy可能启动不起来.)
1bin/codis-proxy -c config.ini -L ./log/proxy.log --cpu=8 --addr=0.0.0.0:19000 --http-addr=0.0.0.0:11000
到此就集群完毕.
-
但是为了能在master当机的时候自动的升级slave,这就需要codis-ha了.这个同样是
go
语言写的.所以直接在终端中输入:1234go get github.com/ngaut/codis-hacd codis-hago buildcodis-ha --codis-config=localhost:18087 --productName=test就搞定了,他会自动的监控dashboard,然后发现节点不正确后,调用restful的接口,提升slave为master
接下来就是客户端的调用了.
由于codis依赖了zk作为服务的发现方.因此,整个集群的节点状态都是在zk中有的.
它自身也提供了一个JAVA的实现,叫jodis.通过这个,就可以关联jedis Driver
.并且获取当前可用的codis-server
.
不过目前这个jodis还没有提供与spring的集成.这个需要我们自己来开发.