Redis分片集群搭建

主从模式可以解决高可用、高并发读的问题。但依然有两个问题没有解决:

  • 海量数据存储
  • 高并发写

要解决这两个问题就需要用到分片集群了。分片的意思,就是把数据拆分存储到不同节点,这样整个集群的存储数据量就更大了。
Redis分片集群的结构如图

分片集群特征:

  • 集群中有多个master,每个master保存不同分片数据 ,解决海量数据存储问题
  • 每个master都可以有多个slave节点 ,确保高可用
  • master之间通过ping监测彼此健康状态 ,类似哨兵作用
  • 客户端请求可以访问集群任意节点,最终都会被转发到数据所在节点
  1. 搭建分片集群
  2. Redis分片集群最少也需要3个master节点,由于我们的机器性能有限,我们只给每个master配置1个slave,形成最小的分片集群:


计划部署的节点信息如下:

容器名角色IP映射端口
r1master192.168.150.1017001
r2master192.168.150.1017002
r3master192.168.150.1017003
r4slave192.168.150.1017004
r5slave192.168.150.1017005
r6slave192.168.150.1017006

集群配置
分片集群中的Redis节点必须开启集群模式,一般在配置文件中添加下面参数:

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes

其中有3个我们没见过的参数:

  • cluster-enabled:是否开启集群模式
  • cluster-config-file:集群模式的配置文件名称,无需手动创建,由集群自动维护
  • cluster-node-timeout:集群中节点之间心跳超时时间

一般搭建部署集群肯定是给每个节点都配置上述参数,不过考虑到我们计划用docker-compose部署,因此可以直接在启动命令中指定参数,偷个懒。
在虚拟机的/root目录下新建一个redis-cluster目录,然后在其中新建一个docker-compose.yaml文件,内容如下:

version: "3.2"services:r1:image: rediscontainer_name: r1network_mode: "host"entrypoint: ["redis-server", "--port", "7001", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]r2:image: rediscontainer_name: r2network_mode: "host"entrypoint: ["redis-server", "--port", "7002", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]r3:image: rediscontainer_name: r3network_mode: "host"entrypoint: ["redis-server", "--port", "7003", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]r4:image: rediscontainer_name: r4network_mode: "host"entrypoint: ["redis-server", "--port", "7004", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]r5:image: rediscontainer_name: r5network_mode: "host"entrypoint: ["redis-server", "--port", "7005", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]r6:image: rediscontainer_name: r6network_mode: "host"entrypoint: ["redis-server", "--port", "7006", "--cluster-enabled", "yes", "--cluster-config-file", "node.conf"]注意:使用Docker部署Redis集群,network模式必须采用host3.1.2.启动集群
进入/root/redis-cluster目录,使用命令启动redis:docker-compose up -d启动成功,可以通过命令查看启动进程:ps -ef | grep redis
# 结果:
root       4822   4743  0 14:29 ?        00:00:02 redis-server *:7002 [cluster]
root       4827   4745  0 14:29 ?        00:00:01 redis-server *:7005 [cluster]
root       4897   4778  0 14:29 ?        00:00:01 redis-server *:7004 [cluster]
root       4903   4759  0 14:29 ?        00:00:01 redis-server *:7006 [cluster]
root       4905   4775  0 14:29 ?        00:00:02 redis-server *:7001 [cluster]
root       4912   4732  0 14:29 ?        00:00:01 redis-server *:7003 [cluster]

注意:使用Docker部署Redis集群,network模式必须采用host

启动集群

进入/root/redis-cluster目录,使用命令启动redis:

docker-compose up -d

启动成功,可以通过命令查看启动进程:

ps -ef | grep redis
# 结果:
root       4822   4743  0 14:29 ?        00:00:02 redis-server *:7002 [cluster]
root       4827   4745  0 14:29 ?        00:00:01 redis-server *:7005 [cluster]
root       4897   4778  0 14:29 ?        00:00:01 redis-server *:7004 [cluster]
root       4903   4759  0 14:29 ?        00:00:01 redis-server *:7006 [cluster]
root       4905   4775  0 14:29 ?        00:00:02 redis-server *:7001 [cluster]
root       4912   4732  0 14:29 ?        00:00:01 redis-server *:7003 [cluster]

