《Redis系列专题》 之 大规模互联网应用Redis架构要点(精华)

article/2025/3/18 22:15:02

建议有一定工作经验者阅读
通常,为了提高网站响应速度,总是把热点数据保存在内存中而不是直接从后端数据库中读取。Redis是一个很好的Cache工具。大型网站应用,热点数据量往往巨大,几十G上百G是很正常的事儿,在这种情况下,如何正确架构Redis呢?

首先,无论我们是使用自己的物理主机,还是使用云服务主机,内存资源往往是有限制的,scale up不是一个好办法,我们需要scale out横向可伸缩扩展,这需要由多台主机协同提供服务,即分布式多个Redis实例协同运行。

其次,目前硬件资源成本降低,多核CPU,几十G内存的主机很普遍,对于主进程是单线程工作的Redis,只运行一个实例就显得有些浪费。同时,管理一个巨大内存不如管理相对较小的内存高效。因此,实际使用中,通常一台机器上同时跑多个Redis实例。

Redis 3正式推出了官方集群技术,解决了多Redis实例协同服务问题。Redis Cluster可以说是服务端Sharding分片技术的体现,即将键值按照一定算法合理分配到各个实例分片上,同时各个实例节点协调沟通,共同对外承担一致服务。

多Redis实例服务,比单Redis实例要复杂的多,这涉及到定位、协同、容错、扩容等技术难题。这里,我们介绍一种轻量级的客户端Redis Sharding技术。

Redis Sharding可以说是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是采用哈希算法将Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上。这样,客户端就知道该向哪个Redis节点操作数据。Sharding架构如图:
去
庆幸的是,java redis客户端驱动jedis,已支持Redis Sharding功能,即ShardedJedis以及结合缓存池的ShardedJedisPool。

Jedis的Redis Sharding实现具有如下特点:

  1. 采用一致性哈希算法(consistent hashing),将key和节点name同时hashing,然后进行映射匹配,采用的算法是MURMUR_HASH。采用一致性哈希而不是采用简单类似哈希求模映射的主要原因是当增加或减少节点时,不会产生由于重新匹配造成的rehashing。一致性哈希只影响相邻节点key分配,影响量小。

2.为了避免一致性哈希只影响相邻节点造成节点分配压力,ShardedJedis会对每个Redis节点根据名字(没有,Jedis会赋予缺省名字)会虚拟化出160个虚拟节点进行散列。根据权重weight,也可虚拟化出160倍数的虚拟节点。用虚拟节点做映射匹配,可以在增加或减少Redis节点时,key在各Redis节点移动再分配更均匀,而不是只有相邻节点受影响。

3.ShardedJedis支持keyTagPattern模式,即抽取key的一部分keyTag做sharding,这样通过合理命名key,可以将一组相关联的key放入同一个Redis节点,这在避免跨节点访问相关数据时很重要。

下面我们用Jedis实际操作下:

1.pom.xml中配置jedis jar包

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.7.2</version>
</dependency>

2.spring配置文件中配置ShardedJedisPool

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">  <property name="maxTotal"  value="4096"/>  <property name="maxIdle" value="200"/>  <property name="maxWaitMillis" value="3000"/><property name="testOnBorrow" value="true" /><property name="testOnReturn" value="true" />
</bean><bean id = "shardedJedisPool" class = "redis.clients.jedis.ShardedJedisPool"><constructor-arg index="0" ref="poolConfig"/><constructor-arg index="1"><list><bean class="redis.clients.jedis.JedisShardInfo"><constructor-arg index="0" value="192.168.1.119" type="String"/><!-- shard name --><constructor-arg index="1" value="Shard-1" type="String"/>       <constructor-arg index="2" value="6379" type="int"/><!--  timeout,default is 2 sec --><constructor-arg index="3" value="2000" type="int"/><!-- weight,default is 1 -->  <constructor-arg index="4" value="1" type="int"/>     </bean><bean class="redis.clients.jedis.JedisShardInfo"><constructor-arg index="0" value="192.168.1.119" type="String"/><constructor-arg index="1" value="Shard-2" type="String"/>       <constructor-arg index="2" value="6479" type="int"/><constructor-arg index="3" value="2000" type="int"/><constructor-arg index="4" value="1" type="int"/></bean><bean class="redis.clients.jedis.JedisShardInfo"><constructor-arg index="0" value="192.168.1.119" type="String"/><constructor-arg index="1" value="Shard-3" type="String"/>       <constructor-arg index="2" value="6579" type="int"/><constructor-arg index="3" value="2000" type="int"/><constructor-arg index="4" value="1" type="int"/></bean><bean class="redis.clients.jedis.JedisShardInfo"><constructor-arg index="0" value="192.168.1.119" type="String"/><constructor-arg index="1" value="Shard-4" type="String"/>       <constructor-arg index="2" value="6679" type="int"/><constructor-arg index="3" value="2000" type="int"/><constructor-arg index="4" value="2" type="int"/></bean></list>           </constructor-arg>
</bean>

