关于Redis的集群(上)

目录

基本概念

数据分片算法

哈希求余

​编辑一致性哈希算法

哈希槽分区算法

搭建集群环境

创建目录和配置

编写 docker-compose.yml

启动容器

构建集群


基本概念

广义的集群,只要是多个机器构成了分布式系统,都可以成为是一个“集群”。

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

哨兵模式提高了系统的可用性。哨兵模式中本质上还是redis主从节点存储数据。其中就要求一个主节点/从节点,就得存储整个数据的“全集”。

此处关键问题,就是引入多台机器,每台机器存储一部分数据。每个存储数据的机器还需要搭配若干个从节点。

每个 Slave 都是对应 Master 的备份(当 Master 挂了, 对应的 Slave 会补位成 Master).
每个红框部分都可以称为是⼀个 分片(Sharding).

把数据分成多份,具体该如何去分呢?


数据分片算法

有三中主流的分片方式。

哈希求余

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

比如有三个分片,编号0,1,2。此时就可以针对要插入的数据的key(redis都是键值对结构的数据)计算hash值。(比如md5)再把这个hash值余上分片个数,就得到了一个下标。此时就可以把这个数据放到该下标对应的分片中了。

后续查询key的时候,也是同样的算法。

拓展:MD5是一个非常广泛使用的hash算法。

        特点:1.md5计算结果是定长的。无论输入的原字符串多长,最终算出的结果就是固定长度。

                   2.md5计算结果是分散的。两个原字符串,哪怕大部分都相同,只有一个小的地方不同,算出来的md5值也会差别很大。

                   3.md5计算结果是不可逆的。给你原字符串,可以很容易算出md5的值。给你md5值,很难还原出原始的字符串的。

一旦服务器集群需要扩容,就需要更高的成本。分片主要目的就是为了能提高存储能力。分片越多,能存的数据越多,成本也更高。随着业务的增长,数据变多了,需要更多的分片。

当引入更多的分片的时候,N(分片数量)就改变了。如果发现某个数据,在扩容之后,不应该在当前的分片中了,就需要重新进行分配(搬运数据)。绝大部分数据都需要进行搬运,成本过高。

一致性哈希算法

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

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

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

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

此时,如果增加分片,会怎样呢?

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

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

优点: 大大降低了扩容时数据搬运的规模, 提⾼了扩容操作的效率。
缺点: 数据分配不均匀 (有的多有的少, 数据倾斜)。

哈希槽分区算法

这是redis真正采用的分片算法。采用以下公式:

hash_slot = crc16(key) % 16384

crc16也是一种计算hash的算法,这里不多展开。

16384 = 16* 1024

得到结果hash_slot,称为哈希槽

会把这些哈希槽分配到不同的分片上。

此时,这三个分片上的数据就是比较均匀的了。会先根据key算出哈希槽,再根据哈希槽,映射到分片。这种算法,本质就是把上述两种方式结合了一下。

这里只是一种可能的方式,实际上分片是非常灵活的。

每个分片持有的槽位号,可以是连续的,也可以是不连续的。

此时,每个分片都会使用“位图”这样的数据结构,表示出当前有多少槽位号。

16384个bit位,用每一位0 / 1来区分自己这个分片当前是否持有该槽位号。  

进行扩容:

上述过程中,只有被移动的槽位,对应的数据才需要搬运。降低了搬运成本。

redis中,当前某个分片包含哪些槽位,都是可以手动配置的。

问题1:Redis集群最多有16384个分片吗?

如果⼀个分片只有⼀个槽位, 这对于集群的数据均匀其实是难以保证的。
实际上 Redis 的作者建议集群分片数不应该超过 1000。而且, 16000 这么⼤规模的集群, 本⾝的可用性也是⼀个⼤问题. ⼀个系统越复杂, 出现故障的概率是越⾼的。

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