可以发现每个redis节点都以cluster模式运行。不过节点与节点之间并未建立连接。
接下来,我们使用命令创建集群:

# 进入任意节点容器
docker exec -it r1 bash
# 然后,执行命令
redis-cli --cluster create --cluster-replicas 1 \
192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 \
192.168.150.101:7004 192.168.150.101:7005 192.168.150.101:7006

命令说明:

  • redis-cli --cluster:代表集群操作命令
  • create:代表是创建集群
  • –cluster-replicas 1 :指定集群中每个master的副本个数为1
    • 此时节点总数 ÷ (replicas + 1) 得到的就是master的数量n。因此节点列表中的前n个节点就是master,其它节点都是slave节点,随机分配到不同master

输入命令后控制台会弹出下面的信息:

这里展示了集群中master与slave节点分配情况,并询问你是否同意。节点信息如下:

  • 7001是master,节点id后6位是da134f
  • 7002是master,节点id后6位是862fa0
  • 7003是master,节点id后6位是ad5083
  • 7004是slave,节点id后6位是391f8b,认ad5083(7003)为master
  • 7005是slave,节点id后6位是e152cd,认da134f(7001)为master
  • 7006是slave,节点id后6位是4a018a,认862fa0(7002)为master

输入yes然后回车。会发现集群开始创建,并输出下列信息:

接着,我们可以通过命令查看集群状态:

redis-cli -p 7001 cluster nodes

结果:

  1. 散列插槽
  2. 数据要分片存储到不同的Redis节点,肯定需要有分片的依据,这样下次查询的时候才能知道去哪个节点查询。很多数据分片都会采用一致性hash算法。而Redis则是利用散列插槽(hash slot)的方式实现数据分片
  3. 在Redis集群中,共有16384个hash slots,集群中的每一个master节点都会分配一定数量的hash slots。具体的分配在集群创建时就已经指定了:


如图中所示:

  • Master[0],本例中就是7001节点,分配到的插槽是0~5460
  • Master[1],本例中就是7002节点,分配到的插槽是5461~10922
  • Master[2],本例中就是7003节点,分配到的插槽是10923~16383
  • 当我们读写数据时,Redis基于CRC16 算法对key做hash运算,得到的结果与16384取余,就计算出了这个key的slot值。然后到slot所在的Redis节点执行读写操作。

不过hash slot的计算也分两种情况:

  • 当key中包含{}时,根据{}之间的字符串计算hash slot
  • 当key中不包含{}时,则根据整个key字符串计算hash slot
  • 例如:
  • key是user,则根据user来计算hash slot
  • key是user:{age},则根据age来计算hash slot

我们来测试一下,先于7001建立连接:

# 进入容器
docker exec -it r1 bash
# 进入redis-cli
redis-cli -p 7001
# 测试
set user jack

会发现报错了:

提示我们MOVED 5474,其实就是经过计算,得出user这个key的hash slot 是5474,而5474是在7002节点,不能在7001上写入!!
说好的任意节点都可以读写呢?
这是因为我们连接的方式有问题,连接集群时,要加-c参数:

# 通过7001连接集群
redis-cli -c -p 7001
# 存入数据
set user jack

结果如下:可以看到,客户端自动跳转到了5474这个slot所在的7002节点。
现在,我们添加一个新的key,这次加上{}:

# 试一下key中带{}
set user:{age} 21# 再试一下key中不带{}
set age 20

结果如下:

可以看到user:{age}和age计算出的slot都是741。

3.3.故障转移

分片集群的节点之间会互相通过ping的方式做心跳检测,超时未回应的节点会被标记为下线状态。当发现master下线时,会将这个master的某个slave提升为master。
我们先打开一个控制台窗口,利用命令监测集群状态:

watch docker exec -it r1 redis-cli -p 7001 cluster nodes

