redis安装和部署

redis安装和部署

简介

Redis是我们目前大规模使用的缓存中间件,由于它强大高效而又便捷的功能,得到了广泛的使用。单节点的Redis已经就达到了很高的性能,为了提高可用性我们可以使用Redis集群。

单机安装

1
2
3
4
5
6
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
tar -zxvf redis-5.0.5.tar.gz
cd redis-5.0.5
make
make test
make install

启动和关闭

1
redis-server &

集群安装

集群的概念

介绍

Redis集群是一个可以在多个Redis节点之间进行数据共享的设施(installation)。

Redis集群不支持那些需要同时处理多个键的Redis命令, 因为执行这些命令需要在多个Redis节点之间移动数据, 并且在高负载的情况下, 这些命令将降低Redis集群的性能,并导致不可预测的错误。

Redis集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

Redis集群提供了以下两个好处:

  • 将数据自动切分(split)到多个节点的能力。
  • 当集群中的一部分节点失效或者无法进行通讯时, 仍然可以继续处理命令请求的能力。
    数据分片
    Redis集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现:一个Redis集群包含16384个哈希槽(hash slot),数据库中的每个键都属于这16384个哈希槽的其中一个,集群使用公式CRC16(key) % 16384来计算键key属于哪个槽, 其中CRC16(key)语句用于计算键key的CRC16校验和。
    主从复制模型
    为了使得集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下, 仍然可以正常运作, Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。

    Redis一致性保证

    Redis并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作:第一个原因是因为集群是用了异步复制. 写操作过程:
  • 客户端向主节点B写入一条命令.
  • 主节点B向客户端回复命令状态.
  • 主节点将写操作复制给他得从节点B1,B2和B3

主节点对命令的复制工作发生在返回命令回复之后,因为如果每次处理命令请求都需要等待复制操作完成的话,那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。注意:Redis集群可能会在将来提供同步写的方法。 Redis集群另外一种可能会丢失命令的情况是集群出现了网络分区,并且一个客户端与至少包括一个主节点在内的少数实例被孤立。

集群搭建

要让集群正常工作至少需要3个主节点,在这里我们要创建3个redis节点,其中一个为主节点,两个为从节点,对应的redis节点的ip和端口对应关系如下(为了简单演示都在同一台机器上面)

1
2
3
4
5
127.0.0.1:7000

127.0.0.1:7001

127.0.0.1:7002

安装和启动Redis集群

  1. 创建目录

    1
    2
    3
    mkdir redis_cluster
    cd redis_cluster
    mkdir 7000 7001 7002 7003 7004 7005
  2. 复制和修改配置文件
    将redis目录下的配置文件复制到对应端口文件夹下,6个文件夹都要复制一份

    1
    cp ~/Downloads/redis-5.0.5/redis.conf ./7000

    修改配置文件redis.conf,将下面的选项修改

    1
    2
    3
    4
    5
    6
    7
    8
    port 7000
    daemonize yes
    cluster-enabled yes
    cluster-config-file nodes-7000.conf
    cluster-node-timeout 5000
    appendonly yes
    appendfilename "appendonly-7000.aof"
    dbfilename dump-7000.rdb

    两个配置文件安装对应的端口分别修改配置文件

  3. 创建启动脚本
    ./redis_cluster目录下创建一个start_cluster.sh

    1
    2
    3
    4
    #!/bin/bash
    redis-server ./7000/redis.conf
    redis-server ./7001/redis.conf
    redis-server ./7002/redis.conf

    这个时候我们查看一下进程看启动情况

    1
    ps -ef | grep redis
  4. 开启集群

    1
    2
    3
    redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
    127.0.0.1:7002 127.0.0.1:7003
    # --cluster-replicas 1 需要六个节点

Redis集群的使用

连接集群

这里我们使用reids-cli连接集群,使用时加上-c参数,就可以连接到集群
连接7000端口的节点

1
2
3
4
5
6
7
redis-cli -c -p 7000
127.0.0.1:7000> set name redis-test
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get name
"redis-test"
127.0.0.1:7001>

前面的理论知识我们知道了,分配key的时候,它会使用CRC16算法,这里将keyname分配到了7001节点上
Redirected to slot [5798] located at 127.0.0.1:7001
redis cluster采用的方式很直接,它直接跳转到7001节点了,而不是还在自身的7000节点。

好,现在我们连接7002这个从节点进入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
redis-cli -c -p 7002
127.0.0.1:7002> get name
-> Redirected to slot [5798] located at 127.0.0.1:7001
"redis-test"
127.0.0.1:7001> set age 20
-> Redirected to slot [741] located at 127.0.0.1:7000
OK
127.0.0.1:7000> set message helloworld
-> Redirected to slot [11537] located at 127.0.0.1:7002
OK
127.0.0.1:7002> set height 175
-> Redirected to slot [8223] located at 127.0.0.1:7001
OK
127.0.0.1:7001>

我们发现数据会在7000-7002这3个节点之间来回跳转

集群中加入新的主节点

这里在cluster目录下再新建一个7003并修改对应的配置文件,然后启动这个这个redis进程
然后再使用redis-cli的add node指令加入节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
redis-cli --cluster add-node 127.0.0.1:7003 127.0.0.1:7000 [ --cluster-slave]
>>> Adding node 127.0.0.1:7003 to cluster 127.0.0.1:7000
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: b48e9199061ca71057630db3cc0b048c0b0f40a6 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: d46f8b8268cbd5792d3d0663f93ef003373725c9 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: 3c61bb75339a6b45f5e72b8f41df0459faf3a78a 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:7003 to make it join the cluster.
[OK] New node added correctly.