3.编写测试代码

@Test
public void basicOpTestForSharded(){ShardedJedis jedis = shardedJedisPool.getResource();long begin = System.currentTimeMillis();    for(int i=0;i<10000; i++){jedis.set("person." + i + ".name", "frank");jedis.set("person." + i + ".city", "beijing");String name = jedis.get("person." + i + ".name");String city = jedis.get("person." + i + ".city");assertEquals("frank",name);assertEquals("beijing",city);jedis.del("person." + i + ".name");Boolean result = jedis.exists("person." + i + ".name");assertEquals(false,result);result = jedis.exists("person." + i + ".city");assertEquals(true,result);}long end = System.currentTimeMillis();    for(Jedis myJedis: jedis.getAllShards()){System.out.println("redis shard: " + myJedis.getClient().getHost() + ":" +            myJedis.getClient().getPort());System.out.println("redis shard size: " + myJedis.dbSize());}System.out.println("total time: " + (end-begin)/1000);jedis.close();
} 

4.运行代码结果
钱
可以看到,最终的10000个键值,被合理分配到四个Redis实例中,由于Shard-4的weight权重是其它三个的1倍,我们看到,分配给Shard-4节点的键值数也大致是其它三个的1倍,整个键值数比例基本符合1:1:1:2。
求
Redis Sharding采用客户端Sharding方式,服务端Redis还是一个个相对独立的Redis实例节点,没有做任何变动。同时,我们也不需要增加额外的中间处理组件,这是一种非常轻量、灵活的Redis多实例集群方法。

当然,Redis Sharding这种轻量灵活方式必然在集群其它能力方面做出妥协。比如扩容,当想要增加Redis节点时,尽管采用一致性哈希,毕竟还是会有key匹配不到而丢失,这时需要键值迁移。

作为轻量级客户端sharding,处理Redis键值迁移是不现实的,这就要求应用层面允许Redis中数据丢失或从后端数据库重新加载数据。但有些时候,击穿缓存层,直接访问数据库层,会对系统访问造成很大压力。有没有其它手段改善这种情况?

Redis作者给出了一个比较讨巧的办法--presharding,即预先根据系统规模尽量部署好多个Redis实例,这些实例占用系统资源很小,一台物理机可部署多个,让他们都参与sharding,当需要扩容时,选中一个实例作为主节点,新加入的Redis节点作为从节点进行数据复制。数据同步后,修改sharding配置,让指向原实例的Shard指向新机器上扩容后的Redis节点,同时调整新Redis节点为主节点,原实例可不再使用。

presharding是预先分配好足够的分片,扩容时只是将属于某一分片的原Redis实例替换成新的容量更大的Redis实例。参与sharding的分片没有改变,所以也就不存在key值从一个区转移到另一个分片区的现象,只是将属于同分片区的键值从原Redis实例同步到新Redis实例。
q4
并不是只有增删Redis节点引起键值丢失问题,更大的障碍来自Redis节点突然宕机。在《Redis持久化》一文中已提到,为不影响Redis性能,尽量不开启AOF和RDB文件保存功能,可架构Redis主备模式,主Redis宕机,数据不会丢失,备Redis留有备份。

