一、redis(缓存类数据库)群集:当遇到单机、内存、并发、流量瓶颈时,可以采用Cluster架构方案达到负载均衡目的。

Redis Cluster之前的分布式方案有两种:
        1)客户端分区方案: 优点分区逻辑可控,缺点是需要自己处理数据路由,高可用和故障转移等。
        2)代理方案: 优点是简化客户端分布式逻辑和升级维护便利,缺点加重架构部署和性能消耗。

二、数据分布
    分布式数据库首先要解决把整个数据库集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上
    每个节点负责整体数据的一个子集,需要关注的是数据分片规则,Redis Cluster采用哈希分片规则。

redis的优点:速度快、单线程架构(用户量少,访问量多)、支持多种数据结构、天然的计数器、健过期功能、消息队列、数据持久化(aof、rdb)、自带高可用架构

redis与memcache的区别: memcache多线程架构(用户量多,访问量少)、不支持持久化、自己没有集群功能(第三方的工具)

环境描述:一台机器启动六个节点,3个主节点,3个从节点。

redis1:192.168.1.61(6380/6381)                           redis2:192.168.1.62(6380/6381)   

                                 redis3:192.168.1.63(6380/6381)

关闭防火墙(systemctl stop firewalld)  selinux (setenforce 0)

1.目录规划

/data/redis_cluster/redis_{PORT}/redis_{PORT}.rdb        # redis数据目录
/root/scripts/redis_shell.sh               # redis运维脚本
# 创建redis安装目录
mkdir -p /opt/redis_cluster/redis_{6380,6381}/{conf,logs,pid}    
mkdir -p /data/redis_cluster/redis_{6380,6381}

2.安装redis(redis1上操作)

#下载redis安装包
wget http://download.redis.io/releases/redis-5.0.7.tar.gz    
#解压到/opt/redis_cluster/
tar xf redis-5.0.7.tar.gz -C /opt/redis_cluster/        
#做个软连接
ln -s /opt/redis_cluster/redis-5.0.7  /opt/redis_cluster/redis
#切换目录安装redis
cd /opt/redis_cluster/redis
make && make install

3.redis1的配置文件

redis1上操作
6380的配置文件:
cat >/opt/redis_cluster/redis_6380/conf/redis_6380.conf<<EOF
bind 192.168.1.61
port 6380
daemonize yes
pidfile "/opt/redis_cluster/redis_6380/pid/redis_6380.pid"
logfile "/opt/redis_cluster/redis_6380/logs/redis_6380.log"
dbfilename "redis_6380.rdb"
dir "/data/redis_cluster/redis_6380/"
cluster-enabled yes
cluster-config-file nodes_6380.conf
cluster-node-timeout 15000
EOF
cd /opt/redis_cluster/
#将6380节点的文件复制一份到6381节点 
cp redis_6380/conf/redis_6380.conf redis_6381/conf/redis_6381.conf
#查找替换6381
sed -i 's#6380#6381#g' redis_6381/conf/redis_6381.conf 
#启动节点
redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf

#复制redis1的安装和数据目录到redis2、redis3
scp -rp /opt/redis_cluster/ root@192.168.1.62:/opt
scp -rp /opt/redis_cluster/ root@192.168.1.63:/opt

2.配置redis2

redis2操作:
cd /opt/redis_cluster/redis
make install 
find /opt/redis_cluster/redis_638* -type f -name "*.conf"|xargs sed -i "s#61#62#g"
mkdir -p /data/redis_cluster/redis_{6380,6381}
redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf

3.配置redis3

redis3操作
cd /opt/redis_cluster/redis
make install
find /opt/redis_cluster/redis_638* -type f -name "*.conf"|xargs sed -i "s#61#63#g"
mkdir -p /data/redis_cluster/redis_{6380,6381}
redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf

手动配置:

1.手动配置节点发现:

#任何一个节点上执行都可以,手动发现其余的节点
redis-cli -h 192.168.1.61 -p 6380
192.168.1.61:6380> CLUSTER MEET 192.168.1.61 6381
OK
192.168.1.61:6380> CLUSTER MEET 192.168.1.62 6380
OK
192.168.1.61:6380> CLUSTER MEET 192.168.1.63 6380
OK
192.168.1.61:6380> CLUSTER MEET 192.168.1.62 6381
OK
192.168.1.61:6380> CLUSTER MEET 192.168.1.63 6381
OK
#查看群集所有节点,初始状态都是master
192.168.1.61:6380> CLUSTER NODES

2.Redis Cluster手动分配槽位

#共16384个槽位,平均分成三份5461,0也算一个槽位,使用的是gossip(流言)协议
#群集之间互相使用流言协议得知对方存在:ping、pong、meet、fail
redis-cli -h 192.168.1.61 -p 6380 cluster addslots {0..5460}
redis-cli -h 192.168.1.62 -p 6380 cluster addslots {5461..10922}
redis-cli -h 192.168.1.63 -p 6380 cluster addslots {10923..16383}
#分配完所有槽位之后我们再查看一下集群的节点状态和集群状态,可以看到三个节点都分配了槽位,而且集群的状态是OK的

