【Redis】Redis 集群搭建与管理: 原理、实现与操作


集群 (Cluster)

本章节相关操作不需要记忆!!! 后续⼯作中如果⽤到了能查到即可.

重点理解流程和原理.

广义的集群:只要是多个机器,构成了分布式系统,都是一个集群

狭义的集群:redis 提供的集群模式。这个集群模式主要是解决存储空间不足的问题(拓展存储空间)

基本概念

上述的 哨兵 模式,提⾼了系统的可⽤性。但是真正⽤来存储数据的还是 master 和 slave 节点。所有的数据都需要存储在单个 master 和 slave 节点中。

如果数据量很⼤,接近超出了 master / slave 所在机器的物理内存,就可能出现严重问题了。

虽然硬件价格在不断降低,⼀些中⼤⼚的服务器内存已经可以达到 TB 级别了,但是 1TB 在当前这个 “⼤数据” 时代,俨然不算什么,有的时候我们确实需要更⼤的内存空间来保存更多的数据。

如何获取更⼤的空间? 加机器即可! 所谓 “大数据” 的核⼼,其实就是⼀台机器搞不定了,⽤多台机器来搞.

Redis 的集群就是在上述的思路之下,引⼊多组 Master / Slave ,每⼀组 Master / Slave 存储数据全集的⼀部分,从⽽构成⼀个更⼤的整体,称为 Redis 集群 (Cluster)。

假定整个数据全集是 1 TB,引⼊三组 Master / Slave 来存储。那么每⼀组机器只需要存储整个数据全集的 1/3 即可。

在上述图中,

  • Master1 和 Slave11 和 Slave12 保存的是同样的数据。占总数据的 1/3
  • Master2 和 Slave21 和 Slave22 保存的是同样的数据。占总数据的 1/3
  • Master3 和 Slave31 和 Slave32 保存的是同样的数据。占总数据的 1/3

这三组机器存储的数据都是不同的

每个 Slave 都是对应 Master 的备份(当 Master 挂了,对应的 Slave 会补位成 Master)。

每个红框部分都可以称为是⼀个 分片 (Sharding)

如果全量数据进⼀步增加,只要再增加更多的分⽚,即可解决。

可能有的同学认为,数据量多了,使⽤硬盘来保存不就⾏了?不要忘了硬盘只是存储多了,但是访问速度是⽐内存慢很多的。但是事实上,还是存在很多的应⽤场景,既希望存储较多的数据,⼜希望有⾮常⾼的读写速度。

⽐如搜索引擎

数据分片算法

Redis cluster 的核⼼思路是⽤多组机器来存数据的每个部分。那么接下来的核⼼问题就是,给定⼀个数据 (⼀个具体的 key),那么这个数据应该存储在哪个分⽚上?读取的时候⼜应该去哪个分⽚读取?围绕这个问题,业界有三种⽐较主流的实现⽅式。

哈希求余

借鉴了哈希表的基本思想:借助 hash 函数,把一个 key 映射到整数,再针对数组的长度求余,就可以得到一个数组下标

设有 N 个分⽚,使⽤ [0,N-1] 这样序号进⾏编号。

针对某个给定的 key,先计算 hash 值,再把得到的结果 % N,得到的结果即为分⽚编号。

例如, N 为 3。给定 key 为 hello,对 hello 计算 hash 值(⽐如使⽤ md5 算法,这本质是一个计算 hash 值的算法),得到的结果为 bc4b2a76b9719d91,再把这个结果 % 3,结果为 0,那么就把 hello 这个 key 放到 0 号分⽚上。当然,实际⼯作中涉及到的系统,计算 hash 的⽅式不⼀定是 md5,但是思想是⼀致的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

后续如果要取某个 key 的记录,也是针对 key 计算 hash,再对 N 求余,就可以找到对应的分⽚编号了。

优点:简单⾼效,数据分配均匀。

缺点:⼀旦需要进⾏扩容,N 改变了,原有的映射规则被破坏,就需要让节点之间的数据相互传输,重新排列,以满⾜新的映射规则。此时需要搬运的数据量是⽐较多的,开销较⼤