这样,我们的架构模式变成一个Redis节点切片包含一个主Redis和一个备Redis。在主Redis宕机时,备Redis接管过来,上升为主Redis,继续提供服务。主备共同组成一个Redis节点,通过自动故障转移,保证了节点的高可用性。则Sharding架构演变成:
q5
Redis Sentinel提供了主备模式下Redis监控、故障转移功能达到系统的高可用性。下面我们搭建一主一从并利用Sentinel进行监控。

1.搭建主从架构,一主一从

主端口号是6379,从端口号是6479,此步略,参看redis持久性一文。

2.构建Sentinel系统

Redis Sentinel其实也是Redis,只不过是以Sentinel模式启动。在Sentinel模式下,Redis只接受有限的几个命令,主要是监控Redis实例是否发生故障,在主Redis发生故障的前提下,进行故障转移,在可用从Redis实例中挑选一个上升为主Redis,同时其它从Redis的主Redis重定向到这个新的主Redis。原有故障的主Redis如重新上线,也会降级为从Redis,指向新的主Redis。

这样看来,Sentinel又成为一个关键的节点,如果Sentinel节点发生故障,那整个HA高可用将变成不可用,故通常情况下,Sentinel本身是处于集群状态的。

多个Sentinel实例集群,那由谁执行故障转移呢?这需要选举一个Sentinel作为主Sentinel,如果一半以上同意,这个Sentinel将选举为主Sentinel负责执行故障转移操作。故一般Sentinel集群为单数,如3个,2个Sentinel集群是无效的。

在本例中,我们只搭建一个Sentinel实例作为监控节点。

Sentinel启动需要指定配置文件,我们来看下sentinel.conf中几个主要参数:

port 26379 监控系统端口号

sentinel monitor Shard-1 192.168.1.146 6379 1 监控名为Shard-1的节点,且其主Redis的IP是192.168.1.146,端口号为6379,同时有1个Sentinel认为其下线,那此主Redis就认为是有效的客观下线状态,需要执行故障转移。

sentinel down-after-milliseconds Shard-1 30000 sentinel实时监控主Redis,如发现30秒没反应,则主观认为其已下线。

sentinel parallel-syncs Shard-1 1 故障转移,从Redis重新同时指向新主Redis的个数。

sentinel failover-timeout Shard-1 180000 故障转移超时判定,缺省3分钟

如下命令启动sentinel:

redis-sentinel /etc/sentinel.conf >> /var/log/sentinel.log &

查看sentinel.log,日志如图:
asda
3.故障转移测试

我们试着shutdown掉主Redis,看看sentinel和从redis反应:
图片描述
我们看到,Shard-1的主Redis从6379这个实例转到了6479这个实例
图片描述
可以看到,6479这个Redis实例由从升级到主,系统完成了自动故障转移。

我们再重新启动6379原主Redis, 查看日志:(<a href="http://www.dztcsd.com/">资质代办</a>)
图片描述
此时,这个Redis已降级为从Redis,同步6479主Redis。

Jedis提供了JedisSentinelPool类可以访问Sentinel监控的主从Redis组成的节点。在我们的架构方案中,它只是作为一个分片节点如Shard-1存在。Jedis并没有提供分片节点是主从模式下的驱动,好在Jedis是开源产品,我们可以根据JedisSentinelPool主逻辑方式得到各分片的最新主Redis信息,这就组成了ShardedJedisPool所需要的JedisShardInfo列表参数,然后按照JedisSentinelPool重新初始化pool的方式重新初始化ShardedJedisPool中的pool。
图片描述
高访问量下,即使采用Sharding分片,一个单独节点还是承担了很大的访问压力,这时我们还需要进一步分解。通常情况下,应用访问Redis读操作量和写操作量差异很大,读常常是写的数倍,这时我们可以将读写分离,而且读提供更多的实例数。

可以利用主从模式实现读写分离,主负责写,从负责只读,同时一主挂多个从。在Sentinel监控下,还可以保障节点故障的自动监测。这时,上述sharding架构下每个单节点进一步演化为一主多从。如下:
图片描述
同样,Jedis没有提供Sharding状态下一主多从节点的访问驱动,我们还是根据ShardedJedisPool和JedisSentinelPool源码实现机理做相应改造,从sentinel那里得到可用从redis实例信息,并将读相关操作按照一定算法合理分配到这些可用从Redis节点,分担主节点压力。