节点之间通过心跳包通信,心跳包中包含该节点持有那些槽位。如果16384个槽位,每个分片需要2KB大小的位图,来表示持有的槽位。如果槽位更多,就需要消耗更大的空间,更大的网络带宽。16384是比较均衡的值,个数上基本够用,也不会占据大的硬件资源(网络带宽)。


搭建集群环境

这里使用docker来搭建redis集群。

创建目录和配置

创建 redis-cluster ⽬录。内部创建两个⽂件。

redis-cluster/
├── docker-compose.yml
└── generate.sh

generate.sh 内容如下:

for port in $(seq 1 9); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.10${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done
# 注意 cluster-announce-ip 的值有变化.
for port in $(seq 10 11); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done

在linux上,以.sh后缀结尾的文件,称为“shell脚本”。使用linux的时候,都是通过一些命令来进行操作的。使用命令操作,就非常适合把命令给写到一个文件中,批量化执行。同时还能加入条件,循环,函数等机制。就可以基于这些来完成更复杂的工作了。

此时需要创建11个redis节点,这些redis的配置文件内容,大同小异。此时就可以使用脚本来批量生成。

• cluster-enabled yes 开启集群。
• cluster-config-file nodes.conf 集群节点⽣成的配置。
• cluster-node-timeout 5000 节点失联的超时时间。
• cluster-announce-ip 172.30.0.101 节点⾃⾝ ip。
• cluster-announce-port 6379 节点⾃⾝的业务端⼝。
• cluster-announce-bus-port 16379 节点⾃⾝的总线端⼝. 集群管理的信息交互是通过这个端口进行的。

编写 docker-compose.yml

• 先创建 networks, 并分配⽹段为 172.30.0.0/24
• 配置每个节点. 注意配置⽂件映射, 端⼝映射, 以及容器的 ip 地址. 设定成固定 ip ⽅便后续的观察和操作。

version: '3.7'
networks:
mynet:
ipam:
config:
- subnet: 172.30.0.0/24
services:
redis1:
image: 'redis:5.0.9'
container_name: redis1
restart: always
volumes:
- ./redis1/:/etc/redis/
ports:
- 6371:6379
- 16371:16379
command:
redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.101
redis2:
image: 'redis:5.0.9'
container_name: redis2
restart: always
volumes:
- ./redis2/:/etc/redis/
ports:
- 6372:6379
- 16372:16379
command:
redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.102
redis3:
image: 'redis:5.0.9'
container_name: redis3
restart: always
volumes:
- ./redis3/:/etc/redis/
ports:
- 6373:6379
- 16373:16379
command:
redis-server /etc/redis/redis.conf
networks:
mynet:
ipv4_address: 172.30.0.103

启动容器

docker-compose up -d

构建集群

接下来,要把上述redis节点构建成集群。

redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379
172.30.0.103:6379 172.30.0.104:6379 172.30.0.105:6379 172.30.0.106:6379
172.30.0.107:6379 172.30.0.108:6379 172.30.0.109:6379  --cluster-replicas 2

列出每个参与构建集群的ip和端口。(端口都是容器内部的端口)

cluster create: 表⽰建⽴集群. 后⾯填写每个节点的 ip 和地址。

cluster-replicas 2: 描述集群的每个主节点,应该有2个从节点。一共9个节点,设置了之后,redis就知道了3个节点是一伙的(一个分片)。

执⾏之后, 容器之间会进⾏加⼊集群操作。⽇志中会描述哪些是主节点, 哪些从节点跟随哪个主节点。

>>> Performing hash slots allocation on 9 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.30.0.105:6379 to 172.30.0.101:6379
Adding replica 172.30.0.106:6379 to 172.30.0.101:6379
Adding replica 172.30.0.107:6379 to 172.30.0.102:6379
Adding replica 172.30.0.108:6379 to 172.30.0.102:6379
Adding replica 172.30.0.109:6379 to 172.30.0.103:6379
Adding replica 172.30.0.104:6379 to 172.30.0.103:6379

以上,关于redis集群,希望对你有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/72635.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CSS3】化神篇

目录 平面转换平移旋转改变旋转原点多重转换缩放倾斜 渐变线性渐变径向渐变 空间转换平移视距旋转立体呈现缩放 动画使现步骤animation 复合属性animation 属性拆分逐帧动画多组动画 平面转换 作用&#xff1a;为元素添加动态效果&#xff0c;一般与过渡配合使用 概念&#x…

Java 线程创建全解析:五种方式详细对比与实战示例

目录 Java 线程创建全解析&#xff1a;五种方式详细对比与实战示例一、引言二、创建线程的五种方式1. 继承 Thread 类2. 实现 Runnable 接口3. 匿名内部类实现 Thread4. 匿名内部类实现 Runnable5. 使用 Lambda 表达式&#xff08;Java 8及以上&#xff09; 三、线程创建方式对…

Dify平台离线镜像部署

Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使开发者可以快速搭建生产级的生成式 AI 应用。即使你是非技术人员&#xff0c;也能参与到 AI 应用的定义和数据运营过程中。 前提…

Unitest和pytest区别

1️⃣ 设计理念 ➡️ unittest &#xff1a; 是 Python 标准库的一部分&#xff0c;设计灵感来自 Java 的 JUnit&#xff0c;强调面向对象的测试模式。 测试用例需要继承 unittest.TestCase 类&#xff0c;测试方法以 test_ 开头。 更适合传统、结构化的测试开发方式。 ⭕ pyte…

GIT标签(Tag)操作

在Git中&#xff0c;标签&#xff08;Tag&#xff09;用于标记特定的提交点&#xff0c;通常用于发布版本。 切换到需要打标签的分支&#xff1a; git checkout <branch-name>创建标签 git tag v1.0.0 git tag -a v1.0.0 -m "Release version 1.0.0"查看所…

玩转github

me github 可以给仓库添加开发人员吗 4o 是的&#xff0c;GitHub允许仓库管理员为仓库添加开发人员&#xff0c;并设置这些开发人员的角色和权限。这里是一个简单的步骤指导&#xff0c;教你如何给一个 GitHub 仓库添加开发人员&#xff1a; 前提条件 你必须有这个仓库的权限&…

EDAS:投稿经验-word版本-问题解决

1. 字体不对&#xff0c;字体未嵌入问题 问题&#xff1a;word转PDF后&#xff0c;总是显示有字体格式不对&#xff08;忘记截图了&#xff09;。 办法&#xff1a;1. EDAS投稿PDF格式问题-CSDN博客-PDF上修改 IEEE论文检测的字体未嵌入问题Times New Ro…

如何在Django中有效地使用Celery进行定时任务?

当我们谈到Web开发时&#xff0c;Django无疑是一个非常流行的框架。而Celery则是与Django配合使用的强大任务队列工具。今天&#xff0c;我们来聊聊如何在Django中使用Celery来实现定时任务。定时任务在很多场景下都非常有用&#xff0c;比如定期发送邮件、清理数据库、执行数据…

声学建模中用于构音障碍语音识别的特征选择意义

声学建模中用于构音障碍语音识别的特征选择意义 原文:Significance of Feature Selection for Acoustic Modeling in Dysarthric Speech Recognition 引言 背景 构音障碍是由运动言语系统的神经损伤引起的,导致发音不清晰。自动语音识别系统对构音障碍语音无效,因其声学差…

【递归与动态规划(DP) C/C++】(1)递归 与 动态规划(DP)

- 第 82 篇 - Date: 2025 - 03 - 17 Author: 郑龙浩/仟濹 【递归与动态规划(DP) C/C】 文章目录 一 递归1基本介绍2 递归技巧**(1) 递归三步法****(2) 思维小技巧** 3 例题(1) 阶乘 (纯递归 or DP)(2) 斐波那契数列 (纯递归 or DP)(3) 汉诺塔 (纯递归 or DP)**① 英文打印过程…

eclipse运行配置,希望带参数该怎么配置

java -Dparam 在eclipse如何配置 在Eclipse中配置-Dparam这样的JVM参数&#xff0c;你可以按照以下步骤进行&#xff1a; 打开Eclipse。 选择菜单栏的"Run" -> "Run Configurations..."。 在弹出的"Run Configurations"窗口左侧&#xff0…

什么是 Fisher 信息矩阵

什么是 Fisher 信息矩阵 Fisher 信息矩阵是统计学和机器学习中一个重要的概念,它用于衡量样本数据所包含的关于模型参数的信息量。 伯努利分布示例 问题描述 假设我们有一个服从伯努利分布的随机变量 X X X,其概率质量函数为 P ( X 

[C++面试] 标准容器面试点

一、入门 1、vector和list的区别 [C面试] vector 面试点总结 vector 是动态数组&#xff0c;它将元素存储在连续的内存空间中。支持随机访问&#xff0c;即可以通过下标快速访问任意位置的元素&#xff0c;时间复杂度为 O(1)&#xff0c;准确点是均摊O(1)。但在中间或开头插…

C++抽象与类的核心概念解析

在C中&#xff0c;抽象&#xff08;Abstraction&#xff09; 是面向对象编程&#xff08;OOP&#xff09;的核心概念之一&#xff0c;它通过隐藏复杂的实现细节&#xff0c;仅暴露必要的接口来实现对现实世界的简化建模。类&#xff08;Class&#xff09; 是实现抽象的核心工具…

C# NX二次开发:拉伸UFUN函数避坑指南

大家好&#xff0c;今天想说一下拉伸相关UFUN函数的使用&#xff0c;尽量让大家别踩坑。 官方给出的拉伸UFUN函数有如下几个&#xff1a; &#xff08;1&#xff09;UF_MODL_create_extruded2 (view source) uf_list_p_tobjectsInputList of objects to be extruded.char *ta…

基于 Python 爬取 TikTok 搜索数据 Tiktok爬虫(2025.3.17)

1. 前言 在数据分析和网络爬虫的应用场景中&#xff0c;我们经常需要获取社交媒体平台的数据&#xff0c;例如 TikTok。本篇文章介绍如何使用 Python 爬取 TikTok 用户搜索数据&#xff0c;并解析其返回的数据。 结果截图 2. 项目环境准备 在正式运行代码之前&#xff0c;我…

AI日报 - 2025年3月18日

AI日报 - 2025年3月18日 &#x1f31f; 今日概览&#xff08;60秒速览&#xff09; ▎&#x1f916; AGI突破 | SOO微调技术减少语言模型欺骗行为10倍 创新对齐技术为更安全AI铺路 ▎&#x1f4bc; 商业动向 | Figure推出全球最高产量人形机器人生产线BotQ 年产1.2万台&#x…

【go】函数类型的作用

Go 语言函数类型的巧妙应用 函数类型在 Go 语言中非常强大&#xff0c;允许将函数作为值进行传递和操作。下面详细介绍函数类型的各种妙用&#xff1a; 1. 回调函数 // 定义一个函数类型 type Callback func(int) int// 接受回调函数的函数 func processData(data []int, ca…

每日一题--进程与协程的区别

进程是什么&#xff1f; 进程&#xff08;Process&#xff09; 是操作系统进行 资源分配和调度的基本单位&#xff0c;代表一个正在执行的程序实例。每个进程拥有独立的虚拟地址空间、代码、数据和系统资源&#xff08;如文件句柄、网络端口等&#xff09;。进程之间通过 IPC&…

关于deepseek R1模型分布式推理效率分析

1、引言 DeepSeek R1 采用了混合专家&#xff08;Mixture of Experts&#xff0c;MoE&#xff09;架构&#xff0c;包含多个专家子网络&#xff0c;并通过一个门控机制动态地激活最相关的专家来处理特定的任务 。DeepSeek R1 总共有 6710 亿个参数&#xff0c;但在每个前向传播…