N 为 3 的时候,[100,120] 这 21 个 hash 值的分布 (此处假定计算出的 hash 值是⼀个简单的整数,⽅便⾁眼观察)

当引⼊⼀个新的分⽚,N 从 3 => 4 时,⼤量的 key 都需要重新映射。(某个key % 3 和 % 4 的结果不⼀样,就映射到不同机器上了)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如上图可以看到,整个扩容⼀共 21 个 key,只有 3 个 key 没有经过搬运,其他的 key 都是搬运过的.

⼀致性哈希算法

为了降低上述的搬运开销,能够更⾼效扩容,业界提出了 “⼀致性哈希算法”

key 映射到分⽚序号的过程不再是简单求余了,⽽是改成以下过程:

第⼀步, 把 0 -> 2^32-1 这个数据空间,映射到⼀个圆环上。数据按照顺时针⽅向增⻓。

第⼆步,假设当前存在三个分⽚,就把分⽚放到圆环的某个位置上。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第三步,假定有⼀个 key,计算得到 hash 值 H,那么这个 key 映射到哪个分⽚呢?规则很简单,就是从 H 所在位置,顺时针往下找,找到的第⼀个分⽚,即为该 key 所从属的分⽚。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这就相当于,N 个分⽚的位置,把整个圆环分成了 N 个管辖区间。Key 的 hash 值落在某个区间内, 就归对应区间管理。

上述⼀致性哈希算法的过程,类似于去⾼铁站取票。

现在的⾼铁站都可以直接刷⾝份证了。但是以前的时候需要⽹上先购票, 然后再去⾼铁站的取票机上把票取出来。

想象下列场景:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

假设, ⼀个⼈每次来⾼铁站, 都会停⻋在同⼀个位置。(不同的⼈停⻋位置不同)。

每个⼈下⻋之后, 都往右⼿⽅向⾛, 遇到第⼀个取票机就进⾏取票。

在这个情况下, 如果扩容⼀个分⽚, 如何处理呢?

原有分⽚在环上的位置不动, 只要在环上新安排⼀个分⽚位置即可

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

此时,只需要把 0 号分⽚上的部分数据,搬运给 3 号分⽚即可。1 号分⽚和 2 号分⽚管理的区间都是不变的。

优点:⼤大降低了扩容时数据搬运的规模,提⾼了扩容操作的效率。

缺点:数据分配不均匀 (有的多有的少,数据倾斜)。

哈希槽分区算法 (Redis 使⽤)

为了解决上述问题 (搬运成本⾼ 和 数据分配不均匀),Redis cluster 引⼊了哈希槽 (hash slots) 算法

这种算法本质上的把一致性哈希和哈希求余结合了一下

 hash_slot = crc16(key) % 16384

其中 crc16 也是⼀种 hash 算法

16384 其实是 16 * 1024, 也就是 2^14

相当于是把整个哈希值, 映射到 16384 个槽位上, 也就是 [0,16383]

然后再把这些槽位⽐较均匀的分配给每个分⽚。每个分⽚的节点都需要记录⾃⼰持有哪些分⽚

假设当前有三个分⽚, ⼀种可能的分配⽅式:

  • 0 号分⽚: [0, 5461], 共 5462 个槽位
  • 1 号分⽚: [5462, 10923], 共 5462 个槽位
  • 2 号分⽚: [10924, 16383], 共 5460 个槽位

这⾥的分⽚规则是很灵活的。每个分⽚持有的槽位也不⼀定连续

每个分⽚的节点使⽤ 位图 来表⽰⾃⼰持有哪些槽位。对于 16384 个槽位来说, 需要 2048 个字节(2KB) ⼤⼩的内存空间表⽰

如果需要进⾏扩容, ⽐如新增⼀个 3 号分⽚, 就可以针对原有的槽位进⾏重新分配

⽐如可以把之前每个分⽚持有的槽位, 各拿出⼀点, 分给新分⽚