本文出自慕课网,转载请注明出处,侵权必究。
作者: 手插口袋_ 
来源:慕课网

http://chatgpt.dhexx.cn/article/eDd3dokf.shtml

相关文章

大数据互联网架构阶段 Redis(二)

Redis(二) 零 、 目录 将缓存引入电商项目主从复制哨兵模式集群容忍度CAP理论 十、 将缓存引入电商项目 使用Spring框架维护Jedis池对象 引入一个配置文件 application-redis.config <beans xmlns"http://www.springframework.org/schema/beans"xmlns:context&…

大数据下Redis的应用

大数据下Redis的应用 1、Redis客户端区别 1.1 redis常用客户端 目前市面上比较流行的客户端有jedis、lettuce、redisson •jedis jedis客户端连接方式是基于TCP阻塞方式 •lettuce lettuce内部是基于netty的多路复用异步非阻塞方式&#xff08;目前业界解决高并发大数据的问…

redis互联网真实应用场景

Redis 1.数据类型&#xff08;5种&#xff09; String、hash、list、set、zset 2.常用命令 2.1 String 分布式锁 SETNX 当别的进程枷锁后 讲不能获得锁 利用INCR来产生分布式序列号&#xff0c;优化策略&#xff1a;一下拿出多个序列号到服务器本地&#xff0c;之后在服务器…

大规模互联网应用Redis架构要点(转)

通常&#xff0c;为了提高网站响应速度&#xff0c;总是把热点数据保存在内存中而不是直接从后端数据库中读取。Redis是一个很好的Cache工具。大型网站应用&#xff0c;热点数据量往往巨大&#xff0c;几十G上百G是很正常的事儿&#xff0c;在这种情况下&#xff0c;如何正确架…

Redis 为什么在互联网应用中广泛应用?

1. redis 都能干嘛 缓存&#xff0c;毫无疑问这是Redis当今最为人熟知的使用场景。再提升服务器性能方面非常有效&#xff1b;排行榜&#xff0c;在使用传统的关系型数据库&#xff08;mysql oracle 等&#xff09;来做这个事儿&#xff0c;非常的麻烦&#xff0c;而利用Redis…

从 Redis7.0 发布看 Redis 的过去与未来

前言 经历接近一年的开发、三个候选版本&#xff0c;Redis 7.0终于正式发布&#xff0c;这是Redis历史上改变最多的一个大版本&#xff0c;它不仅包含了50多个新命令&#xff0c;还有大量核心新特性与改进&#xff0c;这些不仅能够解决用户使用中的诸多问题&#xff0c;还进一…

Redis简介

​强烈推荐一个大神的人工智能的教程&#xff1a;http://www.captainai.net/zhanghan 【前言】 组长反复提到系统加载的慢性能有待提高&#xff0c;正好这两天任务都提前完成&#xff1b;于是乎开始着手研究Redis&#xff1b;经过两天的研究将Redis应用到了系统中并做了相关的…

旅游专题图制作

一 数据源 从网上下载的全国各行政区域矢量图层、福建省九市矢量边界图、福建省公路矢量图、福建省河流矢量图、福建省铁路矢量图&#xff0c;其中&#xff0c;它们的投影坐标系为WGS84投影坐标系以及福建省2018旅游景点图。 二&#xff0e;专题地图设计过程 1.打开Arcgis 10…

使用Arcgis制作的专题地图

使用Arcgis制作的专题地图 一、数据准备二、简单制作1. 将数据拉入图层2. 切换视图3. 插入标题4. 插入图例5. 插入指北针6. 插入比例尺 三、精细制作1. 插入文本2. 插入动态文本3. 调整元素4. 调整符号5. 开启格网 五、结果图六、导出地图 一、数据准备 自己准备数据 二、简单…

ArcGIS Pro 专题图制作

基于昨天已经符号化好的地图基础之上&#xff0c;今天主要操作一下专题图制作。ArcGIS Pro中专题图制作和Map中已经有了很大的改动&#xff0c;只要能够习惯ArcGIS Pro制图的方式&#xff0c;那制作的专题图比Map还是更加漂亮的&#xff0c;因为它提供的元素样式&#xff0c;文…

