Redis集群部署

Redis作为目前最常用的内存NOSQL数据库,使用的场景非常的广泛.但是在3.0以前官方一直都是没有集群的方案的.相当于是一个单机内存数据库.为了其高可用,集群的部署是非常有必要的.因此,各种第三方厂商都推出了自己的集群方案.使用的最多的就是豌豆荚开源的Codis,Twitter开源的Twemproxy,NetflixDynamo.

目前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集群部署的方案.

官方集群方案的部署

虽然目前我们不会使用官方的方案,但是不排除以后会使用.毕竟是官方的东西,应该要靠谱些.

  1. 下载 并编译最新的Redis. 由于Redis是C写的.并且网上只有源码.因此,最好是直接下载源码到类unix机器上,然后调用 sudo make 命令自动的编译出可运行的版本.

  2. 最少集群6个节点,因此在我的磁盘上建立一个redis-test文件夹.然后建立6个子文件夹,作为6个节点.

    1
    2
    3
    mkdir redis-test
    cd redis-test
    mkdir 7000 7001 7002 7003 7004 7005
  3. 然后拷贝6分编译好的redis到各个目录中去.

  4. 修改redis.conf文件.

    1
    2
    3
    4
    5
    6
    7
    port 7005
    pidfile /Volumes/EXCHANGE/redis-test/7000/redis.pid
    cluster-enabled yes
    # 和端口对应
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    appendonly yes
  5. 修改好后,进入每一个的src文件夹,执行./redis-server ../redis.conf 启动6个服务.

  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指的是从服务的数量.

    执行了这句后,会显示出集群的分布情况.

    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
    >>> Creating cluster
    Connecting to node 127.0.0.1:7000: OK
    Connecting to node 127.0.0.1:7001: OK
    Connecting to node 127.0.0.1:7002: OK
    Connecting to node 127.0.0.1:7003: OK
    Connecting to node 127.0.0.1:7004: OK
    Connecting to node 127.0.0.1:7005: OK
    >>> Performing hash slots allocation on 6 nodes...
    Using 3 masters:
    127.0.0.1:7000
    127.0.0.1:7001
    127.0.0.1:7002
    Adding replica 127.0.0.1:7003 to 127.0.0.1:7000
    Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
    Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
    M: d450eaf8b29ccc57c5ab851868a0e23b41d0f50c 127.0.0.1:7000
    slots:0-5460 (5461 slots) master
    M: b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1b 127.0.0.1:7001
    slots:5461-10922 (5462 slots) master
    M: e81eea0243d2976daca5b349ec0bc2d109ac81d0 127.0.0.1:7002
    slots:10923-16383 (5461 slots) master
    S: 52a634c534ca5db005dbc31494676e08454ebfa4 127.0.0.1:7003
    replicates d450eaf8b29ccc57c5ab851868a0e23b41d0f50c
    S: 270c10d1f3b85438b74b8fd5c9d91a3e0ce8a0da 127.0.0.1:7004
    replicates b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1b
    S: e6eae15e4883cd408fe889e85565a38f6e030de7 127.0.0.1:7005
    replicates e81eea0243d2976daca5b349ec0bc2d109ac81d0
    Can 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 cluster
    Waiting for the cluster to join...
    >>> Performing Cluster Check (using node 127.0.0.1:7000)
    M: d450eaf8b29ccc57c5ab851868a0e23b41d0f50c 127.0.0.1:7000
    slots:0-5460 (5461 slots) master
    M: b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1b 127.0.0.1:7001
    slots:5461-10922 (5462 slots) master
    M: e81eea0243d2976daca5b349ec0bc2d109ac81d0 127.0.0.1:7002
    slots:10923-16383 (5461 slots) master
    M: 52a634c534ca5db005dbc31494676e08454ebfa4 127.0.0.1:7003
    slots: (0 slots) master
    replicates d450eaf8b29ccc57c5ab851868a0e23b41d0f50c
    M: 270c10d1f3b85438b74b8fd5c9d91a3e0ce8a0da 127.0.0.1:7004
    slots: (0 slots) master
    replicates b4f505a8bfbc58dcd65c0a106f284ae1fe3efe1b
    M: e6eae15e4883cd408fe889e85565a38f6e030de7 127.0.0.1:7005
    slots: (0 slots) master
    replicates 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的状态。


  1. 由于codis是由GO语言写的,因此,要运行codis首先需要的就是安装go语言环境.

    在类unix机器上都有现成的安装包可以安装.
    比如Macos 在终端中输入brew install go 即可. (安装过程中需要访问https://go.googlesource.com/tools.git,方法你懂的)

  2. 设置GO的workspace路径.在~/.bash_profiler下增加export GOPATH="/Volumes/WORKSPACE/openSourceWorkspace/GoWorkspace" 注意$GOPATH是本机所有go项目(包括项目依赖的第三方库)的所在目录,而非单纯codis的所在目录。

  3. 执行下面的命令下载codis源码并编译.它会自动的下载源码以及依赖包.
1
2
3
4
go get -u -d github.com/wandoulabs/codis
cd $GOPATH/src/github.com/wandoulabs/codis
./bootstrap.sh
make gotest
  1. 执行完命令后,会在codis/bin下生成三个文件codis-config codis-proxy codis-serverassets文件夹里面存放的是dashboard服务所需要的静态文件.

  2. 默认情况下 这些命令都会读取config.ini里面的配置.配置很简单,按照里面的注释改就可以了.

  3. 启动ZK.
  4. 启动dashboard,执行 ./bin/codis-config dashboard,就会启动dashboard
  5. 初始化 slots , 执行 ./bin/codis-config slot init,该命令会在zookeeper上创建slot相关信息.
  6. 开始启动Codis Redis.执行./bin/codis-server,就启动的.这个就和官网的一样. 配置文件也可以跟在后面.比如:./bin/codis-server redis.config
  7. 添加 Redis Server Group , 每一个 Server Group 作为一个 Redis 服务器组存在, 只允许有一个 master, 可以有多个 slave, group id 仅支持大于等于1的整数.这个步骤可以通过dashboard来增加,也可以使用命令行.比如:

    1
    2
    3
    4
    bin/codis-config server add 1 localhost:6379 master
    bin/codis-config server add 1 localhost:6380 slave
    bin/codis-config server add 2 localhost:6479 master
    bin/codis-config server add 2 localhost:6480 slave

    这样就增加了4个节点,并且分成两片,每片一主一从.

  8. 设置 server group 服务的 slot 范围 Codis 采用 Pre-sharding 的技术来实现数据的分片, 默认分成 1024 个 slots (0-1023), 对于每个key来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024 每一个 slot 都会有一个且必须有一个特定的 server group id 来表示这个 slot 的数据由哪个 server group 来提供.

    1
    2
    $ bin/codis-config slot range-set 0 511 1 online
    $ bin/codis-config slot range-set 512 1023 2 online

    同样,可以在界面上进行操作

  9. 启动 codis-proxy (注意,如果zk的session时间设置太短的话,proxy可能启动不起来.)

    1
    bin/codis-proxy -c config.ini -L ./log/proxy.log --cpu=8 --addr=0.0.0.0:19000 --http-addr=0.0.0.0:11000
到此就集群完毕.
  1. 但是为了能在master当机的时候自动的升级slave,这就需要codis-ha了.这个同样是go语言写的.所以直接在终端中输入:

    1
    2
    3
    4
    go get github.com/ngaut/codis-ha
    cd codis-ha
    go build
    codis-ha --codis-config=localhost:18087 --productName=test

    就搞定了,他会自动的监控dashboard,然后发现节点不正确后,调用restful的接口,提升slave为master


接下来就是客户端的调用了.
由于codis依赖了zk作为服务的发现方.因此,整个集群的节点状态都是在zk中有的.

它自身也提供了一个JAVA的实现,叫jodis.通过这个,就可以关联jedis Driver.并且获取当前可用的codis-server.
不过目前这个jodis还没有提供与spring的集成.这个需要我们自己来开发.

posted @ 2018-03-08 16:02  fenghuo  阅读(149)  评论(0编辑  收藏  举报