⼀种可能的分配⽅式:

  • 0 号分⽚: [0,4095], 共 4096 个槽位
  • 1 号分⽚: [5462,9557], 共 4096 个槽位
  • 2 号分⽚: [10924,15019], 共 4096 个槽位
  • 3 号分⽚: [4096,5461] + [9558,10923] + [15020,16383], 共 4096 个槽位

上述过程中,只有被移动的槽位,对应的数据才需要搬运

我们在实际使⽤ Redis 集群分⽚的时候, 不需要⼿动指定哪些槽位分配给某个分⽚, 只需要告诉某个分⽚应该持有多少个槽位即可, Redis 会⾃动完成后续的槽位分配, 以及对应的 key 搬运的⼯作

此处还有两个问题:

问题⼀: Redis 集群是最多有 16384 个分⽚吗?

并⾮如此。如果⼀个分⽚只有⼀个槽位, 这对于集群的数据均匀其实是难以保证的

实际上 Redis 的作者建议集群分⽚数不应该超过 1000

⽽且,16000 这么⼤规模的集群, 本⾝的可⽤性也是⼀个⼤问题。⼀个系统越复杂, 出现故障的概率是越⾼的

每个分片上只有一个槽位(此时很难保证数据在各个分片上的均衡性),key 是先映射到槽位,再映射到分片的。如果每个分片包含的槽位比较多,如果槽位个数相当,就可以认为是包含的 key 数量相当的。如果每个分配包含的槽位非常少,槽位个数不一定能直观的反应到 key 的数目。有的槽位可能是有多个 key,有的槽位可能是没有 key。

问题⼆: 为什么是 16384 个槽位?

Redis 作者的答案: https://github.com/antirez/redis/issues/2576

  • 节点之间通过⼼跳包通信。⼼跳包中包含了该节点持有哪些 slots(槽位)。这个是使⽤位图这样的数据结构表⽰的。表⽰ 16384 (16k) 个 slots, 需要的位图⼤⼩是 2KB。如果给定的 slots 数更多了, ⽐如 65536个了, 此时就需要消耗更多的空间, 8 KB 位图表⽰了。8 KB, 对于内存来说不算什么, 但是在频繁的⽹络⼼跳包中, 还是⼀个不⼩的开销的(周期性很强,特别频繁,很吃网络带宽)。
  • 另⼀⽅⾯, Redis 集群⼀般不建议超过 1000 个分⽚。 所以 16k 对于最⼤ 1000 个分⽚来说是⾜够⽤的, 同时也会使对应的槽位配置位图体积不⾄于很⼤

集群搭建 (基于 docker)

接下来基于 docker, 搭建⼀个集群。每个节点都是⼀个容器.

当前阶段因为只有一个云服务器,搞分布式系统比较麻烦。实际工作中,一般是通过多个主机的方式来搭建集群的。

拓扑结构如下:

注意!

此处我们先创建出 11 个 redis 节点。其中前 9 个⽤

