|
一、关于redis cluster
集群技术是构建高性能网站架构的重要手段,在网站承受高并发访问压力的同时,还需要从海量数据中查询出满足条件的数据,并快速响应,我们必然想到的是将数据进行切片,把数据根据某种规则放入多个不同的服务器节点,来降低单节点服务器的压力。
我部署的redis集群主要是利用切片技术来组建的集群。
集群要实现的目的是要将不同的 key 分散放置到不同的 redis 节点,这里我们需要一个规则或者算法,通常的做法是获取 key 的哈希值,然后根据节点数来求模,但这种做法有其明显的弊端,当我们需要增加或减少一个节点时,会造成大量的 key 无法命中,这种比例是相当高的,所以就有人提出了一致性哈希的概念。
Redis 引入另一种哈希槽(hash slot)的概念。
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
使用哈希槽的好处就在于可以方便的添加或移除节点。
当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;
1,redis cluster的现状
目前redis支持的cluster特性:
1):节点自动发现
2):slave->master 选举,集群容错
3):Hot resharding:在线分片
4):进群管理:cluster xxx
5):基于配置(nodes-port.conf)的集群管理
6):ASK 转向/MOVED 转向机制.
2,redis cluster 架构
架构细节:
(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护nodeslotvalue
3,redis-cluster选举:容错
(1)领着选举过程是集群中所有master参与,如果半数以上master节点与master节点通信超过(cluster-node-timeout),认为当前master节点挂掉.
(2):什么时候整个集群不可用(cluster_state:fail),当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误
a:如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成进群的slot映射[0-16383]不完成时进入fail状态.
b:如果进群超过半数以上master挂掉,无论是否有slave集群进入fail状态.
二、redis cluster搭建
1,安装redis-cluster依赖
(1)确保系统安装zlib,否则gem install会报(no such file to load -- zlib)
(2)安装ruby
yum install –y ruby ruby-rdoc ruby-rvm
yum erase ruby ruby-libs ruby-mode ruby-rdoc ruby-irb ruby-ri ruby-docs
(3)安装rubygem
yum install rubygems 或
- # rubygems-1.8.16.tgz
- cd /path/gem
- sudo ruby setup.rb
- sudo cp bin/gem /usr/local/bin
(4)安装gem-redis
gem install redis --version 3.0.0
#由于源的原因,可能下载失败,就手动下载下来安装
#download地址:http://rubygems.org/gems/redis/versions/3.0.0
gem install -l /data/soft/redis-3.0.0.gem
2,安装redis-cluster
- cd /path/redis
- make
- sudo cp redis-server /usr/local/bin
- sudo cp redis-cli /usr/local/bin
- sudo cp redis-trib.rb /usr/local/bin
3,配置redis-cluster
(1) 配置redis文件结构
使用包含(include)把通用配置和特殊配置分离,方便维护.
(2)redis通用配置
============================================================================
#GENERAL
daemonize yes
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
databases 16
dir /data/webserver/redis/data
slave-serve-stale-data yes
#slave只读
slave-read-only yes
#not use default
repl-disable-tcp-nodelay yes
slave-priority 100
#打开aof持久化
appendonly yes
#每秒一次aof写
appendfsync everysec
#关闭在aof rewrite的时候对新的写操作进行fsync
no-appendfsync-on-rewrite yes
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
#打开redis集群
cluster-enabled yes
#节点互连超时的阀值
cluster-node-timeout 15000
cluster-migration-barrier 1
slowlog-log-slower-than 10000
slowlog-max-len 128
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
================================================================
(3)redis特殊配置
================================================================
#包含通用配置
include /data/webserver/redis/conf/redis-common.conf
#日志PID
logfile /data/webserver/redis/logs/redis_6380.log
pidfile /data/webserver/redis/pids/redis_6380.pid
#监听tcp端口
port 6380
bind 10.161.180.111
#最大可用内存
maxmemory 2G
#内存耗尽时采用的淘汰策略:
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations
maxmemory-policy allkeys-lru
#aof存储文件
appendfilename "appendonly-6380.aof"
#rdb文件,只用于动态添加slave过程
dbfilename dump-6380.rdb
#cluster配置文件(启动自动生成)
cluster-config-file nodes-6380.conf
#部署在同一机器的redis实例,把auto-aof-rewrite搓开,防止瞬间fork所有redis进程做rewrite,占用大量内存
auto-aof-rewrite-percentage 80-100
================================================================,
因为我们要启动多个 redis 实例,虽然可以直接通过命令行来启动,但始终是不怎么方便的,所以我们按端口号分别建立实例目录,在实例目录里面创建redis特殊配置文件
(4)初始化并构建集群
分别启动实例,./redis-server ./6380/redis-6380.conf
使用自带的ruby工具(redis-trib.rb)构建集群 redis-trib.rb create --replicas 0 10.161.180.111:6380 10.161.180.111:6381 10.161.180.111:6382
需要注意的是执行 redis-trib.rb 命令需要 ruby 的支持,如果你没有安装可以先到 https://rubygems.org/gems/redis 下载,然后离线安装。
sudo gem install redis-3.0.7.gem --local
检查集群状态
- #redis-trib.rb的check子命令构建
- #ip:port可以是集群的任意节点
- redis-trib.rb check 10.161.180.111 :6380
最后输出如下信息,没有任何警告或错误,表示集群启动成功并处于ok状态
- [OK] All nodes agree about slots configuration.
- >>> Check for open slots...
- >>> Check slots coverage...
- [OK] All 16384 slots covered.
下面我们用 redis 自带的客户端测试一下:
可以看到,虽然我们第一次连接的是6380端口,当我们去获取 testkey001 的时候,redis cluster 自动帮我们重定向到 6382 。
当我们在 6382设置 testkey002 时,redis cluster 又重定向到 6380 。总的来说, redis 集群部署起来还是非常方便的.
(5)添加新master节点
redis-trib.rb add-node 10.168.228.78:6383 10.161.180.111:6380
add-node 将一个节点添加到集群里面, 第一个是新节点ip:port, 第二个是任意一个已存在节点ip:port
检查一下新节点是否已经加入:
node:新节点没有包含任何数据, 因为它没有包含任何slot。新加入的节点是一个主节点, 当集群需要将某个从节点升级为新的主节点时, 这个新节点不会被选中。
因此需要为新节点分配slot
- redis-trib.rb reshard 10.161.180.111:6383
- #根据提示选择要迁移的slot数量(ps:这里选择500)
- How many slots do you want to move (from 1 to 16384)? 500
- #选择要接受这些slot的node-id
- What is the receiving node ID? f51e26b5d5ff74f85341f06f28f125b7254e61bf
- #选择slot来源:
- #all表示从所有的master重新分配,
- #或者数据要提取slot的master节点id,最后用done结束
- Please enter all the source node IDs.
- Type 'all' to use all the nodes as source nodes for the hash slots.
- Type 'done' once you entered all the source nodes IDs.
- Source node #1:all
- #打印被移动的slot后,输入yes开始移动slot以及对应的数据.
- #Do you want to proceed with the proposed reshard plan (yes/no)? yes
- #结束
(6)添加新的slave节点
redis-cli连接上新节点shell,输入命令:cluster replicate 对应master的node-id
或者执行redis-trib.rb命令,如下:
redis-trib.rb add-node --slave 10.168.228.78:7382 10.161.180.111:6382
(7)在线reshard 数据
对于负载/数据均匀的情况,可以在线reshard slot来解决,方法与添加新master的reshard一样,只是需要reshard的master节点是老节点.
redis-trib.rb reshard 10.161.180.111:6380
(8)删除节点
删除一个slave节点:
- #redis-trib del-node ip:port ''
- redis-trib.rb del-node 10.168.228.78:7386 'c7ee2fca17cb79fe3c9822ced1d4f6c5e169e378'
删除一个master节点:
删除master节点之前首先要使用reshard移除master的全部slot,然后再删除当前节点(目前只能把被删除master的slot迁移到一个节点上)
- #把10.161.180.111:6383当前master迁移到10.161.180.111:6380上
- redis-trib.rb reshard 10.161.180.111:6383
- #根据提示选择要迁移的slot数量(ps:这里选择500)
- How many slots do you want to move (from 1 to 16384)? 500(被删除master的所有slot数量)
- #选择要接受这些slot的node-id(10.161.180.111:6380)
- What is the receiving node ID? c4a31c852f81686f6ed8bcd6d1b13accdc947fd2 (ps:10.161.180.111:6380的node-id)
- Please enter all the source node IDs.
- Type 'all' to use all the nodes as source nodes for the hash slots.
- Type 'done' once you entered all the source nodes IDs.
- Source node #1:f51e26b5d5ff74f85341f06f28f125b7254e61bf(被删除master的node-id)
- Source node #2:done
- #打印被移动的slot后,输入yes开始移动slot以及对应的数据.
- #Do you want to proceed with the proposed reshard plan (yes/no)? yes
删除空master节点:
redis-trib.rb del-node 10.161.180.111:6383 'f51e26b5d5ff74f85341f06f28f125b7254e61bf'
目前c#客户端还不能很好的支持 redis 集群,下一篇将介绍如何使用代理来实现 redis 集群。
|
|
|