ArcGIS 10.5专题地图制作自定义漂亮图框

先来看一下效果: 下面来说明如何在ArcGIS软件里面实现自定义图框。 1. 【自定义】→【样式管理器】→【Administrator.style】→【边框】→【新建】→【常规边框】。 2. 点击【更改符号】。 3. 点击【编辑符号】。

利用envi与arcmap/arcgisPro制作一张植被覆盖指数专题地图(地图学作业)

利用envi与arcmap/arcgisPro制作一张植被覆盖指数专题地图&#xff08;地图学作业&#xff09; 前言数据准备利用envi进行数据处理Lc8数据的打开辐射定标图像裁剪大气校正Band Math获取NDVI计算植被覆盖指数 ArcMap or ArcGIS Pro成图导入tif文件并将其可视化 前言 本篇文章是…

ArcGIS制作专题地图

终于制出我想要的效果地图了 一顿摸索 从昨天到现在 呵呵 功夫不负有心人 特别是九段线数据框 因为完全是自己摸索 还是小小激动下 也记录一下 上图

QGIS创建专题地图

支持创建多种专题图&#xff0c;单符号专题图、分类专题图、渐进专题图&#xff08;数值分段&#xff09;、自定义规则专题图等、热力图、汇聚图、2.4D三维图&#xff1b;以分类专题图制作为例&#xff0c;操作如下&#xff1a; 制作专题地图 选择符号页签&#xff1b;2.选择…

如何制作专题地图集

你可能看到过类似下图的地图&#xff0c;作为一个GISer&#xff0c;有没有想过这些图是怎么做出来的。对ArcMap也很熟悉了&#xff0c;但是看到这些地图&#xff0c;第一感觉是用PS或是AI做的。即使ArcMap可以做出这些图&#xff0c;也是一个很麻烦的过程&#xff0c;或许Pytho…

ArcGIS应用基础4 专题图的制作

&#x1f388;&#x1f388;&#x1f388;本文数据免费下载 &#x1f3af;&#x1f3af;&#x1f3af;其他GIS空间分析文章 本次实验数据基于上次实验3结果进行处理&#xff0c;文章见ArcGIS应用基础3 人口密度图的制作 一、实验名称 ArcGIS应用基础之专题图的制作 二、实验目…

(附word操作以及视频讲解)使用ARCGIS进行地图配准_投影变换_普通地图制作_专题地图制作

目录 1、这里需要说明的是&#xff0c;视频是由成都信息工程大学龙银平老师制作&#xff0c;Word是由本人编辑。 2、外加 实验指导书 3、链接&#xff1a;https://pan.baidu.com/s/1XgULO8zy5fqlbC1aUXSiLg 提取码&#xff1a;Love 4、部分成果展示&#xff1a; 5. 普通地…

如何制作专题地图(设计、规划、测绘制作图斑)标绘使用详解

同步视频教程&#xff1a;制作专题地图-设计规划测量地图应用详解-Bigemap GIS Office 专题地图制作视频教程&#xff1a;地图数据应用&#xff08;地形图制作过程&#xff09;-Bigemap GIS Office 1、导入/导出AutoCAD文件DXF格式 2、在线标注含有 点 线 面 的矢量标注&#x…

专题地图符号设计与构图

专题地图符号设计是地图编制与可视化的重点内容之一。 一、符号设计应遵循的原则 符号设计应遵循多种原则&#xff0c;包括图案化、精确性、逻辑性、系统性、对比性和协调性、色彩的象征性、视力与制印条件、印刷与经济效果和计算机制图的需要等。 图案化 地图符号图形的设计…

SuperMap iDesktop 9D 制作专题地图

1.打开SuperMap iDesktop 9D SuperMap iDesktop 9D可以在官网下载 官网&#xff1a;https://www.supermap.com/cn/ 或者 使用百度网盘下载 链接&#xff1a;https://pan.baidu.com/s/1m4ZE_d-jcvG_IgOetcU3QQ 提取码&#xff1a;v446 复制这段内容后打开百度网盘手…