3手动配置集群高可用

分配三个主的从节点:
  redis-cli -h 192.168.1.61 -p 6381 CLUSTER REPLICATE 5cb6895305520e6a0aa4198a6ea5f2c087530b41  (第二台主机6380的id)
 redis-cli -h 192.168.1.62 -p 6381 CLUSTER REPLICATE aa9da67a594dfb357195f12ca4c44001804ee470  (第三台主机6380的id)
 redis-cli -h 192.168.1.63 -p 6381 CLUSTER REPLICATE d03cb38d612802aead8f727b1726a3359c241818	(第一台主机6380的id)

4.Redis Cluster测试集群

编辑一个简单的脚本:
 cat > input_key.sh <<EOF
#!/bin/bash
for i in $(seq 1 10000)
do
    redis-cli -c -h 192.168.1.61 -p 6380 set k_${i} v_${i} && echo "set k_${i} is ok"
done
EOF

#执行脚本
sh input_key.sh
写入后我们同样使用-c选项来读取刚才插入的键值,然后查看下redis会不会帮我们路由到正确的节点上
-c:支持内部路由功能
[root@redis1 ~]# redis-cli -c -h 192.168.1.61 -p 6380 
redis1:6380> get k_1
"v_1"
redis1:6380> get k_100
-> Redirected to slot [5541] located at 192.168.1.62:6380
"v_100"
192.168.1.62:6380> get k_1000
-> Redirected to slot [79] located at 192.168.1.61:6380
"v_1000"
192.168.1.61:6380>

5.模拟故障转移

模拟redis2故障:
	redis-cli -h 192.168.1.62 -p 6380 shutdown
	redis-cli -h 192.168.1.62 -p 6381 shutdown
查看群集节点状态:
	redis-cli -h 192.168.1.61 -p 6380 
	cluster nodes
再启动redis2的服务:
	redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
	redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf

重回master:
	redis-cli  -h 192.168.1.62 -p  6380 CLUSTER FAILOVER

自动部署:

前提准备:
停掉所有的节点,然后清空数据,恢复成一个全新的集群,所有机器执行命令
pkill redis
rm -rf /data/redis_cluster/redis_6380/*
rm -rf /data/redis_cluster/redis_6381/*

全部清空之后启动所有的节点,所有机器执行
redis-server /opt/redis_cluster/redis_6380/conf/redis_6380.conf
redis-server /opt/redis_cluster/redis_6381/conf/redis_6381.conf

1.使用工具自动搭建部署Redis Cluster

创建群集:
redis-cli --cluster create --cluster-replicas 1 192.168.1.61:6380 192.168.1.62:6380 192.168.1.63:6380 192.168.1.61:6381 192.168.1.62:6381 192.168.1.62:6381 
检查完整性:
redis-cli --cluster check 192.168.1.61:6380

2.工具扩容节点

1)准备新节点
2)加入集群
3)迁移槽和数据

在redis1上创建2个新节点
mkdir -p /opt/redis_cluster/redis_{6390,6391}/{conf,logs,pid}
mkdir -p /data/redis_cluster/redis_{6390,6391}
cd /opt/redis_cluster/
cp redis_6380/conf/redis_6380.conf redis_6390/conf/redis_6390.conf
cp redis_6380/conf/redis_6380.conf redis_6391/conf/redis_6391.conf
sed -i 's#6380#6390#g' redis_6390/conf/redis_6390.conf
sed -i 's#6380#6391#g' redis_6391/conf/redis_6391.conf
启动节点
redis-server /opt/redis_cluster/redis_6390/conf/redis_6390.conf 
redis-server /opt/redis_cluster/redis_6391/conf/redis_6391.conf
发现节点
redis-cli -c -h 192.168.1.61 -p 6380 cluster meet 192.168.1.61 6390
redis-cli -c -h 192.168.1.61 -p 6380 cluster meet 192.168.1.61 6391

3.分配槽位

分配slots
redis-cli --cluster reshard 192.168.1.61:6390 
输入分配的slots数:4096
再输入6390的id号
再输入all

配置6391 slave 6390:
redis-cli -h 192.168.1.61 -p 6391 cluster replicate 18daa915f146787e9e670293f283d5f5f008e6ec

工具收缩节

移除下线节点的槽位:
redis_cli --cluster reshard 192.168.1.61:6390
案例中分三次移除 : 分别是
	1365 	给redis1 的6380
	1366 	给redis2 的6380
	1365	给redis3 的6380

移除后遗忘下线节点:
redis-cli -c -h 192.168.1.61 -p 6380 cluster forget  被遗忘的节点id
redis-cli -c -h 192.168.1.61 -p 6380 cluster forget   6309/6391节点的id

常用命令

Redis集群常用命令

集群(cluster)
	CLUSTER INFO 打印集群的信息
	CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。 
节点(node)
	CLUSTER MEET <ip> <port> 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的一份子。
	CLUSTER FORGET <node_id> 从集群中移除 node_id 指定的节点。
	CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点。
	CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。 
槽(slot)
	CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多个槽(slot)指派(assign)给当前节点。
	CLUSTER DELSLOTS <slot> [slot ...] 移除一个或多个槽对当前节点的指派。
	CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。
	CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。
	CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中。
	CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。
	CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)。 
键 (key)
	CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。
	CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的键值对数量。CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键。

企业redis运维脚本

Redis运维工具
1.运维脚本
[root@redis1 ~]# cat redis_shell.sh 
#!/bin/bash

USAG(){
    echo "sh $0 {start|stop|restart|login|ps|tail} PORT"
}
if [ "$#" = 1 ]
then
    REDIS_PORT='6379'
elif 
    [ "$#" = 2 -a -z "$(echo "$2"|sed 's#[0-9]##g')" ]
then
    REDIS_PORT="$2"
else
    USAG
    exit 0
fi

REDIS_IP=$(hostname -I|awk '{print $1}')
PATH_DIR=/opt/redis_cluster/redis_${REDIS_PORT}/
PATH_CONF=/opt/redis_cluster/redis_${REDIS_PORT}/conf/redis_${REDIS_PORT}.conf
PATH_LOG=/opt/redis_cluster/redis_${REDIS_PORT}/logs/redis_${REDIS_PORT}.log

CMD_START(){
    redis-server ${PATH_CONF}
}

CMD_SHUTDOWN(){
    redis-cli -c -h ${REDIS_IP} -p ${REDIS_PORT} shutdown
}

CMD_LOGIN(){
    redis-cli -c -h ${REDIS_IP} -p ${REDIS_PORT}
}

CMD_PS(){
    ps -ef|grep redis
}

CMD_TAIL(){
    tail -f ${PATH_LOG}
}

case $1 in
    start)
        CMD_START
        CMD_PS
        ;;
    stop)
        CMD_SHUTDOWN
        CMD_PS
        ;;
    restart)
        CMD_START
        CMD_SHUTDOWN
        CMD_PS
        ;;
    login)
        CMD_LOGIN
        ;;
    ps)
        CMD_PS
        ;;
    tail)
        CMD_TAIL
        ;;
    *)
        USAG
esac

2.数据导入导出工具
需求背景
刚切换到redis集群的时候肯定会面临数据导入的问题,所以这里推荐使用redis-migrate-tool工具来导入单节点数据到集群里
官方地址:
http://www.oschina.net/p/redis-migrate-tool
安装工具

cd /opt/redis_cluster/
git clone https://github.com/vipshop/redis-migrate-tool.git
cd redis-migrate-tool/
autoreconf -fvi
./configure
make && make install 
创建配置文件

[root@redis1 ~]# cat redis_6379_to_6380.conf    
[source]
type: single
servers:
- 192.168.1.61:6379
 
[target]
type: redis cluster
servers:
- 192.168.1.61:6380 
 
[common]
listen: 0.0.0.0:8888
source_safe: true
生成测试数据

[root@redis1 ~]# cat input_key.sh 
#!/bin/bash
for i in $(seq 1 1000)
do
    redis-cli -c -h redis1 -p 6379 set k_${i} v_${i} && echo "set k_${i} is ok"
done
执行导入命令

[root@redis1 ~]# redis-migrate-tool -c redis_6379_to_6380.conf 
数据校验

[root@redis1 ~]# redis-migrate-tool -c redis_6379_to_6380.conf -C redis_check

3.分析键值大小
需求背景
redis的内存使用太大键值太多,不知道哪些键值占用的容量比较大,而且在线分析会影响性能.
安装工具

yum install python-pip gcc python-devel 
cd /opt/
git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools
python setup.py install
使用方法

cd /data/redis_cluster/redis_6380/
rdb -c memory redis_6380.rdb -f redis_6380.rdb.csv
分析rdb并导出

awk -F ',' '{print $4,$2,$3,$1}' redis_6380.rdb.csv |sort  > 6380.txt

4.监控过期键
需求背景
因为开发重复提交,导致电商网站优惠卷过期时间失效
问题分析
如果一个键已经设置了过期时间,这时候在set这个键,过期时间就会取消
解决思路
如何在不影响机器性能的前提下批量获取需要监控键过期时间
1.Keys * 查出来匹配的键名。然后循环读取ttl时间
2.scan * 范围查询键名。然后循环读取ttl时间
Keys 重操作,会影响服务器性能,除非是不提供服务的从节点
Scan 负担小,但是需要去多次才能取完,需要写脚本
脚本内容:


cat 01get_key.sh 
#!/bin/bash
key_num=0
> key_name.log
for line in $(cat key_list.txt)
do
    while true
    do
        scan_num=$(redis-cli -h 192.168.47.75 -p 6380 SCAN ${key_num} match ${line}\* count 1000|awk 'NR==1{print $0}')
        key_name=$(redis-cli -h 192.168.47.75 -p 6380 SCAN ${key_num} match ${line}\* count 1000|awk 'NR>1{print $0}')
        echo ${key_name}|xargs -n 1 >> key_name.log
        ((key_num=scan_num))
        if [ ${key_num} == 0 ]
           then
           break
        fi
    done
done