测试集群中的节点挂掉

上面我们建立了一个集群,4个主节点和1个从节点,7000-7003负责存取数据,7004的数据同步到自己的节点上来。我们现在来模拟一下一台matser服务器宕机的情况

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
➜  redis_cluster ps -ef | grep redis
501 2342 1 0 8:45PM ?? 0:08.25 redis-server 127.0.0.1:7000 [cluster]
501 2344 1 0 8:45PM ?? 0:08.21 redis-server 127.0.0.1:7001 [cluster]
501 2346 1 0 8:45PM ?? 0:08.12 redis-server 127.0.0.1:7002 [cluster]
501 2847 1 0 9:08PM ?? 0:03.03 redis-server 127.0.0.1:7003 [cluster]
501 3021 1 0 9:17PM ?? 0:01.05 redis-server 127.0.0.1:7004 [cluster]
501 3133 1478 0 9:22PM ttys001 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn redis
➜ redis_cluster kill -9 2342
➜ redis_cluster redis-cli check 127.0.0.1:7001
Could not connect to Redis at 127.0.0.1:6379: Connection refused
➜ redis_cluster redis-cli --cluster check 127.0.0.1:7001
Could not connect to Redis at 127.0.0.1:7000: Connection refused
127.0.0.1:7001 (d46f8b82...) -> 2 keys | 5462 slots | 0 slaves.
127.0.0.1:7002 (3c61bb75...) -> 1 keys | 5461 slots | 0 slaves.
127.0.0.1:7003 (4384138d...) -> 0 keys | 0 slots | 0 slaves.
127.0.0.1:7004 (dc276668...) -> 1 keys | 5461 slots | 0 slaves.
[OK] 4 keys in 4 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: d46f8b8268cbd5792d3d0663f93ef003373725c9 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: 3c61bb75339a6b45f5e72b8f41df0459faf3a78a 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
M: 4384138dfde44501c49e309231929cb301f8b02d 127.0.0.1:7003
slots: (0 slots) master
M: dc2766687e4c1125aabfd3193062f40840e0f287 127.0.0.1:7004
slots:[0-5460] (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

1
redis-cli --cluster reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes

集群方案对比

Redis集群的解决方案
| 方案 | 贡献者 | 是否官方Redis实现 | 编程语言 |
| — | —– | ————— | ——- |
| Twemproxy | Twitter | 是 | C |
| Redis Cluster | Redis官方 | 是 | C |
| Codis | 豌豆荚 | 否 | Go+C |

基本架构

  1. Twemproxy
    架构图
    数据分片算法
    • 采用一致性哈希算法,以KETAMA为例
  2. Redis Cluster
    架构图
    数据分片算法
    • Key空间被划分为16384个区间,每个Master节点负责一部分区间。
  3. Codis
    架构图
    数据分片算法
    • Key空间被划分为1024个区间, 对于每个key来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024
    • 每一个slot都会有一个特定的server group id来表示这个slot的数据由哪个server group来提供

水平扩容

  1. Twemproxy
    • 不支持运行时水平扩容,需要重启。
    • 根据一致性哈希算法进行数据重新分片。
  2. Redis Cluster
    • 支持通过运行时增加Master节点来水平扩容,提升存储容量,尽力降低命中率波动
    • 存在节点A,需要迁出其中的部分Key区间。新增节点B,接收由节点A迁出的Key区间。
    • 相应Key区间的请求首先还是会发送给A节点:如果请求为新建Key则直接重定向到B节点;如果请求不是新建Key且A节点存储有对应的Key则直接作出响应,否则重定向到B节点
    • 同时Cluster会调用实用工具redis-cli向A节点发送MIGRATE命令,把迁移区间内的所有Key原子的迁移到B节点:同时锁住A、B节点=》在A节点删除Key=》在B节点新建Key=》解锁
    • 运行时动态迁移大尺寸键值可能造成响应时延
  3. Codis
    • 支持运行时水平扩容
    • 底层基于Codis Server特殊实现原子的数据迁移指令

主从备份

主从备份是否必须
  1. Twemproxy
    • 没有数据复制不影响可用节点顶替故障节点
    • 故障发生时,没有数据复制的故障节点的Key会全部丢失
  2. Redis Cluster
    • 没有主从备份的节点一旦故障,将导致整个集群失败:无法写入/读取任何Key;无法进行数据重新分片。
  3. Codis
    • 若出现故障,需要手动配置节点,进行故障转移。
    • 如果没有进行故障转移,只故障节点负责的slots 会失败
主从备份方案

Twemproxy本身不支持出从备份,和Redis Cluster一样,需要引入Redis本身的主备复制功能。

  • 可以设置1主1备或者1主多备
  • 当Slave节点接入Cluster时,就会向配置的Master节点发送SYNC命令。断开重连时,也会再次发送SYNC命令
  • 此后Master将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave服务器在接收到数据库文件数据之后将其存盘并加载到内存中。此后,Master继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。
  • Redis的数据复制是异步的,无论在Master端还是Slave端都不会阻塞。
  • Slave会周期性确认收到的备份数据