redis-cluster-tool 是一个非常便利的 Redis 集群管理工具。help        Usage: redis-cluster-tool [-?hVds] [-v verbosity level] [-o output file]                  [-c conf file] [-a addr] [-i interval]                  [-p pid file] [-C command] [-r redis role]                  [-t thread number] [-b buffer size]    Options:      -h, --help             : this help      -V, --version          : show version and exit      -d, --daemonize        : run as a daemon      -s, --simple           : show the output not in detail      -v, --verbosity=N      : set logging level (default: 5, min: 0, max: 11)      -o, --output=S         : set logging file (default: stderr)      -c, --conf-file=S      : set configuration file (default: conf/rct.yml)      -a, --addr=S           : set redis cluster address (default: 127.0.0.1:6379)      -i, --interval=N       : set interval in msec (default: 1000 msec)      -p, --pid-file=S       : set pid file (default: off)      -C, --command=S        : set command to execute (default: cluster_state)      -r, --role=S           : set the role of the nodes that command to execute on (default: all, you can input: all, master or slave)      -t, --thread=N         : set how many threads to run the job(default: 8)      -b, --buffer=S         : set buffer size to run the job (default: 1048576 byte, unit:G/M/K)        Commands:        cluster_state                 :Show the cluster state.        cluster_used_memory           :Show the cluster used memory.        cluster_keys_num              :Show the cluster holds keys num.        slots_state                   :Show the slots state.        node_slot_num                 :Show the node hold slots number.        new_nodes_name                :Show the new nodes name that not covered slots.        cluster_rebalance             :Show the cluster how to rebalance.        flushall                      :Flush all the cluster.        cluster_config_get            :Get config from every node in the cluster and check consistency.        cluster_config_set            :Set config to every node in the cluster.        cluster_config_rewrite        :Rewrite every node config to echo node for the cluster.        node_list                     :List the nodes            del_keys                      :Delete keys in the cluster. The keys must match a given glob-style pattern.(This command not block the redis)ExampleGet the cluster state:        $redis-cluster-tool -a 127.0.0.1:34501 -C cluster_state -r master    master[127.0.0.1:34504] cluster_state is ok     master[127.0.0.1:34501] cluster_state is ok     master[127.0.0.1:34502] cluster_state is ok     master[127.0.0.1:34503] cluster_state is ok     all nodes cluster_state is ok    Get the cluster used memory:    $redis-cluster-tool -a 127.0.0.1:34501 -C cluster_used_memory -r master    master[127.0.0.1:34504] used 195 M 25%    master[127.0.0.1:34501] used 195 M 25%    master[127.0.0.1:34502] used 195 M 25%    master[127.0.0.1:34503] used 195 M 25%    cluster used 780 MRebalance the cluster slots:    $redis-cluster-tool -a 127.0.0.1:34501 -C cluster_rebalance    --from e1a4ba9922555bfc961f987213e3d4e6659c9316 --to 785862477453bc6b91765ffba0b5bc803052d70a --slots 2048    --from 437c719f50dc9d0745032f3b280ce7ecc40792ac --to cb8299390ce53cefb2352db34976dd768708bf64 --slots 2048    --from a497fc619d4f6c93bd4afb85f3f8a148a3f35adb --to a0cf6c1f12d295cd80f5811afab713cdc858ea30 --slots 2048    --from 0bdef694d08cb3daab9aac518d3ad6f8035ec896 --to 471eaf98ff43ba9a0aadd9579f5af1609239c5b7 --slots 2048Then you can use "redis-trib.rb reshard --yes --from e1a4ba9922555bfc961f987213e3d4e6659c9316 --to 785862477453bc6b91765ffba0b5bc803052d70a --slots 2048 127.0.0.1:34501" to rebalance the cluster slots     Flushall the cluster:    $redis-cluster-tool -a 127.0.0.1:34501 -C flushall -s    Do you really want to execute the "flushall"?    please input "yes" or "no" :        yes    OKGet a config from every node in cluster:    $redis-cluster-tool -a 127.0.0.1:34501 -C "cluster_config_get maxmemory" -r master    master[127.0.0.1:34501] config maxmemory is 1048576000 (1000MB)    master[127.0.0.1:34502] config maxmemory is 1048576000 (1000MB)    master[127.0.0.1:34503] config maxmemory is 1048576000 (1000MB)    master[127.0.0.1:34504] config maxmemory is 1048576000 (1000MB)    All nodes config are Consistent    cluster total maxmemory: 4194304000 (4000MB)Set a config from every node in cluster:    $redis-cluster-tool -a 127.0.0.1:34501 -C "cluster_config_set maxmemory 10000000" -s    Do you really want to execute the "cluster_config_set"?    please input "yes" or "no" :    yes        OKDelete keys in the cluster:    $redis-cluster-tool -a 127.0.0.1:34501 -C "del_keys abc*"    Do you really want to execute the "del_keys"?    please input "yes" or "no" :    yes    delete keys job is running...    delete keys job finished, deleted: 999999 keys, used: 4 s 标签:redis
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值