命令前面的watch可以每隔一段时间刷新执行结果,方便我们实时监控集群状态变化。
接着,我们故技重施,利用命令让某个master节点休眠。比如这里我们让7002节点休眠,打开一个新的ssh控制台,输入下面命令:

docker exec -it r2 redis-cli -p 7002 DEBUG sleep 30

可以观察到,集群发现7002宕机,标记为下线:
过了一段时间后,7002原本的小弟7006变成了master:

而7002被标记为slave,而且其master正好是7006,主从地位互换。

3.4.总结

Redis分片集群如何判断某个key应该在哪个实例?

  • 将16384个插槽分配到不同的实例
  • 根据key计算哈希值,对16384取余
  • 余数作为插槽,寻找插槽所在实例即可
  • 如何将同一类数据固定的保存在同一个Redis实例?
  • Redis计算key的插槽值时会判断key中是否包含{},如果有则基于{}内的字符计算插槽
  • 数据的key中可以加入{类型},例如key都以{typeId}为前缀,这样同类型数据计算的插槽一定相同

3.5.Java客户端连接分片集群(选学)

RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致,参考2.5节:
1)引入redis的starter依赖
2)配置分片集群地址
3)配置读写分离
与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下:

spring:redis:cluster:nodes:- 192.168.150.101:7001- 192.168.150.101:7002- 192.168.150.101:7003- 192.168.150.101:8001- 192.168.150.101:8002- 192.168.150.101:8003

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

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

相关文章

嵌入式系统软件开发环境_2.一般架构

1.Eclipse框架 嵌入式系统软件开发环境是可帮助用户开发嵌入式软件的一组工具的集合,其架构的主要特征离不开“集成”问题,采用什么样的架构框架是决定开发环境优劣主要因素。Eclipse框架是当前嵌入式系统软件开发环境被普遍公认的一种基础环境框架。目…

基 CanMV 的 C 开发环境搭建(Linux,Ubuntu篇)

不论是使用 CanMV 提供的基于 C 语言和 FreeRTOS 的应用开发方式开发应用程序或是编译 CanMV 固件,都需要搭建基于 CanMV 的 C 开发环境,用于编译 CanMV 源码。 1. 开发环境搭建说明 CanMV 提供了基于 C 语言和 FreeRTOS 的应用开发…

Hive笔记-3

3.2.2 查看表 1) 展示所有表 (1) 语法: 语法: SHOW TABLES [IN database_name] LIKE [identifier_with_wildcards]; In database_name 写的是查哪个数据库,一般不写默认是当前数据库 Like 后面跟通配符表达式 (2) 案例: 查看在 db_hive1 数据库里有没有以 stu 开头的表 …

Zookeeper 一、Zookeeper简介

1.分布式系统定义及面临的问题 分布式系统是同时跨越多给物理主机,独立运行的多个软件所组成的系统。类比一下,分布式系统就是一群人一起干活。人多力量大,每个服务器的算力是有限的,但是通过分布式系统,由n个服务器组…

【Python日志模块全面指南】:记录每一行代码的呼吸,掌握应用程序的脉搏

文章目录 🚀一、了解日志🌈二、日志作用🌈三、了解日志模块⭐四、日志级别💥五、记录日志-基础❤️六、记录日志-处理器handler🎬七、记录日志-格式化记录☔八、记录日志-配置logger👊九、流程梳理 &#x…

RTSP/Onvif安防监控平台EasyNVR抓包命令tcpdump使用不了,该如何解决?

安防视频监控汇聚EasyNVR智能安防视频监控平台,是基于RTSP/Onvif协议的安防视频平台,可支持将接入的视频流进行全平台、全终端分发,分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。平台可提供的视频能力包括:…

SpringSecurity实战入门——认证

项目代码 gson/spring-security-demo 简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity来做安全框架。小项目有Shiro的比较多,因为相比…

HTML静态网页成品作业(HTML+CSS+JS)—— 美食企业曹氏鸭脖介绍网页(4个页面)

🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,使用Javacsript代码实现 图片轮播切换,共有4个页面。 二、…

宕机了, redis如何保证数据不丢?

前言 如果有人问你:"你会把 Redis 用在什么业务场景下?" 我想你大概率会说:"我会把它当作缓存使用,因为它把后端数据库中的数据存储在内存中,然后直接从内存中读取数据,响应速度会非常快。…

分支结构相关

1.if 语句 结构: if 条件语句: 代码块 小练习: 使用random.randint()函数随机生成一个1~100之间的整数,判断是否是偶数 import random n random.randint(1,100) print(n) if n % 2 0:print(str(n) "是偶数") 2.else语…

【网络安全产品】---网闸

了解了不少安全产品,但是对网闸的理解一直比较模糊,今天 what 网闸是安全隔离与信息交换系统的简称,使得在不影响数据正常通信的前提下,让络在不连通的情况下数据的安全交换和资源共享,对不同安全域/网络之间实现真正…

【总线】AXI总线:FPGA设计中的通信骨干

目录 AXI4:高性能地址映射通信的基石 AXI4-Lite:轻量级但功能强大的通信接口 AXI4-Stream:高速流数据传输的利器 结语:AXI总线在FPGA设计中的重要性 大家好,欢迎来到今天的总线学习时间!如果你对电子设计、特别是FPGA和SoC设计…

简单分享github

一、官网 GitHub: Let’s build from here GitHub 二、注册 通过简单的注册步骤,你就可以拥有一个属于自己的GitHub账号。再简单注册完成之后会需要验证你所输入的邮箱才能正常使用你的GitHub。 三、设置自己的库 在注册完成之后,完成一些简单的设置之…

Python第二语言(十三、PySpark实战)

目录 1.开篇 2. PySpark介绍 3. PySpark基础准备 3.1 PySpark安装 3.2 掌握PySpark执行环境入口对象的构建 3.3 理解PySpark的编程模型 4. PySpark:RDD对象数据输入 4.1 RDD对象概念:PySpark支持多种数据的输入,完成后会返回RDD类的对…

5款堪称变态的AI神器,焊死在电脑上永不删除!

一 、AI视频合成工具——Runway: 第一款RunWay,你只需要轻轻一抹,视频中的元素就会被擦除,再来轻轻一抹,直接擦除,不喜欢这个人直接擦除,一点痕迹都看不出来。 除了视频擦除功能外,…

从复用性角度阐述中台建设

目录 复用性中台定义深思中台建设产品线形态何时演变中台能力落地中台 业务中台架构总结 技术学习永不止步,最近也是看了很多关于架构设计相关的专栏,慢慢总结出来一部分知识,代入自己的思考与理解,以及结合并反思自己之前公司的架…

Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(factoryMethod)

序言 前面文章介绍了在Spring中多种创建Bean实例的方式,包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor。 这篇文章继续介绍Spring中创建Bean的形式之一——factoryMethod。方法用的不多,感兴趣可以当扩展了解。…

php反序列化的一些知识

问题 <?php $raw O:1:"A":1:{s:1:"a";s:1:"b";}; echo serialize(unserialize($raw)); //O:1:"A":1:{s:1:"a";s:1:"b";}?> php反序列化的时按理说找不到A这个类&#xff0c;但是他没有报错&#xff0c;…

【前端技巧】css篇

利用counter实现计数器 counter-reset&#xff1a;为计数器设置名称&#xff0c;语法如下&#xff1a; counter-rese: <idntifier><integer>第一个参数为变量名称&#xff0c;第二个参数为初始值&#xff0c;默认为0 counter-increment&#xff1a;设置计数器增…

基于51单片机的篮球计分器设计

一.硬件方案 本设计用由AT89C51编程控制LED七段数码管作显示的球赛计时计分系统。该系统具有赛程定时设置、赛程时间暂停、及时刷新甲乙双方的成绩等功能。 电路主要由STC89C52单片机最小系统数码管显示模块数码管驱动模块蜂鸣器模块按键模块&#xff1b; 二.设计功能 &…