- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入浅析Redis 集群伸缩原理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
redis 节点分别维护自己负责的槽和对应的数据。伸缩原理:redis 槽和对应数据在不同节点之间移动 。
环境:centos7 搭建 redis 集群 。
。
1. 手动扩容 。
(1) 准备节点 9007,并加入集群 。
192.168.11.40:9001> cluster meet 192.168.11.40 9007 。
【注意】若 cluster meet 加入已存在于其它集群的节点,会导致集群合并,造成数据错乱!。建议使用 redis-cli 的 add-node:
1
2
3
4
|
# 若节点已加入其它集群或包含数据,会报错
add-node new_host:new_port existing_host:existing_port
--cluster-slave # 直接添加为从节点
--cluster-master-id <arg> # 从节点对应的主节点id
|
(2) 迁移槽和数据 。
槽在迁移过程中集群可以正常提供读写服务 。
首先确定原有节点的哪些槽需要迁移到新节点。确保每个节点负责相似数量的槽,保证各节点的数据均匀 。
槽是 redis 集群管理数据的基本单位。数据迁移是逐槽进行的 。
槽迁移流程:
cluster setslot {slot} importing {sourcenodeid}
cluster setslot {slot} migrating {targetnodeid}
cluster getkeysinslot {slot} {count}
migrate {targetip} {targetport} "" 0 {timeout} keys {keys...}
,把键通过流水线(pipeline)机制批量迁移到目标节点。redis3.0.6 后才支持批量迁移cluster setslot {slot} node {targetnodeid}
内部伪代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
def move_slot(source,target,slot):
# 目标节点准备导入槽
target.cluster("setslot",slot,"importing",source.nodeid);
# 目标节点准备全出槽
source.cluster("setslot",slot,"migrating",target.nodeid);
while true :
# 批量从源节点获取键
keys = source.cluster("getkeysinslot",slot,pipeline_size);
if keys.length == 0:
# 键列表为空时,退出循环
break;
# 批量迁移键到目标节点
source.call("migrate",target.host,target.port,"",0,timeout,"keys",keys);
# 向集群所有主节点通知槽被分配给目标节点
for node in nodes:
if node.flag == "slave":
continue;
node.cluster("setslot",slot,"node",target.nodeid);
|
(3) 将 9001 的槽 4096 迁移到 9007 中 。
准备数据 。
192.168.11.40:9001> set key:test:5028 value:5028 192.168.11.40:9001> set key:test:68253 value:68253 。
目标节点准备工作 。
1
2
3
4
5
6
7
8
9
10
11
|
192.168.11.40:9007> cluster nodes
8ccdb0963411ebd05ce21952bdd4b7597825afdc 192.168.11.40:9001@19001 master - 0 1620928869000 2 connected 0-5461
bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d 192.168.11.40:9007@19007 myself,master - 0 1620928868000 0 connected
...
# 9007 准备导入槽 4096 的数据
192.168.11.40:9007> cluster setslot 4096 importing 8ccdb0963411ebd05ce21952bdd4b7597825afdc
ok
# 槽 4096 已开启导入状态
192.168.11.40:9007> cluster nodes
bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d 192.168.11.40:9007@19007 myself,master - 0 1620928959000 0 connected [4096-<-8ccdb0963411ebd05ce21952bdd4b7597825afdc]
...
|
源节点准备工作 。
1
2
3
4
5
6
7
|
# 9001 准备导出槽 4096 数据
192.168.11.40:9001> cluster setslot 4096 migrating bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d
ok
# 槽 4096 已开启导出状态
192.168.11.40:9001> cluster nodes
8ccdb0963411ebd05ce21952bdd4b7597825afdc 192.168.11.40:9001@19001 myself,master - 0 1620929179000 2 connected 0-5461 [4096->-bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d]
...
|
导出数据 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 获取 100 个属于槽 4096 的键
192.168.11.40:9001> cluster getkeysinslot 4096 100
1) "key:test:5028"
2) "key:test:68253"
# 查看数据
192.168.11.40:9001> mget key:test:5028 key:test:68253
1) "value:5028"
2) "value:68253"
# 迁移这2个键:migrate 命令保证了每个键迁移过程的原子性
192.168.11.40:9001> migrate 192.168.11.40 9007 "" 0 5000 keys key:test:5028 key:test:68253
ok
# 再次查询会报 ask 错误:引导客户端找到数据所在的节点
192.168.11.40:9001> mget key:test:5028 key:test:68253
(error) ask 4096 192.168.11.40:9007
|
通知所有主节点:槽 4096 指派给 9007 。
1
2
3
4
|
192.168.11.40:9001> cluster setslot 4096 node bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d
192.168.11.40:9002> cluster setslot 4096 node bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d
192.168.11.40:9003> cluster setslot 4096 node bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d
192.168.11.40:9007> cluster setslot 4096 node bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d
|
查看最终结果 。
192.168.11.40:9007> cluster nodes 8ccdb0963411ebd05ce21952bdd4b7597825afdc 192.168.11.40:9001@19001 master - 0 1620931743303 7 connected 0-4095 4097-5461 bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d 192.168.11.40:9007@19007 myself,master - 0 1620931741000 8 connected 4096 ... 。
2. 使用 redis-cli 扩容 。
redis-cli 提供了槽重分片功能 。
reshard 命令参数详解:
1
2
3
4
5
6
7
8
|
reshard host:port # 集群内任意节点地址
--cluster-from <arg> # 源节点id,逗号分隔
--cluster-to <arg> # 目标节点id,只有一个
--cluster-slots <arg> # 迁移多少个槽
--cluster-yes # 确认执行reshard
--cluster-timeout <arg> # 每次 migrate 操作的超时时间,默认 60000ms
--cluster-pipeline <arg> # 每次批量迁移键的数量,默认 10
--cluster-replace
|
将 9001、9002、9003 的槽迁移到 9007,共迁移 4096 个 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
$ /usr/local/redis/bin/redis-cli --cluster reshard 192.168.11.40:9001
m: 8ccdb0963411ebd05ce21952bdd4b7597825afdc 192.168.11.40:9001
slots:[0-4095],[4097-5461] (5461 slots) master
1 additional replica(s)
m: bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d 192.168.11.40:9007
slots:[4096] (1 slots) master
...
[ok] all 16384 slots covered.
how many slots do you want to move (from 1 to 16384)? 4096
what is the receiving node id? bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d
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: 8ccdb0963411ebd05ce21952bdd4b7597825afdc
source node #2: 5786e3237c7fa413ed22465d15be721f95e72cfa
source node #3: 85ceb9826e8aa003169c46fb4ba115c72002d4f9
source node #4: done
moving slot 0 from 8ccdb0963411ebd05ce21952bdd4b7597825afdc
...
moving slot 12287 from 85ceb9826e8aa003169c46fb4ba115c72002d4f9
do you want to proceed with the proposed reshard plan (yes/no)? yes
moving slot 0 from 192.168.11.40:9001 to 192.168.11.40:9007:
...
moving slot 12287 from 192.168.11.40:9003 to 192.168.11.40:9007:
|
查看最终结果 。
192.168.11.40:9007> cluster nodes 8ccdb0963411ebd05ce21952bdd4b7597825afdc 192.168.11.40:9001@19001 master - 0 1620933907753 7 connected 1366-4095 4097-5461 5786e3237c7fa413ed22465d15be721f95e72cfa 192.168.11.40:9002@19002 master - 0 1620933906733 1 connected 6827-10922 85ceb9826e8aa003169c46fb4ba115c72002d4f9 192.168.11.40:9003@19003 master - 0 1620933905000 3 connected 12288-16383 bb1bb0f5f9e0ee67846ba8ec94a38da700e2e80d 192.168.11.40:9007@19007 myself,master - 0 1620933900000 8 connected 0-1365 4096 5462-6826 10923-12287 ... 。
检查节点之间槽的均衡性 。
1
2
3
4
|
$ /usr/local/redis/bin/redis-cli --cluster rebalance 192.168.11.40:9001
...
[ok] all 16384 slots covered.
*** no rebalancing needed! all nodes are within the 2.00% threshold.
|
迁移之后所有主节点负责的槽数量差异在 2% 以内,因此集群节点数据相对均匀,无需调整 。
。
1. 迁移槽 。
执行 reshard 三次,将数据平均分布到其他三个节点 。
2. 忘记节点 。
60s 内对所有节点执行如下操作:(不建议) 。
1
2
|
# 执行后,会将该节点加入禁用列表(持续 60s),不再向其发送 gossip 消息
cluster forget {nodeid}
|
建议使用 redis-cli 的 del-node 忘记节点:
1
|
/usr/local/redis/bin/redis-cli --cluster del-node {host:port} {nodeid}
|
内部伪代码 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def delnode_cluster_cmd(downnode):
# 下线节点不允许包含slots
if downnode.slots.length != 0
exit 1
end
# 向集群内节点发送cluster forget
for n in nodes:
if n.id == downnode.id:
# 不能对自己做forget操作
continue;
# 如果下线节点有从节点则把从节点指向其他主节点
if n.replicate && n.replicate.nodeid == downnode.id :
# 指向拥有最少从节点的主节点
master = get_master_with_least_replicas();
n.cluster("replicate",master.nodeid);
#发送忘记节点命令
n.cluster('forget',downnode.id)
# 节点关闭
downnode.shutdown();
|
若主从节点都要下线,先下线从,避免全量复制 。
以上就是redis 集群伸缩原理的详细内容,更多关于redis 集群原理的资料请关注我其它相关文章! 。
最后此篇关于深入浅析Redis 集群伸缩原理的文章就讲到这里了,如果你想了解更多关于深入浅析Redis 集群伸缩原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
最近做一个项目,由于是在别人框架里开发app,导致了很多限制,其中一个就是不能直接引用webservice 。 我们都知道,调用webserivice 最简单的方法就是在 "引用"
这是SDL2代码的一部分 SDL主函数 int main(int argc,char *argv[]) { ... ... bool quit=false; S
c 中的函数: PHPAPI char *php_pcre_replace(char *regex, int regex_len, ch
我有以下映射: public class SecurityMap : ClassMap { public SecurityMap() {
我在vue-lic3中使用了SCSS,但是有一个奇怪的错误,使用/ deep /会报告错误,我不想看到它。 代码运行环境 vue-cli3 + vant + scss 的CSS /deep/ .van
我在深入阅读 C# 时遇到了这个我能理解的内容: 当它被限制为引用类型时,执行的比较类型完全取决于类型参数被限制为什么。 但是不能理解这个: 如果进一步限制派生自重载 == 和 != 运算符的特定类型
Closed. This question is opinion-based。它当前不接受答案。 想改善这个问题吗?更新问题,以便editing this post用事实和引用来回答。 3年前关闭。
有人可以详细介绍关于自赋值的运算符重载中的 *this 和 const 例如: Class& Class::operator=(const Class& other) { a = other.
在向树中插入新节点时,如何填充闭包表的深度/长度列? ancestor 和 descendant 中的值是来自另一个表的 ID,表示要以树结构排列的页面。 关闭表: ancestor desce
现在我正在阅读“深入了解 C#”。缺少的一件事是完成一章后我可以解决的一系列问题。那会帮助我理解我刚刚学到的概念。 哪里可以找到适合 C#3.0 的问题集? 谢谢 最佳答案 你可以试试LINQ 101
TypeScript 给 JavaScript 扩展了类型的语法,我们可以给变量加上类型,在编译期间会做类型检查,配合编辑器还能做更准确的智能提示。此外,TypeScript 还支持了高级类型用
是否有一个单行代码来获取生成器并生成该生成器中的所有元素?例如: def Yearly(year): yield YEARLY_HEADER for month in range(1, 13)
所以我阅读了一些与“什么是方法组”相关的 StackOverflow 问题以及其他互联网文章,它们在底线都说了同样的话——方法组是“一组重载方法” ". 但是,在阅读 Jon Skeet 的“C# 深
有什么方法可以从子组件中获取子组件吗? 想象一下以下组件树: 应用程序 问题 问题选项(包含复选框) 问题选项(包含复选框) 问题选项(包含复选框) 我想从 App 访问问题选项以选中所有复选框。 参
class_eval 和 instance_eval 在定义方法等情况下是完全可以预测的。我也理解类的实例和类的单例(又名特征类)之间的区别。 但是 我无法弄清楚以下唯一的事情:比方说,出于某些策略目
我想出了如何将符号 rwx 部分读取/转换为 421 个八进制部分,这非常简单。但是当涉及到特殊字符时,我感到非常困惑。我们知道 -r-xr---wx 转换为 0543,但 -r-sr---wt 或
我怀疑我系统的 Java 版本有问题。某些应用程序出现段错误或内存不足或存在链接错误。如果我从源代码安装了 JDK,我会做类似“make test”的事情,看看哪些测试失败了。但是,看起来从源代码构建
如何克隆一个 repo(使用 libgit2 ) 我想做什么git clone确实,但有 libgit2 .我可能要问的是什么 git clone确实很深入。 这是我目前正在做的: 初始化一个repo
00、头痛的JS闭包、词法作用域? 被JavaScript的闭包、上下文、嵌套函数、this搞得很头痛,这语言设计的,感觉比较混乱,先勉强理解总结一下😂😂😂.
我开始玩 lubridate R 中的包。我注意到 now(tzone="EST")计算为: [1] "2015-08-25 13:01:08 EST" 而 now(tzone="PST")导致警告:
我是一名优秀的程序员,十分优秀!