1.浏览器缓存过期机制
1.1 最后修改时间 last-modified
浏览器缓存机制是优化网页加载速度和减少服务器负载的重要手段。以下是关于浏览器缓存过期机制、
Last-Modified
和ETag
的详细讲解:一、
Last-Modified
头部
定义 :
Last-Modified
表示服务器上资源的最后修改时间。作用:用于资源的条件请求,帮助浏览器判断缓存的资源是否是最新的。
工作流程:
浏览器第一次请求资源时,服务器返回资源内容和
Last-Modified
时间。下次请求同一资源时,浏览器发送
If-Modified-Since
头部,值为之前的Last-Modified
时间。服务器比较资源的当前修改时间与
If-Modified-Since
的值:
- 如果资源未修改,返回
304 Not Modified
,浏览器继续使用缓存。- 如果资源已修改,返回新的资源内容和更新后的
Last-Modified
时间。示例:
httpLast-Modified: Wed, 21 Oct 2023 07:28:00 GMT
二、
ETag
头部
定义 :
ETag
(Entity Tag)是服务器为资源生成的唯一标识符,通常是资源内容的哈希值或版本号。作用 :比
Last-Modified
更加精确,用于验证资源是否变化。工作流程:
浏览器第一次请求资源时,服务器返回资源内容和
ETag
值。下次请求同一资源时,浏览器发送
If-None-Match
头部,值为之前的ETag
。服务器比较当前资源的
ETag
与If-None-Match
的值:
- 如果
ETag
未变化,返回304 Not Modified
,浏览器继续使用缓存。
- 如果
ETag
变化,返回新的资源内容和新的ETag
值。示例:
httpETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
五、
Last-Modified
vsETag
- 精确度 :
Last-Modified
仅记录最后修改时间,可能无法检测到在同一秒内的多次修改。ETag
通常基于内容的哈希值,能够更精确地检测到任何变化。- 性能:
- 生成
ETag
可能需要更多的计算资源,尤其是在大规模资源或高频请求的情况下。Last-Modified
相对简单,性能开销较小。- 使用场景 :
- 对于静态资源,
ETag
更加适用。- 对于动态资源,可以结合
Last-Modified
和其他缓存策略使用。六、最佳实践
- 合理设置缓存策略 :
- 对于不经常变化的静态资源,设置较长的
max-age
以充分利用缓存。- 对于经常变化的资源,使用较短的
max-age
或结合验证机制。- 使用
ETag
和Last-Modified
:
- 同时使用两者可以提供更可靠的缓存验证,但需注意服务器的性能开销。
- 如果服务器性能有限,可以选择只使用其中一个。
- 版本化资源 :
- 通过在资源URL中包含版本号(如
style.v1.css
),可以在资源更新时强制浏览器下载新版本,避免缓存问题。七、总结
浏览器缓存机制通过多种HTTP头部字段控制资源的缓存和过期,
Last-Modified
和ETag
是其中重要的验证手段。合理配置这些头部字段,可以显著提升网页性能,优化用户体验,同时有效管理服务器资源。
第一次访问
第二次访问
1.2 Etag 标记
但是如果访问的时间一样的,怎么办?If-Modefied精确到的是秒,要知道的是,计算机一秒中可以干好多事的,比如一秒中修改上千次的图片
bash
# 使用 touch 模拟访问时间是一样的(移走1.jpeg,在重新上传一张图片,重命名为1.jpeg)
[root@Rocky9.4 html]#touch -t 202407020931.48 1.jpeg
第一次访问
第一次访问是由于我将时间设置成一样了,但是因为服务器返回的Etag是新的,而浏览器保存的还是旧的,所以Etag不一致,所以返回状态码是200
第二次访问
第二次访问,Etag也统一了,所以返回了状态码304
1.3 过期时间 expires 和 Cache-Control
一、浏览器缓存机制概述
浏览器缓存通过在本地存储网页资源(如HTML、CSS、JavaScript、图片等),避免每次访问网页时都从服务器重新下载这些资源,从而加快页面加载速度,提高用户体验,同时减少服务器带宽的使用。
明白了,你希望更详细地了解浏览器缓存中的
Expires
和Cache-Control
头部,以及它们之间的关系和具体应用。以下是更深入的讲解:二、
Expires
头部1. 定义与作用
Expires
是一个HTTP响应头,用于指定资源的绝对过期时间。它告诉浏览器在指定的时间之前,可以直接从缓存中使用该资源,而无需向服务器重新请求。2. 格式
Expires
的值是一个绝对的HTTP日期和时间 ,格式为:Wdy, DD Mon YYYY HH:MM:SS GMT
。示例:
httpExpires: Wed, 21 Oct 2025 07:28:00 GMT
3. 使用场景
- 适用于静态资源,如图片、CSS、JavaScript文件,这些资源不经常变化。
- 适合设置较长的缓存时间,减少浏览器对服务器的请求频率,提升加载速度。
4. 缺点
- 使用绝对时间,可能受客户端和服务器时间不同步的影响。
- 当资源更新时,若不改变
Expires
,可能导致浏览器继续使用过期的缓存,出现内容不一致的问题。三、
Cache-Control
头部1. 定义与作用
Cache-Control
是一个更为灵活和强大的HTTP响应头,用于控制缓存策略。它可以替代或补充Expires
头部,提供更精确的缓存控制。2. 常用指令
max-age=秒数
:指定资源在多少秒内被认为是新鲜的。max-age
的优先级高于Expires
。示例:
httpCache-Control: max-age=3600
no-cache
:资源必须在使用前重新验证(即使资源没有过期)。示例:
httpCache-Control: no-cache
no-store
:禁止任何形式的缓存,既不存储请求信息,也不存储响应信息。示例:
httpCache-Control: no-store
public
:响应可被任何缓存区缓存,包括浏览器和中间缓存(如CDN)。示例:
httpCache-Control: public
private
:响应仅为单个用户缓存,不能被共享缓存(如CDN)缓存。示例:
httpCache-Control: private
must-revalidate
:一旦资源过期,必须向服务器验证其有效性。示例:
httpCache-Control: must-revalidate
proxy-revalidate
:与must-revalidate
类似,但仅适用于共享缓存。示例:
httpCache-Control: proxy-revalidate
3. 使用场景
- 动态资源:可以灵活设置缓存策略,如需要频繁更新但又希望利用缓存提升性能的资源。
- 细粒度控制:通过组合多个指令,实现更复杂的缓存策略。
4. 与
Expires
的关系
- 优先级 :当同时存在
Cache-Control: max-age
和Expires
时,Cache-Control
优先级更高。- 推荐使用 :现代浏览器和服务器更推荐使用
Cache-Control
,因为它更灵活且不依赖绝对时间。四、
Expires
与Cache-Control
的对比
特性 Expires
Cache-Control
类型 绝对时间 相对时间及其他缓存指令 格式 HTTP日期格式 指令列表 优先级 低于 Cache-Control
高于 Expires
灵活性 较低,只有一个绝对过期时间 高,可以组合多种指令控制缓存行为 推荐使用场景 主要用于向后兼容旧浏览器 现代Web应用的首选缓存控制方式 五、实际应用示例
1. 设置长时间缓存(适用于不经常变化的静态资源)
httpCache-Control: public, max-age=31536000 Expires: Wed, 21 Oct 2025 07:28:00 GMT
- 解释:资源可以被公共缓存(如CDN)缓存,且在1年内(31536000秒)不需要重新验证。
2. 设置短时间缓存,需重新验证(适用于可能会频繁更新的资源)
httpCache-Control: no-cache
- 解释:浏览器每次使用缓存前必须向服务器验证资源是否有更新。
3. 禁止缓存(适用于敏感数据)
httpCache-Control: no-store
- 解释:禁止任何形式的缓存,确保每次请求都从服务器获取最新数据。
六、结合
ETag
和Last-Modified
使用缓存验证即使设置了
Cache-Control
或Expires
,浏览器在某些情况下仍可能需要验证缓存资源的有效性。此时,ETag
和Last-Modified
提供了有效的验证机制:
ETag
:提供资源的唯一标识符,确保缓存的资源与服务器上的一致。Last-Modified
:记录资源的最后修改时间,供浏览器进行条件请求。示例:
httpCache-Control: max-age=3600, must-revalidate ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
七、最佳实践
- 优先使用
Cache-Control
:
- 由于其灵活性和优先级,现代Web开发中应优先配置
Cache-Control
头部。
- 合理设置
max-age
:
- 根据资源的更新频率,合理设置缓存时间。静态资源可以设置较长时间,动态资源设置较短时间或不缓存。
- 结合使用
ETag
和Last-Modified
:
- 提供双重验证机制,确保缓存的资源始终是最新的。
- 版本化静态资源:
- 通过在资源URL中添加版本号(如
style.v1.css
),确保资源更新时浏览器能够获取到最新版本,避免缓存问题。
- 使用CDN:
- 配合缓存头部,利用内容分发网络(CDN)提升全球范围内的资源加载速度,并有效管理缓存策略。
八、总结
Expires
和Cache-Control
都用于控制资源的缓存和过期,但Cache-Control
提供了更高的灵活性和优先级。ETag
和Last-Modified
是用于缓存验证的强大工具,确保浏览器使用最新的资源。- 最佳实践 是结合使用这些HTTP头部,合理设置缓存策略,提升Web应用的性能和用户体验。
1.4 CDN
CDN(内容分发网络,Content Delivery Network)是一种通过将内容复制并缓存到全球多个地理位置的服务器上,从而加速用户访问速度的技术。它主要的目的是提高网站或应用的性能、稳定性、可扩展性,同时减少服务器负载和带宽消耗。
一、CDN的工作原理
CDN的核心思想是将网站的静态资源(如HTML文件、CSS文件、JavaScript、图片、视频等)缓存到分布在全球的边缘服务器(Edge Servers)上。当用户请求访问某个资源时,CDN会根据用户的地理位置,选择距离用户最近的服务器提供资源,从而减少加载时间和提高访问速度。
1. 资源分发与缓存
- 资源分发:当你将资源上传到CDN服务时,CDN提供商会将这些内容分发到位于世界各地的数据中心。
- 缓存:CDN的服务器会将常用的静态内容缓存到本地存储中,当有新的请求时,如果内容已经存在并且没有过期,则直接返回缓存的内容。
2. 边缘服务器与原始服务器
- 边缘服务器(Edge Server):这些是部署在全球各地的服务器,负责将资源提供给终端用户。用户访问时,通常会被路由到离他们最近的边缘服务器,以减少延迟。
- 原始服务器(Origin Server):原始服务器是网站的源服务器,存储网站的所有内容。如果CDN的边缘服务器没有缓存某个请求的内容,它会从原始服务器获取并返回给用户。
3. 缓存策略
CDN通常会使用一些缓存策略来决定哪些内容需要缓存,以及缓存多久。常见的缓存策略包括:
- 缓存时间(TTL,Time to Live):决定缓存的有效期。例如,静态资源如图片、CSS文件可能会缓存较长时间,而动态内容可能缓存较短时间。
- 缓存控制(Cache-Control) :通过设置HTTP头来控制缓存行为(如
max-age
、no-cache
)。- 动态内容缓存:CDN一般针对动态内容(如用户特定数据、实时信息)使用不同的缓存策略,可能会使用"按需缓存"或"低过期时间"的方式进行处理。
4. 智能路由与负载均衡
CDN通常会根据多个因素(如地理位置、网络负载、带宽等)选择最优的边缘服务器来响应用户请求。这一过程称为智能路由或负载均衡。通过此方式,CDN能够确保用户始终通过最快的路径获取到资源。
二、CDN的优势
- 提高加载速度
- 减少延迟:通过将内容分发到全球多个节点,用户总是能够从离自己最近的节点获取资源,从而大幅减少延迟,提高加载速度。
- 更高的可用性:通过分布式缓存,用户能够在多个服务器之间获取资源,即使某个服务器出现故障,也不会影响服务的可用性。
- 减轻原始服务器负载
- CDN缓存了大量静态内容,减少了原始服务器的直接负担,降低了带宽使用和处理请求的压力。
- 提升网站的可扩展性
- CDN帮助网站应对流量激增,能够在不同地区和时段自动调整资源的分配和流量管理,提供更好的扩展性。
- 增强网站的安全性
- DDoS防护:许多CDN提供DDoS攻击防护,能够通过分布式架构分担攻击流量,从而减轻原始服务器的压力。
- SSL加密:CDN服务提供SSL证书支持,帮助加密数据传输,提升安全性。
- 节省带宽成本
- 通过减少从原始服务器到客户端的流量,CDN有助于降低带宽费用,尤其是对于全球性网站。
- 高可用性和容错性
- CDN通过将资源缓存到多个节点,提升了资源的冗余度。在某个节点出现故障时,流量可以被自动引导到其他正常工作的节点,保证网站的高可用性。
三、CDN的类型
- 静态内容CDN
- 主要缓存静态内容,如图片、JavaScript文件、CSS文件等。通过将这些内容缓存到多个位置,能够加速资源加载速度。
- 动态内容CDN
- 动态内容指的是根据用户请求生成的内容,比如数据库查询结果或用户个性化信息。动态内容通常不缓存,但现代CDN提供商提供了对动态内容的优化方案,通过智能缓存策略加速动态内容的加载。
- 直播和视频流CDN
- 专门用于视频流、直播视频内容的传输,优化了大带宽视频数据的分发和传输。常见的技术包括流媒体协议如 HLS(HTTP Live Streaming)和 DASH(Dynamic Adaptive Streaming over HTTP)。
- 边缘计算CDN
- 这种类型的CDN不仅提供缓存功能,还支持在边缘服务器上执行计算任务。它能够在靠近用户的地方处理请求,提高性能和降低延迟。
四、CDN的工作流程
- 资源上传到CDN :
- 将网站的静态资源上传到CDN供应商的服务器。资源可能会分发到多个全球节点进行缓存。
- 用户请求访问资源 :
- 用户访问网页时,浏览器向CDN发起请求。CDN会根据用户的地理位置,智能选择离用户最近的服务器响应请求。
- 缓存命中与未命中 :
- 如果边缘服务器已缓存该资源(缓存命中),CDN直接返回缓存的内容。
- 如果缓存过期或没有缓存该资源(缓存未命中),CDN会向原始服务器请求资源,并将返回的资源缓存起来供后续用户使用。
- 返回资源给用户 :
- 一旦缓存的资源通过CDN的边缘节点返回给用户,用户的浏览器会在本地缓存该资源,下次访问时,直接从浏览器本地获取。
五、CDN的服务提供商
目前,全球有多个主要的CDN服务提供商,最知名的包括:
- Cloudflare
- 提供免费和收费的CDN服务,支持全球分布的边缘节点,提供DDoS防护和Web应用防火墙(WAF)。
- Akamai
- 全球领先的CDN供应商,服务覆盖范围广,适用于大规模企业和高流量网站,提供强大的内容加速和安全功能。
- Amazon CloudFront
- AWS提供的CDN服务,能够与AWS的其他服务(如S3、EC2等)无缝集成,提供高可扩展性和灵活性。
- Fastly
- 以高性能为特点,支持即时缓存清除和高效的动态内容传输,适用于对延迟要求极高的应用。
- KeyCDN
- 提供较为简单和成本效益高的CDN解决方案,适用于中小型网站。
六、CDN的优化策略
- 合理设置缓存过期时间 :
- 根据内容的更新频率,合理设置缓存过期时间(TTL),避免缓存过期导致频繁访问原始服务器。
- 使用分布式缓存 :
- 利用CDN的全球节点分布,将内容缓存到多个节点,从而提供更好的负载均衡和冗余。
- 压缩和优化内容 :
- 对资源进行压缩(如图片、CSS、JavaScript等),减少传输的数据量,提高加载速度。
- 结合HTTPS加密 :
- 使用CDN的SSL证书加密功能,为网站提供HTTPS支持,提升数据传输的安全性。
七、总结
CDN是一种通过将网站内容分发到全球多个节点,减少延迟、提高加载速度、减轻服务器负载的技术。它不仅能加速资源的交付,还能提高网站的安全性、可用性和可扩展性。随着互联网应用的增长,CDN已成为优化网站性能和提供全球用户良好体验的重要工具。
1.4.1 用户请求CDN流程
用户请求CDN资源的流程可以分为几个步骤。这个流程涉及到用户如何向CDN发起请求,CDN如何决定从哪个服务器提供资源,以及缓存如何影响响应时间。以下是详细的用户请求CD能资源的流程:
一、请求流程概述
- 用户发起请求:用户的浏览器或应用程序向服务器请求某个资源(如图片、CSS、JavaScript文件等)。
- DNS解析:请求首先通过DNS解析,将资源的域名解析为CDN的IP地址。
- 路由到CDN边缘节点:用户的请求被路由到距离用户最近的CDN边缘节点。
- 边缘节点缓存检查:CDN的边缘节点检查缓存中是否已有该资源。
- 缓存命中或未命中:根据缓存的情况,决定是直接返回缓存的内容,还是从源服务器获取最新的资源。
- 返回资源给用户:资源通过边缘节点传输给用户,用户的浏览器接收并展示。
二、详细步骤
1. 用户发起请求
用户在浏览器中输入网址或点击链接时,浏览器会发起HTTP请求来请求某个资源。这些资源通常是静态文件,如HTML、CSS、JavaScript文件,或者图片、视频等媒体文件。
例如,用户请求资源:
https://www.example.com/images/logo.png
。2. DNS解析
用户请求的域名(如
www.example.com
)会通过DNS解析,转化为一个IP地址。通常,这个域名已经指向CDN提供商的域名解析系统。
- 传统方式:直接访问原始服务器的IP。
- CDN方式:DNS解析返回的是CDN边缘服务器的IP,而不是源服务器的IP。
CDN提供商通常会在多个地理位置部署多个边缘节点(edge node),当请求发起时,DNS会返回离用户最近的CDN边缘节点的IP地址,确保请求被路由到最近的服务器。
3. 请求被路由到CDN边缘节点
DNS解析完成后,浏览器向CDN的边缘节点发送请求。CDN边缘节点是部署在全球各地的服务器,它们缓存了资源内容,能够快速响应用户请求。
CDN边缘节点的选择通常由以下因素决定:
- 地理位置:用户的IP地址与边缘节点的地理位置之间的距离,尽可能选择距离用户最近的节点。
- 网络负载:当前边缘节点的负载情况。如果某个节点过载,CDN会选择其他负载较低的节点。
4. 边缘节点缓存检查
边缘节点收到请求后,会检查缓存中是否已有该资源。这一步称为缓存命中检查。
- 缓存命中:如果边缘节点缓存中已经存在该资源,并且资源没有过期,则直接从缓存中读取并返回给用户。
- 缓存未命中:如果缓存中没有该资源,或者资源已经过期,则会将请求转发给源服务器(origin server)。
5. 缓存命中或未命中
缓存命中:如果资源已经存在并且有效,CDN会直接将缓存的资源返回给用户。这是加速访问的关键步骤,因为用户不需要访问源服务器,节省了时间和带宽。
例如,若用户请求
https://www.example.com/images/logo.png
,CDN的边缘节点可能已经缓存了这个文件,且TTL(过期时间)没有到期,此时CDN直接返回文件。缓存未命中:如果缓存中没有该资源,或者缓存的资源已经过期,CDN会向源服务器发起请求以获取资源。
6. 从源服务器获取资源
当缓存未命中时,CDN边缘节点会向原始服务器(origin server)请求该资源。此时,源服务器会根据请求返回最新的资源,并且将该资源缓存到边缘节点,以供下次请求使用。
- 资源返回后,CDN会缓存到边缘节点并设置适当的缓存过期时间(TTL)。这意味着下一次请求时,边缘节点可以直接返回缓存的内容,而不需要再访问源服务器。
7. 返回资源给用户
无论是缓存命中还是从源服务器获取资源,最终,CDN的边缘节点会把响应数据返回给用户的浏览器。用户的浏览器从CDN边缘节点接收到资源,并进行展示。
8. 浏览器缓存
在资源返回给浏览器后,浏览器也会根据响应头(如
Cache-Control
、Expires
等)进行本地缓存,以便在下一次访问时直接从本地缓存中获取资源,而不再发送请求到CDN或源服务器。三、缓存策略与内容更新
CDN中的缓存策略非常关键,它决定了缓存内容的过期时间、更新方式以及缓存策略的灵活性。
- TTL(Time to Live,生存时间)
- 每个缓存的资源都会设置一个TTL,TTL指定了该资源在CDN边缘节点缓存的有效期。TTL过期后,缓存的内容会被认为是过期的,需要重新向源服务器请求内容。
- 缓存清除
- 主动清除:CDN提供商允许通过管理控制台或API来主动清除缓存中的某些资源。这对于资源更新频繁或紧急更新的情况非常重要。
- 自动清除:当资源的TTL到期时,CDN会自动清除缓存并向源服务器请求新的内容。
- 缓存验证
- 使用
ETag
和Last-Modified
等HTTP头部字段,CDN可以验证缓存是否有效。即使TTL未到期,CDN也可以通过向源服务器发送条件请求(If-None-Match
或If-Modified-Since
)来判断缓存是否需要更新。四、CDN的优势
- 减少延迟:用户总是能从离自己最近的边缘服务器获取资源,减少了传输延迟。
- 提高可用性:即使源服务器宕机,CDN仍可以从其他节点提供缓存的内容,保持服务可用。
- 减轻源服务器负担:通过缓存大量请求,CDN能够减轻源服务器的负载,减少带宽消耗。
- 提高网站性能:加速资源加载,提升用户体验,尤其是对于全球用户。
五、CDN请求流程示意图
用户请求 --> DNS解析 --> CDN边缘节点 --> 缓存检查 --> | | | 缓存命中 缓存未命中 | | | | 返回缓存的资源 从源服务器请求资源 | | | | 返回给用户的资源 缓存资源并返回给用户 |
六、总结
- CDN工作流程:CDN通过将资源分发到多个边缘节点,利用智能路由、缓存和负载均衡技术,将资源快速交付给用户,减少延迟,提高网站性能。
- 缓存命中与未命中:CDN根据缓存策略决定是否直接返回缓存的内容,或者向源服务器请求更新内容。
- 浏览器与CDN缓存:浏览器本地缓存和CDN的缓存共同工作,确保资源加载更快,减少重复请求。
CDN在提高网站性能、增强网站可用性、降低带宽消耗等方面发挥了重要作用,是现代Web应用不可或缺的组成部分。
1.4.2 CDN分层缓存
CDN(Content Delivery Network,内容分发网络)的分层缓存(Layered Caching)是指通过多级缓存架构有效提升内容分发效率的一种策略。在CDN中,请求的内容通常会经过多个层级的缓存节点,以实现更佳的性能和资源利用率。整个流程通常可以分为以下几个层次:
- L1 边缘节点缓存(Edge Cache) :
这是离用户最近的一层缓存节点。当用户向CDN请求内容时,边缘节点首先检查本地缓存是否已存有该内容。若存在并未过期,便直接从该节点返回内容给用户,降低传输延迟,提高用户体验;若缓存中无此内容或内容已过期,则向上层的缓存节点或源站请求。- L2 区域或中间层缓存(Mid-Tier/Regional Cache) :
当边缘节点未能在本地拿到所需内容时,会将请求向上层的区域缓存节点发出。区域缓存通常位于更靠近源站的核心网络,储存那些在一定时间窗口内被多个边缘节点重复请求的内容。通过在此层进行缓存,CDN减少了向源站多次重复请求同一内容的频率。这一层有助于将热门内容在更广的地理范围内进行共享,降低源站负载,并减少跨区域的回源请求延迟。- 源站(Origin Server) :
当所有中间层缓存与边缘缓存均无请求内容时,才会到达最终的源站。源站是内容的原始出处,CDN会从这里获取最新版本的内容,然后将其分发给请求用户,并在适当的层级缓存节点中储存副本,以便满足未来类似请求。分层缓存的工作原理
以下是一个典型的用户请求过程:
- 用户访问网站,请求某个资源(例如一张图片)。
- 用户的DNS解析请求将用户导向离他最近的L1边缘节点。
- L1节点检查自身是否缓存了该资源。
- 如果有,则直接将资源返回给用户,请求结束。这称为"缓存命中"。
- 如果没有,则L1节点向其上层的L2区域节点发起请求。
- L2节点执行相同的检查,查看自身是否缓存了该资源。
- 如果有,则将资源返回给L1节点,L1节点再将其返回给用户。同时,L1节点也会缓存该资源,以便下次相同的请求可以直接命中。
- 如果没有,则L2节点继续向上,向源站发起请求。
- 源站将资源返回给L2节点,L2节点再返回给L1节点,L1节点最终返回给用户。L1和L2节点都会缓存该资源。
分层缓存的优势
- 减轻源站压力: 通过多层缓存,大部分用户请求都可以在L1或L2节点得到满足,大大减少了回源站的请求数量,从而减轻了源站的负载。
- 提高缓存命中率: 分层结构使得更常用的内容可以缓存在更靠近用户的L1节点上,从而提高整体的缓存命中率,减少用户访问延迟。
- 降低网络拥塞: 由于大量请求在CDN内部完成,减少了跨区域和跨运营商的网络传输,有助于缓解网络拥塞。
- 更好的可扩展性: 分层结构使得CDN系统更容易扩展,可以通过增加L1和L2节点来应对不断增长的用户访问量。
分片缓存(Chunked Caching)
在某些情况下,CDN还会使用分片缓存技术,将大文件(例如视频文件)分割成多个小片段(chunks),然后分别缓存这些片段。当用户请求文件时,CDN只需传输用户需要的片段,而不是整个文件。这对于提高大文件传输效率和支持流媒体播放非常有用。
总结
CDN分层缓存是一种有效的提高网站性能和用户体验的技术。通过合理地组织和管理多层缓存节点,CDN可以更好地分配资源,提高缓存命中率,并减轻源站的压力。
2.Redis 安装及连接
Redis简介 :
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息队列中间件。它以Key-Value形式存储数据,提供多种数据结构和丰富的功能特性。Redis的核心价值在于高速访问、简单的数据操作模型以及丰富的数据结构支持,使其在需要快速读写、实时计算和高并发的场景中表现突出。
Redis的主要特性:
内存存储 :
Redis将数据存储在内存中,从而达到非常高的访问速度(读写操作通常在微秒级别)。这使其在对实时性要求高的场景(如会话存储、实时排行、实时计数器等)表现优异。
多种数据结构支持 :
相较于传统的Key-Value存储仅支持字符串,Redis支持多种丰富的数据结构类型,这些数据结构以简单命令即可操作:
- String(字符串):最基础的数据结构,可存储普通字符串、数字、二进制数据。
- Hash(哈希):类似于Key-Value映射的集合,可方便存储对象属性并对属性进行增删改操作。
- List(列表):双端链表实现,支持从头尾插入、弹出元素,可用来实现消息队列、任务列表等功能。
- Set(集合):无序集合结构,支持求交集、并集和差集等集合运算,常用于去重、标签管理。
- Sorted Set(有序集合):每个元素会关联一个分数(score),Redis会根据分数对元素进行排序,可用于排行榜、延时队列等场景。
- Bitmap (位图)、HyperLogLog 、Geo(地理位置)等特殊数据类型:满足统计计数、地理位置查询等特殊需求。
持久化能力 :
虽然Redis是内存数据库,但它并非易失性存储。Redis提供两种持久化机制,让数据在断电后仍能恢复:
- RDB(Redis Database Backup):定时生成内存快照并持久化到磁盘,恢复速度快,数据略有延迟。
- AOF(Append Only File):将每次写操作以日志的形式追加到文件中,数据恢复更为完整,可根据策略对AOF文件进行定期重写压缩。
可以根据业务需求选择合适的持久化方案,或同时开启RDB和AOF实现数据安全与高效率的折中。
高可用与分布式 :
Redis提供主从复制(Master-Slave Replication)实现数据的多份冗余,主节点负责写操作,从节点同步主节点的数据,提供读取分流和故障切换。当主节点出现故障时,可手动或借助Redis Sentinel(哨兵)实现自动故障转移。
对于更大规模的数据集与访问压力,Redis Cluster可以将数据分片至多个节点,提升整体存储能力和吞吐性能。
事务支持 :
Redis提供简单的事务机制(MULTI/EXEC命令),可以将一组操作打包,保证这些操作的顺序性和原子性。虽然不支持复杂的回滚功能,但事务可以确保一组命令要么都执行要么都不执行。
Lua脚本扩展 :
Redis内置了Lua解释器,用户可以在Redis内原子执行Lua脚本,对数据进行复杂操作,而无需在客户端与Redis之间多次往返,提高复杂操作的性能和一致性。
丰富的使用场景 :
凭借高性能和多数据结构支持,Redis可广泛应用于各种场景:
- 缓存热点数据(例如:热门商品信息、用户会话数据、应用程序配置)
- 消息队列与任务调度(利用List或Stream)
- 实时统计(计数器、排行榜、实时分析)
- 分布式锁(利用SetNx命令实现简单的分布式锁机制)
简单易用的命令行与客户端支持 :
Redis提供简洁直观的命令行客户端和与主流编程语言(如Java、Python、Go、C#等)兼容的客户端库,降低学习成本与集成难度。
总结 :
Redis作为一个内存数据存储系统,具有高性能、丰富的数据类型、灵活的持久化策略以及高可用性架构支持。它在高并发、低延迟与实时处理场景中得到广泛应用,已成为构建现代互联网应用的重要基础组件。
2.1 dnf 安装 Redis
bash
# Rocky 9.4 由系统源提供
[root@redis1.xyy.org ~]#dnf info redis
Name : redis
Version : 6.2.7
Release : 1.el9
Architecture : x86_64
Size : 1.3 M
Source : redis-6.2.7-1.el9.src.rpm
Repository : appstream
Summary : A persistent key-value database
URL : https://redis.io
License : BSD and MIT
Description : Redis is an advanced key-value store. It is often referred to as a data
: structure server since keys can contain strings, hashes, lists, sets and
: sorted sets.
:
: You can run atomic operations on these types, like appending to a string;
: incrementing the value in a hash; pushing to a list; computing set
: intersection, union and difference; or getting the member with highest
: ranking in a sorted set.
:
: In order to achieve its outstanding performance, Redis works with an
: in-memory dataset. Depending on your use case, you can persist it either
: by dumping the dataset to disk every once in a while, or by appending
: each command to a log.
:
: Redis also supports trivial-to-setup master-slave replication, with very
: fast non-blocking first synchronization, auto-reconnection on net split
: and so forth.
:
: Other features include Transactions, Pub/Sub, Lua scripting, Keys with a
: limited time-to-live, and configuration settings to make Redis behave like
: a cache.
:
: You can use Redis from most programming languages also.
[root@Rocky9.4 ~]#
# CentOS 7由 epel 源提供
[root@CentOS7 ~]#yum info redis
Name : redis
Arch : x86_64
Version : 3.2.12
Release : 2.el7
Size : 1.4 M
Repo : installed
From repo : epel
Summary : A persistent key-value database
URL : http://redis.io
License : BSD
[root@redis1.xyy.org ~]#dnf install redis
[root@redis1.xyy.org ~]#systemctl enable --now redis
[root@redis1.xyy.org ~]#pstree -p | grep redis
|-redis-server(4237)-+-{redis-server}(4238)
| |-{redis-server}(4239)
| |-{redis-server}(4240)
| `-{redis-server}(4241)
[root@redis1.xyy.org ~]#
[root@redis1.xyy.org ~]#redis-cl
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> INFO Server
# Server
redis_version:6.2.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:ec192bdd77ecd321
redis_mode:standalone
os:Linux 5.14.0-427.13.1.el9_4.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:11.3.1
process_id:4237
process_supervised:systemd
run_id:37144e0c3a2930dac6148605d26afae8ee4d38ba
tcp_port:6379
server_time_usec:1734486571682241
uptime_in_seconds:37314
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:6433323
executable:/usr/bin/redis-server
config_file:/etc/redis/redis.conf
io_threads_active:0
127.0.0.1:6379>
2.2 编译安装 Redis
从 Redis 官方下载地址 获取稳定版压缩包,如 redis-7.4.0.tar.gz
。
bash
# 1.创建 Redis 用户(不需要登录权限,只是用于运行 Redis 服务以提高安全性)
useradd -r -s /sbin/nologin redis
# 2.获取源码包
wget https://download.redis.io/releases/redis-7.4.0.tar.gz
# 3.解压并进入源码目录
tar xf redis-7.4.0.tar.gz
cd redis-7.4.0
# 4.开始编译(在某些发行版下可开启 USE_SYSTEMD=yes 选项,以生成可与 systemd 交互的可执行文件。)
make -j $(nproc) USE_SYSTEMD=yes
# 5.安装到指定位置
make PREFIX=/apps/redis install
# 6.建立软链接(方便在命令行中使用redis-server、redis-cli)
ln -s /apps/redis/bin/redis-* /usr/bin/
# 7.创建所需目录
mkdir -p /apps/redis/{etc,log,data,run}
# 8.拷贝源码目录中自带redis.conf,拷贝到配置目录:
cp redis.conf /apps/redis/etc/
# 9.redis.conf:修改关键配置
#bind:改为 0.0.0.0 或保留默认看实际需要;
#requirepass:设置 Redis 密码,如 requirepass 123456;
#dir:RDB/快照文件存放目录,一般设为 /apps/redis/data;
#logfile:日志文件路径,如 /apps/redis/log/redis-6379.log;
#pidfile:pid 文件路径,如 /apps/redis/run/redis-6379.pid;
sed -i -e 's/bind 127.0.0.1/bind 0.0.0.0/' \
-e "/# requirepass/a requirepass 123456" \
-e "/^dir .*/c dir /apps/redis/data/" \
-e "/^logfile .*/c logfile /apps/redis/log/redis-6379.log" \
-e "/^pidfile .*/c pidfile /apps/redis/run/redis-6379.pid" \
/apps/redis/etc/redis.conf
# 10.设置文件权限
chown -R redis:redis /apps/redis
# 11.内核与系统参数优化(不优化会有告警)
# 11.1 调整内核参数
vim /etc/sysctl.conf
net.core.somaxconn = 1024
vm.overcommit_memory = 1
sysctl -p
# 11.2 禁用透明大页(THP)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
# 可以写入启动脚本(如 /etc/rc.local 或 /etc/rc.d/rc.local)以在重启后继续生效。
# 12.创建Systemd服务并启动
# CentOS/Rocky:/usr/lib/systemd/system/redis.service
# Ubuntu:/lib/systemd/system/redis.service(或 /etc/systemd/system/redis.service)
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis.conf --supervised systemd
ExecStop=/bin/kill -s QUIT $MAINPID
# 启动notify一定要编译了 USE_SYSTEMD=yes,否则启动服务有问题
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
# 13.刷新并启动服务
systemctl daemon-reload
systemctl enable --now redis
systemctl status redis
# 14.查看Redis版本或者信息
redis-server -v
# 查看服务信息
redis-cli -a 123456 INFO Server
# 测试插入和查询数据
redis-cli -a 123456 set mykey "Hello World"
redis-cli -a 123456 get mykey
bash
#! /bin/bash
#-----------------------------------------------------
#Author: XingYuyu
#Date: 2024-08-12
#Blog: http://8.141.4.74
#Filename: install_redis.sh
#Description: [Online Install Redis for Rocky Linux ,Ubuntu,CentOS ]
#-----------------------------------------------------
VERSION=redis-7.4.0
PASSWORD=123456
INSTALL_DIR=/apps/redis
os_type() {
awk -F'[ "]' '/^NAME/{print $2}' /etc/os-release
}
color() {
RES_COL=80
MOVE_TO_COL="echo -en \e[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \e[1;32m"
SETCOLOR_FAILURE="echo -en \e[1;31m"
SETCOLOR_WARNING="echo -en \e[1;33m"
SETCOLOR_NORMAL="echo -en \e[0m"
echo -n "$1" && $MOVE_TO_COL
echo -n "["
if [ $2 = "success" -o $2 = "0" ]; then
${SETCOLOR_SUCCESS}
echo -n $" OK "
elif [ $2 = "failure" -o $2 = "1" ]; then
${SETCOLOR_FAILURE}
echo -n $"FAILED"
else
${SETCOLOR_WARNING}
echo -n $"WARNING"
fi
${SETCOLOR_NORMAL}
echo -n $"]"
echo
}
install_redis() {
wget https://download.redis.io/releases/${VERSION}.tar.gz || {
color "Redis 源码下载失败" 1
exit
}
tar xf ${VERSION}.tar.gz
cd ${VERSION}
CPUS=lscpu | awk '/^CPU\(s\)/{print $2}'
make -j $CPUS USE_SYSTEMD=yes PREFIX=${INSTALL_DIR} install && color "Redis 编译安装完成" 0 || {
color "Redis 编译安装失败" 1
exit
}
ln -s ${INSTALL_DIR}/bin/redis-* /usr/bin/
mkdir -p ${INSTALL_DIR}/{etc,log,data,run}
cp redis.conf ${INSTALL_DIR}/etc/
sed -i -e 's/bind 127.0.0.1/bind 0.0.0.0/' -e "/# requirepass/a requirepass $PASSWORD" -e "/^dir .*/c dir ${INSTALL_DIR}/data/" -e "/^logfile .*/c logfile ${INSTALL_DIR}/log/redis-6379.log" -e "/^pidfile .*/c pidfile ${INSTALL_DIR}/run/redis-6379.pid" ${INSTALL_DIR}/etc/redis.conf
if id redis &>/dev/null; then
color "Redis 用户已经存在,无需创建" 0
else
useradd -r -s /sbin/nologin redis
color "Redis 用户创建成功" 0
fi
chown -R redis.redis ${INSTALL_DIR}
cat >>/etc/sysctl.conf <<EOF
net.core.somaxconn = 1024
vm.overcommit_memory = 1
EOF
sysctl -p
if [ `os_type` == "Ubuntu" ];then
cat >> /lib/systemd/system/rc-local.service <<EOF
[Install]
WantedBy=multi-user.target
EOF
echo '#!/bin/bash' > /etc/rc.local
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >>/etc/rc.local
chmod +x /etc/rc.local
/etc/rc.local
# Ubuntu 的service文件放在/lib/systemd/system/下或者/etc/systemd/system/下不能放在/usr/lib/下
cat > /lib/systemd/system/redis.service <<EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=${INSTALL_DIR}/bin/redis-server ${INSTALL_DIR}/etc/redis.conf --supervised systemd
ExecStop=/bin/kill -s QUIT \$MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
EOF
else
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >>/etc/rc.d/rc.local
chmod +x /etc/rc.d/rc.local
/etc/rc.d/rc.local
cat > /usr/lib/systemd/system/redis.service <<EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=${INSTALL_DIR}/bin/redis-server ${INSTALL_DIR}/etc/redis.conf --supervised systemd
ExecStop=/bin/kill -s QUIT \$MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
EOF
fi
systemctl daemon-reload
systemctl enable --now redis &>/dev/null
systemctl is-active redis &> /dev/null && color "Redis 服务启动成功,Redis信息如下:" 3 || { color "Redis 启动失败" 1 ;exit; }
#sleep 5
redis-cli -a $PASSWORD INFO Server 2>/dev/null
}
install_CentOS7() {
. /etc/init.d/functions
# jemalloc-devel依赖于epel源
yum -y install epel-release && yum -y install gcc jemalloc-devel systemd-devel || {
color "安装软件包失败,请检查网络配置" 1
exit
}
rpm -q wget &>/dev/null || yum -y install wget &>/dev/null
wget https://download.redis.io/releases/${VERSION}.tar.gz || {
action "Redis 源码下载失败" false
exit
}
tar xf ${VERSION}.tar.gz
cd ${VERSION}
CPUS=lscpu | awk '/^CPU\(s\)/{print $2}'
make -j $CPUS USE_SYSTEMD=yes PREFIX=${INSTALL_DIR} install && action "Redis 编译安装完成" || {
action "Redis 编译安装失败" false
exit
}
ln -s ${INSTALL_DIR}/bin/redis-* /usr/bin/
mkdir -p ${INSTALL_DIR}/{etc,log,data,run}
cp redis.conf ${INSTALL_DIR}/etc/
sed -i -e 's/bind 127.0.0.1/bind 0.0.0.0/' -e "/# requirepass/a requirepass $PASSWORD" -e "/^dir .*/c dir ${INSTALL_DIR}/data/" -e "/^logfile .*/c logfile ${INSTALL_DIR}/log/redis-6379.log" -e "/^pidfile .*/c pidfile ${INSTALL_DIR}/run/redis-6379.pid" ${INSTALL_DIR}/etc/redis.conf
if id redis &>/dev/null; then
action "Redis 用户已经存在" false
else
useradd -r -s /sbin/nologin redis
fi
chown -R redis.redis ${INSTALL_DIR}
cat >>/etc/sysctl.conf <<EOF
net.core.somaxconn = 1024
vm.overcommit_memory = 1
EOF
sysctl -p
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >>/etc/rc.d/rc.local
chmod +x /etc/rc.d/rc.local
/etc/rc.d/rc.local
cat >/usr/lib/systemd/system/redis.service <<EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=${INSTALL_DIR}/bin/redis-server ${INSTALL_DIR}/etc/redis.conf --supervised systemd
ExecReload=/bin/kill -s HUP \$MAINPID
ExecStop=/bin/kill -s QUIT \$MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now redis &>/dev/null
systemctl is-active redis &> /dev/null && ${COLOR}"Redis 服务启动成功,Redis信息如下:"${END} || { ${COLOR}"Redis 启动失败"${END};exit; }
#sleep 5
redis-cli -a $PASSWORD INFO Server 2>/dev/null
}
install_Ubuntu() {
apt -y install make gcc libjemalloc-dev libsystemd-dev || {
color "安装软件包失败,请检查网络配置" 1
exit
}
install_redis
}
install_Rocky() {
# jemalloc-devel依赖于epel源
yum -y install epel-release && yum -y install gcc jemalloc-devel systemd-devel || {
color "安装软件包失败,请检查网络配置" 1
exit
}
rpm -q wget &>/dev/null || yum -y install wget &>/dev/null
install_redis
}
if [ $(os_type) == 'CentOS' ]; then
install_CentOS7
elif [ $(os_type) == 'Rocky' ]; then
install_Rocky
elif [ $(os_type) == 'Ubuntu' ]; then
install_Ubuntu
else
color "未识别的操作系统" 1
fi
2.3 连接到 Redis
2.3.1 客户端连接到 Redis
1.本机无密码连接
bash
redis-cli
2.跨主机无密码连接
bash
redis-cli -h HOSTNAME/IP -p PORT
3.跨主机密码连接
bash
redis-cli -h HOSTNAME/IP -p PORT -a PASSWORD
2.3.2 程序连接 Redis
redis 支持多种开发语言访问
http
https://redis.io/docs/latest/develop/clients/
shell 脚本写入数据到 Redis
bash
#!/bin/bash
NUM=100
PASS=123456
for i in `seq $NUM`; do
redis-cli -h 127.0.0.1 -a "$PASS" --no-auth-warning set key${i} value${i}
echo "key${i} value${i} 写入完成"
done
echo "$NUM 个key写入到Redis完成"
3.Redis 的多实例
在生产环境中,为了更好地利用资源、实现多租户隔离或分离不同业务的数据与配置,运维人员往往会在一台服务器上运行多个 Redis 实例。Redis 的多实例部署并非Redis内建的特性,而是通过为每个实例指定独立的配置文件、独立的运行端口与数据目录来实现的。以下是关于Redis多实例的详细讲解:
为什么需要多实例
- 资源隔离与多租户支持 :
在某些场景下,不同的业务线或不同的用户需要独立的Redis服务,以免数据和性能相互影响。多实例可以为每个业务运行独立的Redis,保证数据和访问流量的隔离。- 不同的配置要求 :
某些业务可能需要不同的持久化策略(RDB或AOF)、内存管理策略或安全设置。多实例部署允许针对每个实例使用单独的配置文件,从而灵活定制每个实例的行为。- 更好地利用硬件资源 :
一台物理机/虚拟机的CPU、内存、网络资源较为充裕时,可以在同一台机器上运行多个Redis实例,充分利用硬件资源。尤其在内存较大时,不同实例分别作为缓存、队列、会话存储使用,可以最大化硬件利用率。配置多实例的关键点
独立的配置文件 :
每个实例都需要一个独立的配置文件(例如
redis-6379.conf
,redis-6380.conf
)。在配置文件中需要注意如下参数:
port
:每个实例必须使用不同的端口,如6379、6380、6381等。pidfile
:每个实例需要独立的PID文件,如/var/run/redis_6379.pid
、/var/run/redis_6380.pid
。logfile
:为每个实例指定独立的日志文件,如/var/log/redis_6379.log
、/var/log/redis_6380.log
。dir
:为每个实例指定独立的数据目录,如/var/lib/redis/6379/
、/var/lib/redis/6380/
,确保RDB或AOF文件不冲突。daemonize yes
:通常在生产中,多实例都以守护进程方式后台运行。利用 systemd 的进程监督能力,即使用--supervised systemd
参数时,必须将daemonize
设为no
。如果将daemonize
设为yes
,则与 systemd 的监督模式相矛盾,导致 Redis 无法正常通过 systemd 进行管理和监控。独立的启动命令 :
启动时为每个实例指定相应的配置文件。常用命令形式:
bashredis-server /path/to/redis-6379.conf redis-server /path/to/redis-6380.conf
确保每个实例正常监听自己的端口并使用自己的配置。
服务管理与守护进程 :
为每个实例创建单独的systemd服务文件或init脚本,方便运维管理。如在systemd中创建
/etc/systemd/system/redis@6379.service
、redis@6380.service
等文件,然后通过systemctl start redis@6379
启动指定实例。安全与访问控制 :
确保为每个实例设置合理的访问控制,如
bind
参数、protected-mode
设置、requirepass
或ACL策略。多实例运行时应确保不同实例的数据和访问策略独立,避免安全隐患。监控与报警 :
多实例运行时需要对每个实例分别进行监控,收集其内存使用、连接数、QPS、延迟、慢查询等指标,并对异常情况及时报警。
举例:多实例文件组织形式
bash/etc/redis/ ├─ redis-6379.conf ├─ redis-6380.conf └─ redis-6381.conf /var/lib/redis/ ├─ 6379/ │ ├─ dump.rdb │ └─ appendonly.aof ├─ 6380/ │ ├─ dump.rdb │ └─ appendonly.aof └─ 6381/ ├─ dump.rdb └─ appendonly.aof /var/log/ ├─ redis_6379.log ├─ redis_6380.log └─ redis_6381.log
总结
Redis多实例部署是通过为每个实例提供独立的端口、独立的配置文件以及数据和日志目录来实现的。这种方式在同一台服务器上实现了灵活的资源分配和多租户支持。通过精心配置和管理,运维人员能够同时运行多个Redis实例,为不同应用提供高效、独立而又经济实惠的内存数据存储服务。
案例:以编译安装为例实现 Redis 多实例
bash
# 生成的文件列表
[root@Rocky9.4 ~]#ll /apps/redis/
total 0
drwxr-xr-x 2 redis redis 134 Dec 17 23:22 bin
drwxr-xr-x 2 redis redis 22 Dec 18 20:04 data
drwxr-xr-x 2 redis redis 24 Dec 18 20:04 etc
drwxr-xr-x 2 redis redis 28 Dec 17 23:22 log
drwxr-xr-x 2 redis redis 28 Dec 18 20:04 run
[root@Rocky9.4 redis]#tree /apps/redis/
/apps/redis/
├── bin
│ ├── redis-benchmark
│ ├── redis-check-aof -> redis-server
│ ├── redis-check-rdb -> redis-server
│ ├── redis-cli
│ ├── redis-sentinel -> redis-server
│ └── redis-server
├── data
│ ├── dump-6379.rdb
│ ├── dump-6380.rdb
│ └── dump-6381.rdb
├── etc
│ ├── redis_6379.conf
│ ├── redis_6380.conf
│ ├── redis_6381.conf
│ └── redis.conf
├── log
│ ├── redis-6379.log
│ ├── redis-6380.log
│ └── redis-6381.log
└── run
├── redis-6379.pid
├── redis-6380.pid
└── redis-6381.pid
5 directories, 19 files
# 配置文件需要修改的地方
vim /apps/redis/etc/redis_6379.conf
bind 0.0.0.0 -::1
port 6379
daemonize no
pidfile /apps/redis/run/redis-6379.pid
logfile /apps/redis/log/redis-6379.log
# 写入数据的时候,并且满足save才会生产dump-6379.rdb这个文件
dbfilename dump-6379.rdb
dir /apps/redis/data/
# 3600秒,写一次数据 300秒,100次数据,60秒,10000次数据 满足就会备份,为了更快的看到效果可以更改,例如:save 60 1
save 3600 1 300 100 60 10000
appendfilename "appendonly-6379.aof"
vim /apps/redis/etc/redis_6380.conf
bind 0.0.0.0 -::1
port 6380
daemonize no
pidfile /apps/redis/run/redis-6380.pid
logfile /apps/redis/log/redis-6380.log
dbfilename dump-6380.rdb
dir /apps/redis/data/
save 3600 1 300 100 60 10000
appendfilename "appendonly-6380.aof"
vim /apps/redis/etc/redis_6381.conf
bind 0.0.0.0 -::1
port 6381
daemonize no
pidfile /apps/redis/run/redis-6381.pid
logfile /apps/redis/log/redis-6381.log
dbfilename dump-6381.rdb
dir /apps/redis/data/
save 3600 1 300 100 60 10000
appendfilename "appendonly-6381.aof"
# 创建service文件
# 1./usr/lib/systemd/system/redis6379.service
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis_6379.conf --supervised systemd
ExecStop=/bin/kill -s QUIT $MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
[root@Rocky9.4 ~]#
# 2./usr/lib/systemd/system/redis6380.service
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis_6380.conf --supervised systemd
ExecStop=/bin/kill -s QUIT $MAINPID
#Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
[root@Rocky9.4 ~]#
# 3./usr/lib/systemd/system/redis6381.service
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=/apps/redis/bin/redis-server /apps/redis/etc/redis_6381.conf --supervised systemd
ExecStop=/bin/kill -s QUIT $MAINPID
#Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
[root@Rocky9.4 ~]#
systemctl daemon-reload
systemctl enable --now redis6379.service redis6380.service redis6381.service
# 这里有个问题,通过二进制安装好的Redis,start的时候用tab键无法补全
4.Redis 持久化
Redis 是一个基于内存的数据结构存储系统,但它提供了多种持久化机制,可以将内存中的数据保存到磁盘中,从而在 Redis 重启或服务器宕机后依然能够恢复数据。Redis 主要提供了两种持久化方式:RDB(Redis Database) 和 AOF(Append Only File)。这两种方式可以单独使用,也可以配合使用,具体选择取决于业务需求(对数据一致性、写入性能、磁盘空间等的不同要求)。
4.1 RDB(Redis Database)
RDB 方式是 Redis 最早的持久化模式,即在某个时间点对内存数据做快照 ,并保存到一个 .rdb
文件中
4.1.1 RDB 的工作机制
方法1:
SAVE 命令是"阻塞式"保存,Redis 不会创建子进程,而是直接由主进程把内存数据写到 RDB 文件里。
bash
[root@Rocky9.4 redis]#( redis-cli -a 123456 save & );pstree -p | grep redis-server;ls /apps/redis/data/ -lh
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
|-redis-server(28847)-+-{redis-server}(28854)
| |-{redis-server}(28855)
| |-{redis-server}(28856)
| |-{redis-server}(28857)
| `-{redis-server}(28858)
total 180M
-rw-r--r-- 1 redis redis 180M Dec 23 18:48 dump_6379.rdb
-rw-r--r-- 1 redis redis 48K Dec 23 21:45 temp-28847.rdb
使用 python 脚本存入一千万条数据,再进行备份看到下面的现象
python
# 这个需要使用pip install redis来安装redis包
import redis
pool=redis.ConnectionPool(host="10.0.0.41",port=6379,password="123456")
r=redis.Redis(connection_pool=pool)
for i in range(10000000):
r.set("k%d" % i,"v%d" % i)
data=r.get("k%d" % i)
print(data)
方法2:
BGSAVE 才是"后台"保存,Redis 会 fork 一个子进程来完成 RDB 持久化,主进程继续对外提供服务。
bash
[root@Rocky9.4 data]#redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379>
# 生产临时文件,fork 子进程 pid是920,从temp-920.rdb也可以看出是进程920在备份
[root@Rocky9.4 data]#pstree -p | grep redis-server;ls /apps/redis/data/ -lh
|-redis-server(638)-+-redis-server(920)
| |-{redis-server}(666)
| |-{redis-server}(667)
| |-{redis-server}(668)
| |-{redis-server}(669)
| `-{redis-server}(671)
total 128M
-rw-r--r-- 1 redis redis 67M Dec 24 14:43 temp-920.rdb
# 备份结束以后,将文件重命名
[root@Rocky9.4 data]#pstree -p | grep redis-server;ls /apps/redis/data/ -lh
|-redis-server(638)-+-{redis-server}(666)
| |-{redis-server}(667)
| |-{redis-server}(668)
| |-{redis-server}(669)
| `-{redis-server}(671)
total 180M
-rw-r--r-- 1 redis redis 180M Dec 24 14:43 dump_6379.rdb
# 也可以查看日志
[root@Rocky9.4 data]#tail -f ../log/redis-6379.log
# bgsave的日志,会显示出具体的子进程编号
638:M 24 Dec 2024 15:15:14.746 * Background saving started by pid 1037
1037:C 24 Dec 2024 15:15:22.016 * DB saved on disk
1037:C 24 Dec 2024 15:15:22.026 * Fork CoW for RDB: current 0 MB, peak 0 MB, average 0 MB
638:M 24 Dec 2024 15:15:22.095 * Background saving terminated with success
# save的日志
638:M 24 Dec 2024 15:20:09.364 * DB saved on disk
方法3:
Redis 会在配置文件中设置触发 RDB 生成快照的条件,典型配置示例:
bashsave 900 1 # 在900秒内(15分钟)至少有1个键发生变动,则触发保存快照 save 300 10 # 在300秒内(5分钟)至少有10个键发生变动,则触发保存快照 save 60 10000# 在60秒内(1分钟)至少有10000个键发生变动,则触发保存快照 # 上面是之前老版本的写法,写在新版本的写法: save 3600 1 300 100 60 10000
触发快照后,Redis 通过fork 出一个子进程,子进程负责将内存数据写入临时的 RDB 文件;父进程继续处理客户端请求,因此在快照生成的过程中,Redis 仍然能够服务读写请求。
当子进程将数据写完后,会原子性地用临时文件替换原先的 RDB 文件,以确保在替换前的 RDB 文件依然可用。这个和bgsave的模式是一样的.
4.1.2 RDB 的优点
- 性能开销小:由于生成快照时是通过 fork 子进程来执行,主进程只需做少量工作,对性能影响较小。
- 适合做冷备:如果业务允许一定程度的数据丢失(因为 RDB 只能反映生成快照时的数据状态),那么 RDB 非常简洁且容易做冷备份与全量备份。
- 启动速度快:从 RDB 文件进行数据恢复时,因为只是加载一个快照文件,启动速度通常比较快。
4.1.3 RDB 的缺点
- 可能丢失数据 :快照通常并不会很频繁地生成(除非你把
save
指令配置得极短,这会带来极大的性能损耗),所以在两次快照之间发生的数据写操作可能会丢失。 - fork 开销 :在大数据量时执行 fork 操作需要分配子进程的内存页表,会有一定系统开销,且写入
.rdb
文件时也会消耗 I/O 资源。
4.2 AOF(Append Only File)
从 Redis 7.0.0开始以及之后的版本中,AOF(Append Only File)机制经过优化,引入了基础 RDB 文件和增量 AOF 文件的组合方式。这种设计有助于提高 AOF 的管理效率和数据恢复的速度。().
AOF 是另一种持久化方式,它会将每次写操作以命令的形式追加到一个文件中 (默认叫 appendonly.aof
),从而实现数据的保存。
4.2.1 AOF 的工作机制
- 写命令追加:Redis 会把收到的每条写命令,用 Redis 协议格式(Redis Serialization Protocol)记录到 AOF 文件的末尾。
- AOF 刷盘策略:Redis 提供了多种 AOF 同步策略(即何时将命令真正写到磁盘) ,通过
appendfsync
参数控制:appendfsync always
:每次有写操作时都同步写入磁盘,最安全但最慢。appendfsync everysec
:每秒将缓存中的写命令同步写到磁盘,默认配置,在系统断电时最多丢失1秒的数据。appendfsync no
:由操作系统决定何时同步写到磁盘,性能最高,安全性最低。
- AOF 重写(Rewrite):随着大量写操作的发生,AOF 文件会越来越大,因此需要对 AOF 文件进行"重写压缩"。
- Redis 会 fork 出子进程,把内存中的数据以最精简的命令集合重新写到一个新文件中。
- 重写过程中,主进程持续将新的写操作命令追加到一个缓冲区,待子进程重写完成后,再将这些命令同步到新文件末尾,最后原子地替换旧 AOF 文件。
4.2.2 AOF 的优点
- 数据安全 :AOF 可以配置成每次写操作都写入磁盘(
always
),或者至少每秒写一次(everysec
),相比 RDB,数据丢失的风险会小得多。 - 日志记录:AOF 文件是按命令记录的文本文件,人为可读,并且在出现紧急情况时可以对其进行分析或修复(比如手动删除错误指令)。
4.2.3 AOF 的缺点
- 文件体积大:和 RDB 相比,AOF 文件会更大,尤其是在没有做 AOF 重写的情况下。
- 写性能影响 :如果采用最安全的
appendfsync always
模式,那么每次写操作都要同步到磁盘,会带来明显的性能损耗。 - 恢复速度:AOF 重放所有写命令来恢复数据,可能比载入一个完整的 RDB 文件更慢。
4.3 如何选择 RDB 和 AOF
- 只用 RDB :
- 对数据一致性要求不高,能容忍几分钟的数据丢失,且更倾向于更好的写性能。
- 能够定期手动备份 RDB 文件,或者通过复制等方式冗余数据。
- 只用 AOF :
- 对数据安全性要求更高,不能容忍太多数据丢失,希望可以在秒级甚至实时上落盘。
- 愿意投入更多的磁盘性能和空间成本,接受 AOF 重放带来的恢复速度影响。
- RDB + AOF 同时使用(较推荐)
- 大多数生产环境下,往往两者结合使用,Redis 启动时优先载入 AOF 文件(更完整),如果 AOF 文件不存在或不可用才载入 RDB 文件。
- 可以在保证数据安全的同时,也能定期生成快照,便于快速恢复或冷备份。
4.4 AOF相关配置
bash
[root@Rocky9.4 etc]#vim /apps/redis/etc/redis_6379.conf
# 启用 AOF 持久化,通过config命令开启,防止数据清空 config set appendonly yes
appendonly yes
# AOF 文件的名称
appendfilename "appendonly-6379.aof"
# 新版本专门为aof增加了一个目录,这个目录是在$dir下创建的
appenddirname "appendonlydir"
# AOF 同步策略
# always: 每个写命令都同步到磁盘
# everysec: 每秒同步一次
# no: 让操作系统决定何时同步
appendfsync everysec
# 数据目录
dir /apps/redis/data/
# AOF 重写的策略
# 例如,当 AOF 文件大小增长到上一个重写后的大小的 100% 时触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 查看备份的目录
[root@Rocky9.4 data]#tree
.
├── appendonlydir
│ ├── appendonly-6379.aof.1.base.rdb
│ ├── appendonly-6379.aof.1.incr.aof
│ └── appendonly-6379.aof.manifest
├── dump-6379.rdb
├── dump-6380.rdb
└── dump-6381.rdb
1 directory, 6 files
[root@Rocky9.4 data]#pwd
/apps/redis/data
[root@Rocky9.4 data]#
根据业务需求选择合适的同步策略:
- always:适用于对数据安全性要求极高的场景,但性能开销较大。
- everysec:默认策略,适用于大多数场景,平衡了性能和数据安全性。
- no:适用于对性能要求极高且可以容忍数据丢失的场景。
在 Redis 7.4 中,AOF(Append Only File)持久化机制引入了更为复杂和高效的文件结构,以提高数据持久性和恢复速度。您在
appendonlydir
目录下看到的以下几个文件:
bash基础 RDB 文件: appendonly-6379.aof.1.base.rdb 180M 增量 AOF 文件: appendonly-6379.aof.1.incr.aof 56 清单文件: appendonly-6379.aof.manifest 98
这些文件分别代表了 Redis 7.4 中 AOF 持久化机制的新特性和结构。下面将详细解释每个文件的含义及其作用。
1.
appendonly-6379.aof.1.base.rdb
(180M)作用:
- 这是一个 基础 RDB 快照 文件。它包含了在某个时间点上 Redis 数据库的完整状态。
- 作为 AOF 持久化的一部分,Redis 7.4 结合了 RDB 和 AOF 的优点,通过基础 RDB 文件和增量 AOF 文件来实现高效的数据持久化和恢复。
优点:
- 快速恢复:通过加载 RDB 快照,Redis 可以快速恢复到某个时间点的状态,而无需重放所有 AOF 命令。
- 减少文件大小:基础 RDB 文件存储了数据的全量快照,后续的增量 AOF 文件只记录自快照以来的变化,避免了 AOF 文件过大的问题。
2.
appendonly-6379.aof.1.incr.aof
(56)作用:
- 这是一个 增量 AOF 文件,记录了自基础 RDB 快照以来的所有写操作命令(如
SET
,HSET
,LPUSH
等)。- 增量 AOF 文件用于补充基础 RDB 快照,确保在恢复时可以通过重放这些命令来达到最新的数据状态。
优点:
- 高效写入:相比传统 AOF 记录所有写操作,增量 AOF 只记录快照之后的变化,减少了磁盘写入量。
- 灵活管理:可以定期生成新的基础 RDB 快照,并清理旧的增量 AOF 文件,优化存储空间。
3.
appendonly-6379.aof.manifest
(98)作用:
- 这是一个 清单文件(Manifest File),用于管理和跟踪基础 RDB 文件与对应的增量 AOF 文件之间的关系。
- 该文件记录了哪些增量 AOF 文件对应于哪个基础 RDB 文件,确保在数据恢复时能够正确地加载和重放命令。
优点:
- 数据一致性:通过清单文件,Redis 可以准确地知道需要加载哪些文件来恢复数据,避免数据不一致的问题。
- 自动管理:清单文件帮助 Redis 自动管理文件的生命周期,如删除过期的增量 AOF 文件,维护持久化目录的整洁。
二、Redis 7.4 AOF 持久化机制的改进
Redis 7.4 引入了 混合持久化(Hybrid Persistence) 机制,将 RDB 和 AOF 结合起来,以充分利用两者的优势:
- 基础 RDB + 增量 AOF :
- 定期生成基础 RDB 快照,作为持久化的基准点。
- 记录基础 RDB 之后的所有写操作到增量 AOF 文件中,确保数据的实时性和持久性。
- 高效恢复 :
- 在恢复数据时,Redis 首先加载基础 RDB 文件,快速恢复到某个时间点的状态。
- 然后重放对应的增量 AOF 文件,达到最新的数据状态。
- 优化存储和性能 :
- 通过将持久化过程分为全量快照和增量记录,减少了 AOF 文件的大小和重写开销。
- 提高了持久化和恢复的效率,降低了对系统性能的影响。
三、如何管理这些文件
1. 自动管理
Redis 7.4 会自动生成和管理这些文件,包括:
- 生成基础 RDB 文件:根据配置的策略(如 AOF 重写触发条件),定期生成新的基础 RDB 文件。
- 记录增量 AOF:在基础 RDB 文件生成后,开始记录新的写操作到增量 AOF 文件中。
- 更新清单文件:确保清单文件准确反映当前的持久化文件结构。
2. 手动管理
虽然 Redis 会自动管理这些文件,但您仍可以进行一些手动操作以优化或排查问题:
- 触发 AOF 重写 :可以使用
BGREWRITEAOF
命令手动触发 AOF 重写,生成新的基础 RDB 文件和增量 AOF 文件。- 备份持久化文件 :定期备份
appendonlydir
目录下的所有持久化文件(包括.rdb
,.aof
,.manifest
)以防止数据丢失。- 监控文件大小:监控各类持久化文件的大小,确保磁盘空间充足,并根据需要调整持久化策略。
四、配置示例
在 Redis 配置文件 (
redis.conf
) 中,相关配置可能如下:
conf# 启用 AOF 持久化 appendonly yes # AOF 文件的名称 appendfilename "appendonly.aof" # AOF 同步策略 appendfsync everysec # 混合持久化配置 # 具体配置项可能因 Redis 版本而异,请参考官方文档
注意 :Redis 7.4 的混合持久化机制可能引入了新的配置选项,请务必参考 Redis 官方文档 以获取最新和详细的配置说明。
五、总结
Redis 7.4 在 AOF 持久化机制上引入了基础 RDB 文件、增量 AOF 文件和清单文件的结构,通过混合持久化机制,结合了 RDB 和 AOF 的优势,实现了高效、可靠的数据持久化和快速恢复。这些文件的存在确保了 Redis 在高负载和大数据量的场景下,能够保持数据的完整性和系统的高可用性。
理解和正确管理这些持久化文件,对于保障 Redis 数据的安全性和系统的稳定性至关重要。建议定期备份持久化文件,并监控文件的大小和系统性能,以确保 Redis 实例的健康运行。
4.5 AOF rewrite 重写
appendonly-6379.aof.1.base.rdb
:基础 RDB 快照文件。
appendonly-6379.aof.1.incr.aof
:增量 AOF 文件,记录自基础快照以来的所有写命令。
appendonly-6379.aof.manifest
:清单文件,管理基础 RDB 文件与增量 AOF 文件的关系。
4.5.1 基础 RDB 文件与增量 AOF 文件的工作机制
- 基础 RDB 文件 (
appendonly-6379.aof.1.base.rdb
)
- 作用:保存某一时间点的数据库完整状态,相当于一个 RDB 快照。
- 更新条件:当进行 AOF 重写(AOF Rewrite)操作时,Redis 会生成一个新的基础 RDB 文件。此操作可以自动触发,也可以手动执行。
- 增量 AOF 文件 (
appendonly-6379.aof.1.incr.aof
)
- 作用:记录自上一个基础 RDB 快照以来的所有写命令(增量操作)。
- 更新方式:在 Redis 运行过程中,所有写操作都会被追加到当前的增量 AOF 文件中。
- 清单文件 (
appendonly-6379.aof.manifest
)
- 作用:跟踪和管理基础 RDB 文件与增量 AOF 文件之间的关系,确保在数据恢复时能够正确加载基础快照并应用增量命令。
4.5.2 增量备份的工作原理与配置
增量备份主要依赖于基础 RDB 文件和增量 AOF 文件的组合。通过这种方式,你可以在保持高效的同时,实现数据的持续备份。
增量备份的工作流程
- 基础快照生成:
- 当执行 AOF 重写操作时,Redis 会生成一个新的基础 RDB 文件,记录当前数据库的完整状态。
- 记录增量操作:
- 在基础快照生成后,所有新的写操作会被记录到新的增量 AOF 文件中。
- 管理文件关系:
- 清单文件 (
appendonly-6379.aof.manifest
) 记录了当前使用的基础 RDB 文件和对应的增量 AOF 文件,确保数据恢复时能够正确加载。
- 清单文件 (
4.5.3 参数配置
bash
[root@redis.xyy.org ~]#vim /apps/redis/etc/redis.conf
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no
以下是这些参数的详细解释及其在增量备份中的作用:
appendonly yes
:启用 AOF 持久化。
appendfilename "appendonly.aof"
:指定 AOF 文件名。
appendfsync everysec
:每秒执行一次 FSYNC
操作,平衡性能与持久性。
no-appendfsync-on-rewrite no
- 解释 :当设置为
yes
时,Redis 在执行 AOF 重写期间会停止执行FSYNC
操作,从而提高性能;设置为no
则不会停止FSYNC
。 - 建议 :默认情况下建议保持
no
,确保数据的持久性,尤其在对数据一致性要求较高的场景。
- 解释 :当设置为
auto-aof-rewrite-percentage 100
- 解释 :定义 AOF 文件增长的比例,达到此比例后触发 AOF 重写。
100
表示当当前 AOF 文件大小是上一次重写后的大小的 2 倍时触发重写(增长了 100%)。 - 建议:根据实际数据写入量和系统性能调整此值。较低的比例会更频繁地进行重写,但可能影响性能;较高的比例则减少重写频率,但可能导致 AOF 文件过大。
- 解释 :定义 AOF 文件增长的比例,达到此比例后触发 AOF 重写。
auto-aof-rewrite-min-size 64mb
- 解释 :设置 AOF 重写的最小触发文件大小。只有当 AOF 文件大小超过
64MB
且增长比例达到auto-aof-rewrite-percentage
时,才会触发重写。 - 建议:确保设置一个合理的最小值,以避免频繁的小规模重写,影响性能。
- 解释 :设置 AOF 重写的最小触发文件大小。只有当 AOF 文件大小超过
aof-load-truncated yes
- 解释 :当 AOF 文件不完整或被截断时,是否允许 Redis 加载这些文件。
yes
表示允许加载,并尽可能恢复数据。 - 建议 :在生产环境中建议设置为
no
,以避免加载损坏的数据。如果设置为yes
,需要确保有其他数据恢复机制,以防止数据丢失。
- 解释 :当 AOF 文件不完整或被截断时,是否允许 Redis 加载这些文件。
aof-use-rdb-preamble yes
- 解释:在 AOF 文件开头包含一个 RDB 快照的前导数据(preamble)。这有助于加快数据加载速度。
- 建议 :默认建议保持
yes
,提高数据恢复的效率。
aof-timestamp-enabled no
- 解释 :是否在 AOF 文件中记录命令的时间戳。
no
表示不记录,yes
表示记录。 - 建议 :通常设置为
no
,除非你有特定需求需要记录时间戳。
- 解释 :是否在 AOF 文件中记录命令的时间戳。
bash
# 通过 bgrewriteaof 手动触发重写机制
[root@Rocky9.4 data]#redis-cli -a 123456 bgrewriteaof;pstree -p | grep redis ;ll appendonlydir/ -h
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
Background append only file rewriting started
|-redis-server(22944)-+-redis-server(23325)
| |-{redis-server}(22946)
| |-{redis-server}(22947)
| |-{redis-server}(22948)
| |-{redis-server}(22949)
| `-{redis-server}(22950)
total 180M
-rw-r--r-- 1 redis redis 180M Dec 28 23:25 appendonly.aof.1.base.rdb
-rw-r--r-- 1 redis redis 719K Dec 29 23:04 appendonly.aof.1.incr.aof
-rw-r--r-- 1 redis redis 0 Dec 29 23:08 appendonly.aof.2.incr.aof
-rw-r--r-- 1 redis redis 132 Dec 29 23:08 appendonly.aof.manifest
[root@Rocky9.4 data]#ll appendonlydir/
total 183420
-rw-r--r-- 1 redis redis 187817903 Dec 29 23:08 appendonly.aof.2.base.rdb
-rw-r--r-- 1 redis redis 0 Dec 29 23:08 appendonly.aof.2.incr.aof
-rw-r--r-- 1 redis redis 88 Dec 29 23:08 appendonly.aof.manifest
[root@Rocky9.4 data]#
5.Redis 常用命令
5.1 ACL 控制
bash
user <username> [on|off] [>password] [~pattern] [+permissions] [-permissions]
命令权限(+ / - / ~ / allcommands / nocommands)
+<command>
:允许执行某个命令(如+get
,+set
)-<command>
:禁止执行某个命令allcommands
:允许执行所有命令nocommands
:禁止执行所有命令~<pattern>
:这是匹配命令子令(SUBCOMMANDS),也可以通过+<command>|subcommand
的形式添加特定子命令权限
on
或 off
:启用或禁用用户。
>password
:设置用户的密码,可以有多个密码。
~pattern
:指定用户可以访问的键的模式(可选)。
+permissions
和 -permissions
:授予或撤销用户的权限,可以使用命令类别或具体命令。
bash
# 假设您希望创建一个名为 alice 的用户,设置密码为 123456,并且授予她所有权限但禁用 FLUSHALL 命令。可以按照以下方式配置:
[root@Rocky9.4 etc]#vim redis_6379.conf
user alice on >123456 ~* +@all -FLUSHALL -FLUSHDB
user default on >123456 ~* +@all +get +set -FLUSHALL -FLUSHDB -keys -config
# 重启服务
systemctl restart redis
# 连接到Redis
[root@Rocky9.4 etc]#redis-cli -u redis://alice:123456@127.0.0.1:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> FLUSHALL
(error) NOPERM User alice has no permissions to run the 'flushall' command
127.0.0.1:6379> exit
Key 权限(~ / %)
~<pattern>
:允许对匹配<pattern>
的 key 进行读写操作&<pattern>
:只读访问(7.0+ 里对 key 权限有进一步细分,比如读写分离,会使用%
符号来区分写权限)%<pattern>
:只写访问(这是 Redis 7.0+ 扩展的语法,用于区分只写权限)allkeys
/nokeys
:允许/禁止访问所有 key
解释:
user alice
:定义用户名为alice
。on
:启用该用户。>123456
:设置用户的密码为123456
。~*
:允许用户访问所有键。+@all
:授予用户所有命令权限。-FLUSHALL -FLUSHDB
:撤销FLUSHALL
和FLUSHDB
命令的权限,防止用户执行这些危险命令。
bash
# 默认用户去掉flushall 和 flushdb
user default on >123456 &logs:* ~* +@all -FLUSHALL -FLUSHDB
# 动态管理 ACL
# 1.ACL List(查看当前所有用户配置的详细信息,包括用户名称、密码哈希、权限列表、key patterns 等。)
[root@Rocky9.4 etc]#redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> acl list
1) "user alice on sanitize-payload #87b9b7660e9fe77503b14eb6da0277151e031aad3a88a1673b798d8443af242b resetchannels -@all"
2) "user default on sanitize-payload #8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 ~* resetchannels &logs:* +@all -flushall -flushdb"
127.0.0.1:6379>
# 2.ACL GETUSER <username>(获取指定用户的ACL信息)
127.0.0.1:6379> ACL GETUSER alice
1) "flags"
2) 1) "on"
2) "sanitize-payload"
3) "passwords"
4) 1) "87b9b7660e9fe77503b14eb6da0277151e031aad3a88a1673b798d8443af242b"
5) "commands"
6) "-@all"
7) "keys"
8) ""
9) "channels"
10) ""
11) "selectors"
12) (empty array)
127.0.0.1:6379>
# 3.ACL SETUSER <username> [规则 ...](更新(创建或修改)用户的权限规则)
ACL SETUSER bob on >bob_password +get +set -FLUSHALL -FLUSHDB ~bob:*
ACL SETUSER bob on:启用 bob
>bob_password:设置 bob 的密码
+get +set:允许 bob 执行 get、set 命令
~bob:*:只允许 bob 访问前缀为 bob: 的 key
# 4.ACL DELUSER <username> [<username> ...](删除用户及其权限配置)
ACL DELUSER alice
ACL DELUSER bob
# 5.ACL SAVE(将内存中的 ACL 配置写回到 aclfile(如果在配置文件中指定了 aclfile 路径的话)中。默认不写入 redis.conf,如果希望保存到文件,需要先在 redis.conf 中指定:)
注意:在redis.conf中配置aclfile,就不能同时配置user alice...,必须要将配置写入到aclfile,并且还要将这个文件手动创建出来,服务才会重启成功
aclfile /path/to/aclfile.conf
5.2 INFO
作用:查看 Redis 服务器的各种统计信息和状态,例如内存使用情况、复制状态、连接数、持久化信息、keyspace 信息等。
使用示例:
bash
127.0.0.1:6379> INFO keyspace
# Keyspace
db0:keys=10000001,expires=0,avg_ttl=0,subexpiry=0
127.0.0.1:6379>
127.0.0.1:6379> info server
# Server
redis_version:7.4.1
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:a9a1c6875521b0ad
redis_mode:standalone
os:Linux 5.14.0-427.13.1.el9_4.x86_64 x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:11.5.0
process_id:9692
process_supervised:no
run_id:83d8b9655623d6edaf809f8a7456e68179e9de91
tcp_port:6379
server_time_usec:1735883510494144
uptime_in_seconds:6113
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:7830262
executable:/apps/redis/bin/redis-server
config_file:/apps/redis/etc/redis_6379.conf
io_threads_active:0
listener0:name=tcp,bind=0.0.0.0,bind=-::1,port=6379
127.0.0.1:6379>
Redis 会返回一个多段文本,包含大量信息,可按模块划分(server、clients、memory、persistence、stats、replication、cpu、cluster、keyspace 等)。
常用操作:
INFO memory
:只查看和内存相关的信息。INFO replication
:只查看主从复制(replication)相关信息。INFO server
: 查看server的相关信息INFO cluster
: 查看集群的信息INFO keyspace
: 查看数据库的信息,有多少键.
5.3 SELECT
作用 :切换 Redis 的逻辑数据库(DB)。Redis 默认有 16 个逻辑数据库,编号 0 到 15(可通过 databases
配置修改)。
使用示例:
bash
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> # 命令提示符会显示当前已在 DB1
注意点:
- Redis 集群模式下通常只能使用 DB 0,不支持多 DB 切换。
- 切换数据库仅对当前连接有效,断开后重新连接默认仍进入 DB 0。
5.4 KEYS
作用 :列出匹配给定模式(pattern)的所有 Key。常用模式如 KEYS user:*
。
使用示例:
bash
127.0.0.1:6379> KEYS *
1) "foo"
2) "bar"
3) "user:1001"
注意点:
- 不要在生产环境中频繁使用
KEYS
命令,因为它会对整个 keyspace 做遍历,耗时且阻塞服务器,容易导致性能问题,最好使用acl禁用keys*。 - 生产中更常用
SCAN
命令以非阻塞(游标)方式遍历 key。
5.5 BGSAVE
作用 :在后台(异步)执行一次 RDB 快照,将当前数据集保存到磁盘(默认文件名 dump.rdb
,可在 redis.conf
中配置)。
使用示例:
bash
127.0.0.1:6379> BGSAVE
Background saving started
执行后会立即返回,Redis 会在后台完成 RDB 持久化。
常用场景:
- 手动触发一次快照,做数据备份。
- 结合
save
配置自动触发也可以,但在现代 Redis 版本中,很多人更倾向于 AOF 或混合持久化。
5.6 DBSIZE
作用:返回当前所选数据库(DB)中 key 的数量。
使用示例:
bash
127.0.0.1:6379> DBSIZE
(integer) 10000001
127.0.0.1:6379>
表示当前 DB 里有 一千万 个 key。
注意点:
DBSIZE
是一个简单计数,不涉及 keys 的遍历,因此非常高效。- 如果想知道所有 DB 的 key 数量,可以逐一
SELECT
0~15 执行DBSIZE
,或者用INFO keyspace
查看。
5.7 FLUSHDB
作用:清空当前数据库(DB)的所有 Key(只影响您当前选择的 DB)。
使用示例:
bash
127.0.0.1:6379> FLUSHDB
OK
如果在 DB0 执行,会清空 DB0;在 DB1 执行会清空 DB1。
风险与注意:
- 不可逆,会删除当前 DB 中的全部数据!
- 可在正式执行前先确认
SELECT
到了正确的 DB,避免误删。
5.8 FLUSHALL
作用 :清空 所有 DB 的所有 Key(默认 16 个数据库都会被清空)。
使用示例:
bash
127.0.0.1:6379> FLUSHALL
OK
风险与注意:
- 比
FLUSHDB
更危险,一旦执行,会导致 Redis 整个实例所有 DB 数据被清空。 - 一般不建议在生产环境使用,务必谨慎操作!
5.9 SHUTDOWN
作用:关闭 Redis 服务器。执行后,会尝试做一次持久化(若配置了 RDB/AOF),然后退出进程。
使用示例:
bash
127.0.0.1:6379> SHUTDOWN
连接会立即断开,Redis 服务停止。
注意点:
- 如果想避免保存数据(即不再进行 RDB/AOF 落盘),可加选项
SHUTDOWN NOSAVE
。 - 如果只想保存数据而不关闭,可以执行
SAVE
或BGSAVE
。
5.10 SCAN
在 Redis 中,SCAN
命令是一种基于游标(cursor)**的迭代查询方式,能够**分批且非阻塞 地遍历数据库中的 Key(或集合、哈希、ZSet 等),避免像 KEYS *
这样一次性扫描全部 Key 导致大规模阻塞的问题。下面介绍一下 SCAN
的核心概念、使用方法以及与 KEYS
的区别。
一、为什么要用 SCAN?
- KEYS 的缺点 :
KEYS pattern
命令会一次性遍历所有 Key,并返回所有匹配的结果。- 在 Key 数量很大的情况下(几百万上千万),一次扫描会造成主线程阻塞,期间无法处理其他请求,导致服务卡顿甚至超时。
- 因为 Redis 是单线程架构,这种大规模阻塞会严重影响线上业务。
- SCAN 的优点 :
- 将大范围扫描拆分成多次小范围扫描,每次只返回一部分数据。
- 采用"游标(Cursor)+ 增量遍历"的模式,每扫描一部分,Redis 就返回一个新的 Cursor,并在响应中包含本次扫描到的部分数据。
- 用户可以根据返回的 Cursor 继续下一次扫描,直到 Cursor 回到 0 表示扫描结束。
- 相比
KEYS
,SCAN
对服务器的阻塞时间更短,也更可控。
二、SCAN 的基本用法
2.1 命令格式
css
SCAN cursor [MATCH pattern] [COUNT count]
- cursor :游标,初次调用时通常传
0
,表示从头开始扫描。 - MATCH pattern :可选,用于过滤匹配的 Key 模式(如
MATCH user:*
)。如果不指定MATCH
,则返回的 Key 不做任何模式过滤(会返回所有 Key 的子集)。 - COUNT count :可选,用于指定每次扫描希望返回的 Key 数量。并非严格保证返回固定数量,而是"期望值",Redis 可能实际返回多于或少于这个数的 key。
初次调用:
bash
127.0.0.1:6379> SCAN 0 COUNT 10
1) "13107200"
2) 1) "k4533779"
2) "k252114"
3) "k933235"
4) "k3676789"
5) "k2576537"
6) "k7573677"
7) "k5285770"
8) "k2267950"
9) "k2473601"
10) "k4433328"
- 返回结果中的第一个元素
"13107200"
是新的游标值,下次扫描时要用它。 - 第二个数组是本次扫描到的一批 Key(例如 5~10 个)。
后续调用:
bash
127.0.0.1:6379> SCAN 13107200 COUNT 10
1) "4456448"
2) 1) "k1450912"
2) "k9102989"
3) "k6829708"
4) "k3410677"
5) "k2513869"
6) "k9564207"
7) "k7683296"
8) "k2951179"
9) "k6113726"
10) "k8041825"
127.0.0.1:6379>
- 继续用上一次返回的游标 13107200 作为本次调用的游标;
- Redis 返回游标 "4456448" 和新的 Key 列表。
三、SCAN 与其他相关命令
SCAN:遍历数据库中的 Key。
SSCAN:遍历 Set 中的元素。
HSCAN:遍历 Hash 中的 field-value 对。
ZSCAN:遍历 Sorted Set 中的 member-score 对。
它们用法相似,都是 SCAN cursor [MATCH pattern] [COUNT count]
的形式,只是操作的数据结构不同。例如:
bash
HSCAN myhash 0 MATCH field:* COUNT 10
四、SCAN vs. KEYS
- 性能:
KEYS
命令会阻塞 Redis 直到扫描完所有 Key;SCAN
采用增量扫描,每次只处理一部分,能把阻塞时间分散到多个小的时间片,对线上性能影响更小。
- 用法:
KEYS
适合在测试 或小规模场景下调试时使用,方便一次性获取所有匹配 key;- 生产环境中强烈推荐 使用
SCAN
,能避免大规模阻塞。
- 一致性:
KEYS
在那一刻会返回快照式的所有 Key;SCAN
可能会出现漏扫或重复,尤其当 Key 动态变化时。但大多数情况下,这种不完全一致性是能接受的(可额外在应用层做去重处理)。
6.Redis 数据类型
6.1 字符串(string)类
6.1.1 String 的存储特点
- 存储内容 :可以是文本(如
"Hello"
)、数字(如"123"
)、二进制文件(如图片、音频等),只要单个值不超过 512 MB 即可。 - 内存开销 :小字符串采用
SDS(Simple Dynamic String)
结构存储,Redis 会根据实际值大小自动选择合适的底层结构,避免频繁的内存分配。 - 常用场景:
- 缓存网页内容、配置、令牌、session 信息、计数器等;
- 存储对象的序列化结果,比如 JSON、Protobuf 等;
- 计数统计,利用
INCR
/DECR
等快速自增自减
6.1.2 常见的操作命令
6.1.2.1 设置与获取
6.1.2.1.1 SET
-
作用:设置 key 的值。如果 key 已存在,会被覆盖;如果 key 不存在,则创建一个新的 key。
-
基本语法:
httpSET <key> <value>
-
示例:
bashSET user:1001 "Alice" GET user:1001 # 返回 "Alice"
-
扩展参数:
-
EX seconds
:设置过期时间(秒)。 -
PX milliseconds
:设置过期时间(毫秒)。 -
NX
:只有当 key 不存在时才执行设置。 -
XX
:只有当 key 存在时才执行设置。 -
例如:
bashSET mykey "Hello" EX 10 NX
表示仅当
mykey
不存在时,才设置值为"Hello"
并自动在 10 秒后过期。
-
6.2.2.1.2 GET
-
作用:获取 key 的字符串值。
-
示例:
cssGET mykey
若 key 存在,则返回对应的值;若 key 不存在,返回
nil
6.2.2.1.3 MSET / MGET
-
MSET:同时设置多个 key-value 对。
cssMSET k1 "v1" k2 "v2" k3 "v3"
一次性写入多对数据,减少多次网络往返。
-
MGET:批量获取多个 key 的值。
cssMGET k1 k2 k3
返回一个数组,如
[v1, v2, v3]
,不存在的 key 会以nil
对应。
6.2.2.1.4 SETNX / SETXX(或结合 SET 命令的 NX / XX 参数)
SETNX
:Set if Not eXists,只在 key 不存在时设置成功。等价于SET key value NX
。SETXX
:Set if eXists,只在 key 存在时设置成功。等价于SET key value XX
。
6.1.2.2 数值操作
Redis 支持对字符串值进行数字自增自减操作(前提是该字符串能被解析为整数或浮点数)。
6.1.2.2.1 INCR / DECR
-
INCR:将 key 的值自增 1。如果 key 不存在,则先初始化为 0 再自增。
-
DECR:将 key 的值自减 1。
-
示例:
cssSET counter 10 INCR counter # counter = 11 DECR counter # counter = 10
-
注意 :若 value 不是一个整数字符串,例如
"Hello"
或1.5
,执行INCR
/DECR
会报错 (ERR value is not an integer)。
6.1.2.2.2 INCRBY / DECRBY
-
作用:一次性加/减指定数值。
cssINCRBY <key> <increment> DECRBY <key> <decrement>
-
示例:
cssSET counter 100 INCRBY counter 50 # counter = 150 DECRBY counter 20 # counter = 130
6.1.2.2.3 INCRBYFLOAT
-
作用:对浮点数进行加法操作。
cssSET price 12.5 INCRBYFLOAT price 0.7 # price = 13.2
-
使用场景:计量或需要小数的场景,如金额、温度等。
6.1.2.3 部分字符串操作
6.1.2.3.1 APPEND
-
作用 :向指定 key 的现有值 追加 一段字符串。如果 key 不存在,就相当于
SET
。 -
示例:
cssSET greeting "Hello" APPEND greeting ", Redis!" GET greeting # "Hello, Redis!"
6.1.2.3.2 GETRANGE (旧命令:SUBSTR)
-
作用:获取字符串中指定区间(基于下标)的子串,区间含左右边界,支持负索引(-1 表示最后一个字符)。
-
语法:
cssGETRANGE <key> <start> <end>
-
示例:
cssSET greeting "Hello, Redis!" GETRANGE greeting 0 4 # 返回 "Hello" GETRANGE greeting -6 -2 # 返回 "Redis" 中的一部分, 要根据实际字符串判断
注意字符串
"Hello, Redis!"
的长度和下标分布,从 0 到 12。
6.1.2.3.3 SETRANGE
-
作用 :在指定偏移量开始处,用给定值覆盖或插入到现有字符串里。若偏移量超过当前字符串长度,中间会填充空字节(
\x00
)。 -
示例:
cssSET mykey "Hello World" SETRANGE mykey 6 "Redis" # 原字符串第 6 位开始覆盖,"Hello Redis" GET mykey # "Hello Redis"
6.1.2.3.4 STRLEN
-
作用:返回字符串值的长度(字节数)。
-
示例:
cssSET name "Alice" STRLEN name # 返回 5
6.1.2.4 过期与生命周期管理
尽管不是 String 专有命令,但实际中常和 String 配合使用:
- EXPIRE:为 key 设置过期时间(单位秒)。到期后 key 会被自动删除。
- TTL:查看 key 剩余存活时间。
- PERSIST:移除 key 的过期时间,使 key 变为永久存在。
也可在 SET
命令时直接附带 EX
或 PX
参数来设置过期时间。
6.1.2.5 高级应用:Bit 操作
Redis 还提供了对 String 值执行位操作 (bitwise)的命令,如 SETBIT
, GETBIT
, BITCOUNT
, BITOP
等,能在每个位上进行读写、计数或逻辑运算。
- SETBIT:将字符串第 offset 位设置为 value (0 或 1)。
- GETBIT:获取 offset 位置的位值。
- BITCOUNT [start end]:统计字符串指定区间内值为 1 的位数。
- BITOP AND/OR/XOR [... ] :对多个 key 对应的位进行逻辑运算,并将结果存储到
destkey
。
这些命令常用于**实现位图(bitmap)**功能,比如用户签到、活跃状态等布尔属性的存储。
6.1.2.6 小结 & Best Practices
- 最常用的操作 :
SET
/GET
:基础读写;MSET
/MGET
:批量读写;INCR
/DECR
/INCRBYFLOAT
:实现计数器或数值操作;APPEND
/GETRANGE
/SETRANGE
:在字符串上做增改或截取;EXPIRE
/SET ... EX
:配合过期管理。
- 注意字符串与数值 :
- 只有当字符串能被解析为整数或浮点数时,
INCR
/DECR
/INCRBYFLOAT
才能工作,否则会报错; - 若您频繁进行数值操作,不要在中间手动
GET
并转回数字,以免造成竞态。直接用原子操作(INCR...)更可靠。
- 只有当字符串能被解析为整数或浮点数时,
- 内存与大小限制 :
- Redis 默认可存储最大 512MB 的值,但太大的值会带来内存和网络传输负担,不建议把超大文件直接放 Redis。
- 建议拆分或使用外部文件存储(OSS、S3、NFS),Redis 中只存元数据或指针。
- 使用过期策略 :
- 若用 Redis 做缓存,通常会为 key 设置过期时间,避免数据无限堆积或过期失效。
- Bit 操作 :
- 高级玩法,能有效利用 Redis 做位图统计,如用户行为打点、日活统计等,但要注意数据结构设计和偏移量计算。
bash
# 设置值,并设置有效期(s)--10秒过期,也可以通过SETEX来直接设置
set title study ex 10
# 存在title,不变,不存在设置成ceo
127.0.0.1:6379> SETNX title ceo
(integer) 1
# 批量设置--mset/mget
127.0.0.1:6379> mset name xingyuyu age 18 gender femal title cto
OK
127.0.0.1:6379> mget name age
1) "xingyuyu"
2) "18"
127.0.0.1:6379>
6.2 哈希(Hash)
在 Redis 中,Hash(哈希) 是一种将多个字段(field)映射到同一个 key 下的数据结构。换句话说,Redis 的 Hash 类似一个小型的 key-value 表,表里的 "行" 只有一条,但这条"行"有许多字段(field)。它非常适合用来存储和读取类似对象、用户资料、配置项等场景。下面将按照前面类似的步骤,讲解 Hash 的基本概念、常用操作以及使用场景与注意事项。
6.2.1 Redis Hash 的存储与特点
- 内部结构
- 小规模的哈希对象(字段数量较少时),Redis 会采用一种紧凑的数据结构(ziplist 或 listpack)来节省内存。
- 当字段数量或字段长度变大时,会转换成真正的哈希表(Hashtable)。
- 这对我们是透明的,Redis 会自动处理。但它解释了为什么 Hash 往往在小规模场景下特别高效。
- 数据特征
- Key :在 Redis 层面标识一个哈希对象的名称(如
user:1001
)。 - Fields 和 Values:在哈希对象内部,每个 field 都拥有一个独立的 value。不同 field 之间互不干扰,但都属于同一个 key 的管理之下。
- 字段唯一:同一个哈希键下的 field 名称是不能重复的,若重复则会更新原有值。
- Key :在 Redis 层面标识一个哈希对象的名称(如
- 常用场景
- 存储用户信息(字段:username、email、age 等);
- 缓存一条记录或配置,这些记录可有多个属性;
- 减少 key 数量(相比将每个属性单独存为一个 String key,这时可以把多个属性存在一个哈希 key 中)。
6.2.2 Hash 的常见操作命令
6.2.2.1 基础增删改查
6.2.2.1.1 HSET / HGET
-
HSET
-
作用:向 hash 里设置(或更新)一个字段的值。如果字段不存在,就创建;若存在,就覆盖。
-
语法:
bashHSET <key> <field> <value> [<field2> <value2> ...]
-
示例:
bashHSET user:1001 name "Alice" age "20"
若
user:1001
不存在,Redis 会创建一个新的哈希;并设置字段name="Alice"
、age="20"
-
-
HGET
-
作用:获取 hash 表中某个字段的值。
-
语法:
bashHGET <key> <field>
-
示例:
bashHGET user:1001 name # 返回 "Alice"
-
6.2.2.1.2 HMSET / HMGET
-
HMSET(在新版本 Redis 中
HSET <key> <field> <value> ...
已经可以替代,HMSET 旧命令仍兼容)-
作用 :同时设置多个字段值,和
HSET
的多字段形式一致。 -
示例:
bashHMSET user:1001 city "Beijing" gender "female"
-
-
HMGET
-
作用:一次性获取多个字段的值。
-
示例:
bashHMGET user:1001 name age city # 返回一个数组 ["Alice", "20", "Beijing"]
-
6.2.2.1.3 HGETALL / HKEYS / HVALS
-
HGETALL
-
作用:获取 hash 中所有字段和对应值,以 field-value 对数组返回。
-
示例:
bashHGETALL user:1001 # 可能返回 ["name","Alice","age","20","city","Beijing","gender","female"]
-
-
HKEYS
- 作用:只返回所有字段(field)的列表。
-
HVALS
- 作用:只返回所有字段的值列表。
-
这三种命令在字段很多时,一次性返回也会占用较多资源,生产环境可考虑
HSCAN
(分批扫描)以避免阻塞。
6.2.2.1.4 HDEL
-
作用:删除哈希表中的一个或多个字段。
-
语法:
bashHDEL <key> <field1> [field2 ...]
-
示例:
bashHDEL user:1001 age # 移除 "age" 字段
6.2.2.1.5 HEXISTS
-
作用:检查哈希表中指定字段是否存在。
-
示例:
bashHEXISTS user:1001 name # 若存在则返回 1,否则返回 0
6.2.2.2 数值操作(原子自增自减)
如果哈希字段的值可以解析为数字(整数或浮点数),Redis 提供了原子增量命令:
6.2.2.2.1 HINCRBY
-
作用:对 hash 某个字段的值执行加法(整数)。
-
语法:
bashHINCRBY <key> <field> <increment>
-
示例:
bashHINCRBY user:1001 login_count 1 # 将 user:1001 哈希表里 login_count 字段值 +1
-
注意:若字段不存在,会先初始化为 0,再进行加法。如果原来是非数字值,会报错。
6.2.2.2.2 HINCRBYFLOAT
-
作用:对 hash 字段的值执行浮点加法。
-
语法:
bashHINCRBYFLOAT <key> <field> <increment>
-
示例:
bashHINCRBYFLOAT product:2001 price 9.99 # 如果 price="100.0",则更新后 price="109.99"
6.2.2.3 Hash 扫描(HSCAN)
-
HSCAN
-
与
SCAN
类似,用于分批遍历 一个哈希表,避免HGETALL
在字段太多时造成阻塞。 -
用法:
bashHSCAN <key> <cursor> [MATCH pattern] [COUNT count]
-
示例(伪代码):
bashcursor = 0 do { response = HSCAN user:1001 cursor MATCH "f*" cursor = response[0] # 新游标 fields = response[1] # 字段值对 # ...处理 fields ... } while (cursor != 0)
-
这样可以一口气分多次小范围返回,不会阻塞 Redis 主线程太久。
-
6.2.3 Hash 的使用场景
- 用户配置、信息
user:1001
存放 name、email、age、login_count 等多个字段;- 避免把每个字段都做一个单独的 String key。
- 数据对象缓存
- 例如商品信息
product:2001
,其中字段 price、title、stock 等; - 更新时只需改对应字段,不必覆盖整个对象;
- 还能减小内存,因为小哈希在 Redis 内部可用紧凑结构存储。
- 例如商品信息
- 记录统计量
- 可以在 Hash 中存放各种计数器字段(点击量、点赞量、回复数等),通过
HINCRBY
实现原子自增。 - 一个大对象的多种统计值都集中在同一个 hash key 下,管理更方便。
- 可以在 Hash 中存放各种计数器字段(点击量、点赞量、回复数等),通过
- 减少 key 的数量
- 在某些业务场景中,为了避免 key 过多带来的管理压力,把多个相关字段汇总到同一个 Hash key 下会简化命名空间;
- 但要注意单个哈希不宜过大,否则一次获取全部字段仍可能带来性能开销。
6.2.4 常见注意事项 & Best Practices
- 单个 Hash 不宜过度膨胀
- 虽然 redis.conf 里会控制 ziplist/listpack 的一些大小阈值,但如果字段规模过大,一次性
HGETALL
会占用大量内存带宽; - 对于超大哈希(上百万字段),谨慎使用或考虑拆分、使用
HSCAN
分批遍历。
- 虽然 redis.conf 里会控制 ziplist/listpack 的一些大小阈值,但如果字段规模过大,一次性
- 字段值只能是字符串
- Redis Hash 的 value 最终以字符串形式存储(即使是数字,也保存为字符串);
- 若要存储复杂对象,可以序列化后放进字段值,如 JSON 或 MsgPack,更新局部字段可能就需要再解析 JSON。
- 过期和生命周期
- Redis 只能对整个 Hash key 设置过期时间(如
EXPIRE user:1001 3600
),无法对 Hash 里某个字段单独设置过期。 - 若只想让特定字段有失效时间,需要业务层逻辑配合。
- Redis 只能对整个 Hash key 设置过期时间(如
- Hash vs. String
- 如果只有一个字段,使用 String 即可;
- 如果对象字段较多或需要一起管理,Hash 更方便,也可能更省内存(对小字段场景)。
- Hash vs. Set、ZSet
- Hash 专注于"字段-值"映射的结构,字段之间无顺序,主要用于描述对象属性;
- Set/ZSet 更侧重"集合"或"排序"语义;
- 业务逻辑决定选择。
- HXxx 命令的性能
- 在绝大多数情况下都为 O(1) 或 O(N)(N 为字段数)的操作,和内部哈希表实现相关。
- 大量字段更新时,也要留意操作分散性,别在高并发下对同一个大哈希做高频更新------可能会增大锁竞争(Redis 单线程)。
6.2.5 小结
Redis Hash 提供了紧凑且灵活的 "字段-值" 结构,适合存储类似对象、配置、多字段记录等。它的核心命令可总结为:
- 增改
HSET
/HMSET
:设置或更新字段HINCRBY
/HINCRBYFLOAT
:对数值字段原子自增
- 查询
HGET
/HMGET
:获取单个或多个字段值HGETALL
:获取全部字段和值HKEYS
/HVALS
:只获取字段 / 值列表HEXISTS
:判断字段是否存在HLEN
:获取字段总数
- 删除
HDEL
:删除一个或多个字段
- 扫描
HSCAN
:分批遍历所有字段
- 其他
EXPIRE
/TTL
等只能针对整个 key,不支持单字段到期
合理使用 Hash,可以有效减少 key 数量,提升空间利用率,且让数据结构更加接近业务对象形态。结合其他 Redis 数据类型(如 List、Set、ZSet)能够构建出更丰富的业务逻辑与功能。
- HSET
<key> <field> <value>
:设置 hash 表中的 field 字段值。 - HGET
<key> <field>
:获取 hash 表中 field 字段值。 - HMGET / HMSET:批量操作多个字段。
- HGETALL
<key>
:获取 hash 的所有字段和值。 - HDEL
<key> <field>
:删除指定字段。
6.3 列表(List)
在 Redis 中,List(列表) 是一个非常常用的数据类型,用来存储一个按插入顺序排序的字符串列表。它本质上可被视作一个双端队列,允许在队列头(left)或队列尾(right)进行插入、弹出等操作。
6.3.1 List 的存储与特点
- 存储结构 :在内部,Redis 为 List 使用了一种类似双端链表 或QuickList(在较新的版本中)来维护元素。
- 数据特征:
- 有序:按插入顺序保存元素,可在头/尾插入或弹出。
- 元素类型:每个元素都是一个字符串,可以是文本、JSON、二进制等,长度最大可达 512MB(与普通 String 的限制相同)。
- 常用场景:
- 实现消息队列 / 任务队列(如生产者-消费者模型)。
- 记录最新的操作日志或信息(用 LPUSH + LTRIM 维护一个固定长度)。
- 需要按顺序遍历、插入、或弹出的场景。
6.3.2 List 的常见操作命令
Redis 提供了多种与 List 交互的命令,下面分门别类介绍。
6.3.2.1 插入与弹出
6.3.2.1.1 LPUSH / RPUSH
- LPUSH [element ...]:在列表头部插入一个或多个元素。新的元素会排在最前面。
- RPUSH [element ...]:在列表尾部插入一个或多个元素。新的元素会排在最后面。
示例:
bash
LPUSH mylist "world"
LPUSH mylist "hello"
# mylist = ["hello", "world"]
RPUSH mylist "!"
# mylist = ["hello", "world", "!"]
当 mylist
不存在时,会先创建一个空列表再插入。
6.3.2.1.2 LPOP / RPOP
- LPOP :移除并返回列表头部的第一个元素。
- RPOP :移除并返回列表尾部的最后一个元素。
示例:
bash
LPOP mylist # 返回 "hello",mylist 变为 ["world", "!"]
RPOP mylist # 返回 "!", mylist 变为 ["world"]
6.3.2.1.3 LINSERT
- LINSERT BEFORE|AFTER
- 作用:在列表中的某个"现有元素"前或后,插入一个新元素。
- 如果
pivot
不存在,命令返回 -1;如果 key 不存在或列表为空,返回 0。
示例:
bash
# mylist = ["hello", "world"]
LINSERT mylist BEFORE "world" "there"
# mylist = ["hello", "there", "world"]
6.3.2.2 获取与查看列表内容
6.3.2.2.1 LRANGE
-
LRANGE :返回列表在指定区间内的所有元素,包含左右边界。
-
下标可以是正数,0 表示第一个元素,1 表示正数第二个,以此类推。
-
下标可以为负数,-1 表示最后一个元素,-2 表示倒数第二个,以此类推。
-
示例:
bash# mylist = ["hello", "world", "!"] LRANGE mylist 0 -1 # 返回所有元素 ["hello","world","!"] LRANGE mylist 0 1 # 返回 ["hello","world"]
6.3.2.2.2 LINDEX
-
LINDEX :根据索引获取列表中的单个元素。
-
index 可为正数(从头开始 0 表示第一个)或负数(-1 表示最后一个)。
-
示例:
bash# mylist = ["hello", "world", "!"] LINDEX mylist 0 # 返回 "hello" LINDEX mylist -1 # 返回 "!"
6.3.2.2.3 LLEN
-
LLEN :返回列表的长度(元素个数)。
-
若 key 不存在,则返回 0;若 key 的类型不是 list,则报错。
-
示例:
bash# mylist = ["hello","world","!"] LLEN mylist # 返回 3
6.3.2.3 修改与截取
6.3.2.3.1 LSET
-
LSET :将列表中下标为
index
的元素设置为新的element
。 -
若索引越界或列表不存在,会返回错误。
-
示例:
bash# mylist = ["hello","world","!"] LSET mylist 1 "Redis" # mylist 变为 ["hello", "Redis", "!"]
6.3.2.3.2 LTRIM
-
LTRIM :对列表进行修剪,只保留指定区间的元素,区间外的全部删除。
-
注意截取的是保留区间,而非删除区间。
-
示例:
bash# mylist = ["hello","Redis","!"] LTRIM mylist 0 1 # 只保留下标 0 到 1 的元素,mylist = ["hello","Redis"]
6.3.2.3.3 LREM
-
LREM :按 count 的值移除指定元素:
- count > 0:从头到尾,删除最多 count 个等于 element 的元素;
- count < 0:从尾到头,删除最多 |count| 个等于 element 的元素;
- count = 0:删除所有等于 element 的元素。
-
示例:
bash# mylist = ["hello","world","hello","Redis"] LREM mylist 1 "hello" # 只从头到尾删除 1 个 "hello" -> mylist = ["world","hello","Redis"] LREM mylist 0 "hello" # 再把剩余的 "hello" 都删掉 -> mylist = ["world","Redis"]
6.3.2.4 队列 & 阻塞操作
6.3.2.4.1 RPOPLPUSH
-
RPOPLPUSH :
- 从
source
列表的尾部弹出一个元素,并将该元素插入到destination
列表的头部; - 返回弹出的元素值。
- 从
-
可把它当作右弹左推的队列操作,适用于在多个队列之间传递消息。
-
示例:
bash# list1 = ["job1","job2","job3"] # list2 = ["x","y"] RPOPLPUSH list1 list2 # -> 返回 "job3" # list1 = ["job1","job2"] # list2 = ["job3","x","y"]
6.3.2.4.2 BLPOP / BRPOP
-
BLPOP [key2 ...] :
- 从左侧弹出第一个非空列表的头元素;如果所有列表都为空,则阻塞等待,直到有新元素插入或达到超时。
- 如果超时,返回
nil
。
-
BRPOP 类似,但从右侧弹出。
-
常用场景:消息队列或任务队列中,消费者阻塞等待任务的到来。
-
示例:
bashBLPOP list1 list2 30 # 优先检查 list1 是否有元素,如果空,再检查 list2 # 如果都为空,则最多阻塞 30 秒,期间如果有新元素插入便立即返回
LPOP/RPOP :如果列表为空,会立即返回 nil,不会阻塞。
BLPOP/BRPOP :如果列表为空,会阻塞等待 (直到超时或有新数据),适合任务队列 或实时消费场景。
6.3.3 List 的使用场景
- 消息/任务队列
- 生产者使用
LPUSH
/RPUSH
将任务入队; - 消费者使用
BLPOP
/BRPOP
阻塞式弹出任务进行处理; - 实现简单可靠的异步队列机制。
- 生产者使用
- 最新消息/日志列表
- 使用
LPUSH
不断往头部插入最新的消息; - 配合
LTRIM mylist 0 99
等截取操作,保留最近 100 条; - 方便快速获取最新信息,同时限制长度防止无限膨胀。
- 使用
- 数据分页或流式读取
- 可以用 List 存储一批数据,使用
LRANGE
分段获取; - 但要注意大规模数据可能导致内存占用多,可分而治之或考虑其他结构。
- 可以用 List 存储一批数据,使用
- Rotating Queue
- 使用
RPOPLPUSH
在多个队列之间转移数据,实现轮转或多队列消费等策略。
- 使用
6.3.4 常见注意事项 & Best Practices
- 避免存放超大量元素
- 虽然 List 可存数百万个元素,但一次
LRANGE
全量拉取也会占用大量内存和带宽。 - 如果只需要最新部分数据,建议使用
LTRIM
保留一定区间,或按业务逻辑拆分存储。
- 虽然 List 可存数百万个元素,但一次
- 使用阻塞命令需注意
BLPOP
/BRPOP
在等待时会占用一个连接。如果连接数有限或网络环境复杂,需要注意不要同时有太多阻塞请求。
- List 并非随机访问结构
- 只能通过索引下标 进行有限的随机访问(LINDEX),或是依赖
LRANGE
全面遍历。 - 若需要频繁随机读写中间元素或多字段操作,或许
Hash
/Set
等其他结构更合适。
- 只能通过索引下标 进行有限的随机访问(LINDEX),或是依赖
- 使用场景决定操作端
- 对于队列场景,一般约定一端入队 ,另一端出队,避免混用增删端,以形成一致的 FIFO(或 LIFO)顺序。
- 对于日志场景,通常是
LPUSH
+LTRIM
,永远从头部插入最新消息。
- Watch out for
LRANGE
large ranges- 尽管 Redis 的 List 操作性能较优,但一次性获取几百万条也会造成阻塞和内存压力。
- 生产环境中可以
LRANGE key start end
分段获取,或设计合理的数据拆分。
6.3.5 小结
List 在 Redis 中是一个强大且灵活的数据结构,可满足多种顺序场景需求,包括消息队列 、最新列表 、双端队列等。其核心操作可以概括为:
- 头/尾插入 :
LPUSH
、RPUSH
- 头/尾弹出 :
LPOP
、RPOP
- 读取区间 :
LRANGE
、查看长度:LLEN
- 阻塞弹出 :
BLPOP
、BRPOP
(常用于队列) - 部分修改 :
LSET
、LTRIM
、LREM
- 多队列转移 :
RPOPLPUSH
在实际业务中,通过合理地选用这些操作并结合过期策略或其他结构(例如存储 ID 在 List 中、实际数据放在 Hash 或 String 里),可以构建高效的队列模型、实时日志系统或其他顺序相关的功能。
6.4 集合(Set)
在 Redis 中,Set(集合) 类型用于存储一组不重复 的字符串元素,和我们熟悉的数学集合很类似。Redis 提供了丰富的命令来操作集合,包括添加、删除、判断成员以及集合之间的交并差运算等。下面将按照类似前面介绍的风格,详细讲解 Set 的基本概念、常用命令以及使用场景和最佳实践。
6.4.1 Redis Set 的存储与特点
- 内部结构
- Redis 使用类似哈希表(Hash Table)来存储 Set 中的元素,每个元素都是一个独立的字符串,并且不允许重复。
- 如果尝试将相同的元素多次添加到同一个 Set,只有第一次会生效,后续重复添加不会改变集合状态,也不会报错。
- 数据特征
- 元素无序:Redis 不保证插入顺序,也不能通过索引下标来获取或遍历。
- 元素唯一:同一个元素在集合中只会出现一次。
- 高效查找:Set 中的元素查找、添加、删除通常是 O(1) 平均复杂度(哈希表机制)。
- 常用场景
- 存储好友列表、标签列表、不重复的元素集合;
- 取交集、并集、差集,做共同好友、共同兴趣、共同标签分析等;
- 去重功能(快速判断某元素是否已经存在);
- 随机获取、抽奖(
SRANDMEMBER
、SPOP
)等场景。
6.4.2 Set 的常见操作命令
6.4.2.1 添加、删除、判断成员
6.4.2.1.1 SADD
-
作用:向集合添加一个或多个元素,如果元素已存在则跳过,不影响集合。
-
语法:
bashSADD <key> <member1> [member2 ...]
-
返回值:实际被添加进集合的新元素数量。
-
示例:
bashSADD myset "apple" SADD myset "banana" "orange" # 如果 myset 之前不存在,会创建并插入元素。 # 如果 "apple" 已存在,再次插入不会重复。
6.4.2.1.2 SREM
-
作用:删除集合中指定的一个或多个元素。
-
语法:
bashSREM <key> <member1> [member2 ...]
-
返回值:被成功移除的元素数量。
-
示例:
bashSREM myset "banana" # 从 myset 移除 "banana"
6.4.2.1.3 SISMEMBER
-
作用:判断某个元素是否为集合成员。
-
语法:
bashSISMEMBER <key> <member>
-
返回值:1 表示是集合成员,0 表示不是或不存在。
-
示例:
bashSISMEMBER myset "apple" # 若 myset 包含 "apple" 则返回 1,否则返回 0
6.4.2.2 获取、查看和随机操作
6.4.2.2.1 SMEMBERS
-
作用:返回集合中的所有元素。
-
示例:
bashSMEMBERS myset # 比如返回 ["apple","orange","grape"]
-
注意:
- 如果集合元素非常多,一次性返回也会占用较多内存和带宽;
- 生产环境中若集合规模庞大,可以考虑使用
SSCAN
命令分批次扫描(类似于SCAN
)。
6.4.2.2.2 SCARD
-
作用:返回集合中的元素数量。
-
示例:
bashSCARD myset # 返回例如 3
6.4.2.2.3 SPOP
-
作用:随机移除并返回集合中的一个或多个元素(默认 1 个)。
-
语法:
bashSPOP <key> [count]
-
示例:
bashSPOP myset # 随机弹出一个元素,并将其从 myset 中移除。 SPOP myset 2 # 随机弹出 2 个元素,返回数组,并同时从集合删除它们。
-
应用场景:抽奖、随机发放奖品、随机推荐等。
6.4.2.2.4 SRANDMEMBER
-
作用 :随机获取集合中的一个或多个元素,但不会 将其从集合中删除(和
SPOP
区别在于是否删除)。 -
语法:
bashSRANDMEMBER <key> [count]
-
示例:
bashSRANDMEMBER myset # 随机返回一个元素,不会删除 SRANDMEMBER myset 2 # 随机返回 2 个元素(可能包含重复,Redis < 3.2 不同版本实现略有区别)
在 Redis 3.2+,如果 count 为正,则返回不重复的元素;如果 count 为负,则可能返回重复的元素。可查阅对应版本文档以确认。
6.4.2.3 集合运算(交集、并集、差集)
Redis 提供了三个重要的集合运算命令:SINTER 、SUNION 、SDIFF。配合这些命令,能快速做聚合分析,如"共同好友"、"共同兴趣标签"等场景。
6.4.2.3.1 SINTER / SINTERSTORE
-
SINTER:求一个或多个集合的交集。
bashSINTER <key1> [key2 ... keyN]
返回所有在这些集合中都存在的元素。
-
SINTERSTORE:将交集结果直接存储到另一个集合。
bashSINTERSTORE <destination> <key1> [key2 ... keyN]
-
示例:
bash# set1 = {"apple","banana","orange"} # set2 = {"banana","orange","grape"} SINTER set1 set2 # 返回 {"banana","orange"} SINTERSTORE set3 set1 set2 # set3 将变成 {"banana","orange"}
6.4.2.3.2 SUNION / SUNIONSTORE
-
SUNION:求并集,返回任意集合里出现过的所有元素(去重)。
bashSUNION <key1> [key2 ... keyN]
-
SUNIONSTORE :把并集结果存储到
destination
。 -
示例:
bashSUNION set1 set2 # 返回 {"apple","banana","orange","grape"} SUNIONSTORE set4 set1 set2 # set4 = {"apple","banana","orange","grape"}
6.4.2.3.3 SDIFF / SDIFFSTORE
-
SDIFF:求差集,返回在第一个集合里而不在其他集合中的元素。
bashSDIFF <key1> [key2 ... keyN]
-
SDIFFSTORE :将差集结果存储到
destination
。 -
示例:
bashSDIFF set1 set2 # 返回 {"apple"} (set1 - set2) SDIFFSTORE set5 set1 set2 # set5 = {"apple"}
6.4.2.4 其他常用或补充命令
- MOVE (Redis 6.2 以前版本仅对 key 生效,对 Set 结构可以使用 SPOP + SADD 或 SRANDMEMBER + SADD 的方式来"移动"元素到另一个集合)。
- RENAME (对整个 key 进行重命名,不仅是 Set 类型才有,不过仅限 key 级别操作,不是移动元素)。
- SSCAN (类似
SCAN
,可分批扫描大集合,语法:SSCAN key cursor [MATCH pattern] [COUNT count]
),在集合规模很大的情况下比一次性SMEMBERS
友好,避免阻塞。
6.4.3 使用场景
- 去重功能
- 将数据插入 Set 并检查返回值或
SISMEMBER
判断重复。 - 例如:用户签到(只需保存用户 ID,保证每个用户只签到一次)。
- 将数据插入 Set 并检查返回值或
- 社交场景
- 用户关注列表、好友列表都可以用 Set 表示;
- 求共同好友:
SINTER userA:friends userB:friends
。 - 计算关注量:
SCARD user:followers
,判断是否互相关注:SISMEMBER user:followers otherUserID
等。
- 标签或权限管理
- 每个用户的标签是一个 Set 或每个标签对应一个 Set。可通过集合运算来灵活地求交集或并集。
- 随机抽取、抽奖
SRANDMEMBER
随机选取;如需"抽完就移除",可用SPOP
。
- 去除重复消息 / 数据过滤
- 在大量数据处理中,可把已处理过的标记存进 Set,若再次出现,表明是重复,跳过即可。
6.4.4 常见注意事项 & Best Practices
- Set 元素数量过多
SMEMBERS
会把所有元素一次性返回,可能导致内存和带宽的压力;- 建议使用
SSCAN
分批遍历;或在设计上拆分成多个小集合,避免单个 Set 过大。
- 大小限制
- 单个 Set 理论上可存放多达数亿个元素(只要内存足够);但操作如交集并集也会消耗相应资源,需评估性能和内存。
- 不要滥用 Set 做有序需求
- Set 是无序的,若需要按照分数、排名等有序排序,应该考虑使用 Sorted Set (ZSet)。
- 若需要按插入顺序遍历或常做 FIFO/LIFO,List 或 Stream 更适合。
- 集合运算开销
SINTER
,SUNION
,SDIFF
等运算在集合很大时也可能比较耗时,尤其当参与运算的集合规模都非常庞大时。- 若是在线查询,需要注意别在关键路径上做大规模集合运算,以免阻塞 Redis 主线程。可以在业务层缓存结果,或在后端服务分层处理。
- 过期策略
- Redis 不支持对 Set 中的单个元素 设置过期,但可以对整个 key 设置过期时间(
EXPIRE key seconds
)。 - 若需要对其中的部分元素做时间控制,可能需要别的结构或自行管理。
- Redis 不支持对 Set 中的单个元素 设置过期,但可以对整个 key 设置过期时间(
6.4.5 小结
Set 在 Redis 中是一个非常基础且强大的数据结构,最显著的特点是:
- 元素唯一 、无序 、快速判重;
- 提供交集、并集、差集等集合运算;
- 适合去重、好友列表、标签管理、抽奖等常见场景。
其核心命令可以简要归纳为:
- 添加 / 删除:
SADD
/SREM
- 查询:
SMEMBERS
(全量),SISMEMBER
(单一判断),SCARD
(数量)SSCAN
(分批扫描)
- 随机:
SPOP
(随机弹出并删除),SRANDMEMBER
(随机获取不删除)
- 集合运算:
SINTER
,SUNION
,SDIFF
以及对应的STORE
版本
- 其他:
SMOVE
(已弃用命令,Redis 版本里没有 SMOVE 可以用 SPOP+SADD 替代),LTRIM
不适用于 Set,SSCAN
用于分批遍历等。
只要掌握这些命令和组合应用,就能应对大部分业务中需要"去重、随机、集合运算"的需求。在实际开发中,还要结合 Redis 的内存管理、过期策略及安全控制,来达成高性能又可靠的 Set 使用方案。
6.5 有序集合(ZSet)
在 Redis 中,有序集合 (Sorted Set)是一种在集合基础上,为每个成员额外关联一个分数(score)的数据结构,常被缩写为 ZSet 。它的特点是:元素不允许重复,但可以按照分数进行排序。Redis 提供了一系列针对有序集合的操作命令,方便我们在排行榜、排名查询、区间筛选、限时排序等场景中使用。
6.5.1 ZSet 的存储与特点
- 内部结构
- Redis 使用一种结合了 跳表(Skiplist) + 哈希表(Hashtable) 的结构来存储 ZSet。
- 跳表可以让我们在 O(log N) 的复杂度下进行有序操作(如范围查找、排序等),哈希表负责快速定位元素。
- 数据特征
- 元素唯一:和 Set 一样,每个 ZSet 里元素(member)是唯一的。
- 分数(score)可以重复:不同的 member 可以拥有相同的分数,但是 member 自身不能重复。
- 按分数排序:Redis 可以根据 score 值对元素进行从小到大的排序,也可以支持倒序操作。
- 常用场景
- 排行榜:比如按积分、热度、时间戳等排序;
- 时间序列:score 代表时间戳,按时间顺序进行查询或截取;
- 带权重的列表:以 score 作为优先级或权重值,随时能快速获取指定排名或分数区间的元素。
6.5.2 有序集合 ZSet 的常见命令
6.5.2.1 添加、更新、删除元素
6.5.2.1.1 ZADD
-
作用 :向有序集合中添加一个或多个
member
,并设置它们的分数score
。若 member 已存在则更新其 score。 -
语法:
bashZADD <key> [NX|XX] [CH] [INCR] <score1> <member1> [<score2> <member2> ...]
-
常用选项:
NX
:仅当 member 不存在时才添加;XX
:仅当 member 已存在时才更新;CH
:返回被修改的成员数量(包括 score 发生变动的情况);INCR
:对给定 member 的分数执行加法操作,而不是直接设置分数。
-
示例:
bashZADD leaderboard 100 "playerA" ZADD leaderboard 200 "playerB" 150 "playerC" # 如果 playerA 已存在,则其 score 会被更新为 100
6.5.2.1.2 ZREM
-
作用:从有序集合中删除指定元素,支持删除多个。
-
语法:
bashZREM <key> <member1> [member2 ...]
-
示例:
bashZREM leaderboard "playerC" # 删除 "playerC" 这个 member
6.5.2.1.3 ZINCRBY
-
作用:对指定 member 的分数进行增量操作(加上给定的数值)。
-
语法:
bashZINCRBY <key> <increment> <member>
-
示例:
bashZINCRBY leaderboard 50 "playerA" # playerA 的分数在原来的基础上 +50
-
注意:如果 member 不存在,会先以 score=0 创建该 member,再加上 increment。
6.5.2.2 按序查询、获取排名
6.5.2.2.1 ZRANGE / ZREVRANGE
-
作用 :按 score 升序(
ZRANGE
)或降序(ZREVRANGE
)获取指定索引区间内的元素。 -
语法:
bashZRANGE <key> <start> <stop> [WITHSCORES] ZREVRANGE <key> <start> <stop> [WITHSCORES]
-
示例:
bashZRANGE leaderboard 0 -1 # 返回所有成员,按分数升序 ZRANGE leaderboard 0 2 WITHSCORES # 返回前三名,同时显示分数 ZREVRANGE leaderboard 0 2 WITHSCORES # 返回分数最高的三名
-
索引区间:
0
表示第一个元素(最小或最大的视升序/降序而定),-1
表示最后一个元素;- 依次类推,
start
、stop
可以是正负索引。
6.5.2.2.2 ZRANGEBYSCORE / ZREVRANGEBYSCORE
-
作用 :按 score 值进行范围查询,返回分数在
[min, max]
(或[max, min]
)区间内的 member 列表。 -
语法:
bashZRANGEBYSCORE <key> <min> <max> [WITHSCORES] [LIMIT offset count] ZREVRANGEBYSCORE <key> <max> <min> [WITHSCORES] [LIMIT offset count]
-
说明:
ZRANGEBYSCORE leaderboard 100 200
:获取 score 处于 100~200 范围内的所有成员,按升序。ZREVRANGEBYSCORE leaderboard 200 100
:同样的分数区间,但按分数从高到低排序。LIMIT offset count
:可对结果做分页或截取。+inf
和-inf
:可用于表示无穷大和无穷小;(100
表示大于 100 而不是大于等于(带括号表示排除)。
6.5.2.2.3 ZRANGEBYLEX / ZRANGEBYDICT(Redis 6.2+ 支持),与按 Member 字典序相关
- 在某些场景下可以按 Member 的字典序,而非 score 进行范围查询;这是比较少用的操作,了解即可。
6.5.2.2.4 ZRANK / ZREVRANK
-
作用:获取某个 member 在有序集合中的排名(下标)。
-
语法:
bashZRANK <key> <member> ZREVRANK <key> <member>
-
返回值:
- 若 member 存在,返回其以 0 开始的排名;
- 若不存在,返回
nil
。
-
举例:
bashZRANK leaderboard "playerA" # 返回 playerA 在升序下的名次(0 表示 score 最小的那个,数值越大表示分数更高的排名) ZREVRANK leaderboard "playerA" # 返回 playerA 在降序下的名次(0 表示分数最高的)
6.5.2.2.5 ZCARD / ZCOUNT
-
ZCARD:返回有序集合的基数(元素总数)。
bashZCARD <key>
-
ZCOUNT:统计指定 score 区间内的元素数量。
bashZCOUNT <key> <min> <max>
例如:
ZCOUNT leaderboard 100 200
返回分数在 [100,200] 的元素数。
6.5.2.3 删除和范围操作
6.5.2.3.1 ZREM、ZREM_RANGE_BY_SCORE、ZREM_RANGE_BY_RANK
-
ZREM:前面提过,按 member 删除指定元素。
-
ZREMRANGEBYSCORE:删除score在给定区间内的所有元素。
bashZREMRANGEBYSCORE <key> <min> <max> # 如 ZREMRANGEBYSCORE leaderboard 0 100 -> 删除 score <= 100 的所有元素
-
ZREMRANGEBYRANK:按排名下标区间删除元素。
bashZREMRANGEBYRANK <key> <start> <stop> # 如 ZREMRANGEBYRANK leaderboard 0 9 -> 删除排在前 10 名的元素
6.5.2.3.2 ZINTERSTORE / ZUNIONSTORE / ZDIFFSTORE (Redis 6.2+)
和普通 Set 类似,Redis 也提供了对 ZSet 的交集、并集、差集 运算,但是会对 score 做相应的合并或计算。常见的是 ZINTERSTORE
和 ZUNIONSTORE
。
- ZINTERSTORE:计算给定的一个或多个有序集合的交集,将结果存储到新集合中,并根据每个元素在各集合中的 score 值执行相应的加权或求和。
- ZUNIONSTORE:并集操作,score 同样可以加权或求和。
示例:
bash
ZADD zset1 1 "a" 2 "b"
ZADD zset2 2 "a" 3 "c"
ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3
# 这里 2 表示有 2 个集合,WEIGHTS 2 3 分别表示 zset1 的 score 乘以2, zset2 的 score 乘以3
# "a" 在 zset1 score=1, zset2 score=2
# 计算交集则 "a" 存在于两个集合中,score = 1*2 + 2*3 = 2 + 6 = 8
# "b" 只在 zset1, "c" 只在 zset2,故它们不在交集里
# out = {"a":8}
注意:Redis 7.0+ 也引入了
ZDIFFSTORE
(差集),不过在实际中用得相对少一些。
6.5.3 常见使用场景
- 排行榜
- 例如游戏积分榜、电商热卖榜、点赞数排行等;
- 可以通过
ZADD
不断更新用户的分数,然后使用ZREVRANGE
或ZREVRANK
获取某人排行或前 N 名; ZINCRBY
用于积分累加,ZRANK
/ZREVRANK
用于查询当前名次。
- 实时排序
- 每次用户操作(点赞、查看、消费等)都可以更新其相关分数;
- 通过按分数的正序或倒序可实现某些实时刷新排名的功能,比如直播间送礼排行榜、产品热度排名等。
- 延时队列 / 时间调度
- 把 future task 存成
(score=执行时间, member=任务ID)
,利用ZRANGEBYSCORE
查询到期任务,进行处理后删除; - 可以替代一些简单的定时任务逻辑。
- 把 future task 存成
- 时间序列
- 以时间戳作为 score,member 是事件或数据 ID;
- 使用
ZRANGEBYSCORE
或ZREVRANGEBYSCORE
在一定时间范围内获取数据。
6.5.4 注意事项 & Best Practices
- score 的精度
- Redis score 本质是一个双精度浮点数(double),尽量避免过于依赖小数精度(有时可能出现浮点误差)。
- 如果分数是整数,推荐直接用整数(如 123456)或用一个长整型来表达时间戳/分值以减小误差。
- ZADD 频繁更新
- 更新分数只需再次执行
ZADD <key> <score> <member>
,若 member 存在则覆盖;或ZINCRBY
增量更新。 - 频繁更新大规模 ZSet 时要注意是否会带来内存或操作耗时,毕竟跳表需要维护顺序。
- 更新分数只需再次执行
- 排行榜前 N vs. 全量遍历
ZRANGE key 0 9
、ZREVRANGE key 0 9
:获取前 10 名或后 10 名非常高效;- 如果要获取所有成员(百万级)并排序,返回时就会产生大流量,占用 Redis 线程,对性能影响较大。
- 可以考虑分页或分段查询:
ZREVRANGE key start end
,或者用ZRANGEBYSCORE + LIMIT
进行分页,不过也要注意性能。
- 删除操作
ZREM
只针对指定 member;- 对于区间删除(score 范围或排名范围)可使用
ZREMRANGEBYSCORE
或ZREMRANGEBYRANK
。 - 这些批量删除操作会在一定程度上阻塞 Redis 主线程,规模很大时也需注意分拆或异步管理。
- 内存占用
- ZSet 同时维持哈希表 + 跳表结构,会比普通 Set 消耗更多内存;
- 大量 ZSet 时需考虑内存大小、持久化和主从复制对性能的影响。
- 过期策略
- Redis 不支持对 ZSet 的单个 member设置过期,只能对整个 key 设置过期;
- 如果需要对某些分数或元素做定期清理,可以定时执行
ZREMRANGEBYSCORE
或业务逻辑判断后删除。
6.5.5 小结
有序集合(ZSet)是 Redis 中一个非常重要 的高级数据结构,结合了"集合去重"与"分数排序"的优势,能在很多排行榜 、调度 、排序场景下大显身手。
其核心命令可简要概括为:
- 增、删、改、查
ZADD
:添加/更新元素ZREM
:删除指定元素ZINCRBY
:分数自增ZRANK
/ZREVRANK
:查看排名(索引位置)ZCARD
:ZSet 大小
- 获取数据
ZRANGE
/ZREVRANGE
:按索引区间获取排序结果ZRANGEBYSCORE
/ZREVRANGEBYSCORE
:按分数区间获取ZCOUNT
:分数区间计数
- 范围删除
ZREMRANGEBYSCORE
、ZREMRANGEBYRANK
- 交并集操作
ZINTERSTORE
/ZUNIONSTORE
:对多个 ZSet 做交集 / 并集,score 可以加权合并
掌握了这些命令及应用思路,就能灵活地在Redis中构建实时排行榜、延时队列、时间序列、计分系统等功能。配合其他类型(String、Hash、Set 等)的混合使用,可以构建出更丰富、更高效的 Redis 数据模型来满足不同业务需求。
6.7 时间复杂度
在 Redis 中(以及更多计算机科学领域),我们常用 Big-O 表示法(如 O(1)、O(log N)、O(N) 等)来衡量算法或操作的时间复杂度 ,它代表了输入规模增大时,操作耗时的增长趋势。下面我们分几个层面来理解:
6.7.1 什么是 O(1) 和 O(log N)?
- O(1) (常数时间):
- 表示无论数据规模 N N N 多大,操作所需的时间都近似于一个常数,与 N N N 无关。
- 例如,用哈希表(Hash Table)查询一个元素的平均时间复杂度是 O(1):查找过程可在常数时间内完成。
- 当然这是平均情况。哈希表在最坏情况下(冲突严重)可能退化到 O(N);但在良好散列和低冲突的场景下可视为 O(1)。
- O(log N) (对数时间):
- 表示操作所需时间随数据规模 N N N 的增加按照对数规律增长。对数函数增长速度比线性函数慢得多,所以当 N N N 很大时,O(log N) 算法仍然比较高效。
- 例如,用平衡二叉搜索树 、跳表(Skiplist) 、B+树等结构进行插入、查找、删除,通常都能在 O(log N) 时间内完成。
- Redis 的**有序集合(ZSet)**内部默认用"跳表 + 哈希表"来存储:
- 跳表部分提供按分数排序、区间查询等有序操作的能力(O(log N));
- 哈希表部分用来快速定位元素是否存在(O(1) 平均情况)。
6.7.2 为何跳表的有序操作是 O(log N)?
跳表(Skiplist) 是一种随机化的数据结构,结构上类似多层链表,每一层都跳过一些节点,以加速查找、插入和删除。
- 在理想状态下,每向上走一层,节点数量就减少一半;
- 因此要找到某个元素或一个分数区间时,只需从高层往低层"逐渐逼近",访问的节点数大约是 log 2 N \log_2{N} log2N量级;
- 整体上插入、删除、查找等操作都能保持在 O(log N) 水平。
举个形象的例子:
- 在一个排好序的很长的链表中,如果只能一格一格往后找,那么查找就是 O(N)。
- 但如果你先在"顶层链表"里,隔几个节点就有一个索引,可以快速跳过若干节点;再往下一层,更密集的索引帮助你再定位......如此层层逼近目标,就能极大缩短"跳"的次数,这就是为什么能够得到 O(log N) 的时间复杂度。
6.7.3 为何哈希表的操作是 O(1)(平均情况)?
哈希表(Hash Table) 通过哈希函数把键(Key)映射到一个桶(bucket)或槽(slot)里,理论上可以直接通过哈希函数一次定位到存储位置。
- 在冲突不多、散列均匀的情况下,查找、插入、删除操作只需要常数步(O(1))就能完成;
- 这是因为无论有多少数据(N 很大),哈希函数的计算和一次到位的寻址,都不太依赖 N 的规模;
- 不过,如果哈希冲突非常多或散列不均,最坏情况可能要在某个桶里的链表挨个遍历(O(N)),但平均来说,好的哈希设计能让大部分操作维持在 O(1)。
6.7.4 Redis 中具体体现
- ZSet 的跳表部分 :
- 当我们需要"根据分数范围取某些元素"或"得到某个分数在有序集合中的排名"时,就需要依赖有序结构(跳表)来进行区间遍历或排名查找,复杂度约为 O(log N)(有序查找、插入、删除)或 O(log N + M)(找出 M 个结果)。
- 例如命令
ZRANGEBYSCORE
、ZREVRANGEBYSCORE
就是利用跳表快速定位到指定分数区间的起始位置,然后顺序扫描出结果。
- ZSet 的哈希表部分 :
- 当我们执行 "给某个元素设置分数" 或 "查一个元素在不在这个 ZSet 里" 时,可先通过哈希表在 O(1) 平均时间内找到该元素;如果更新分数,则跳到跳表对应位置更新节点;
- 这样就兼顾了快速定位 (哈希表)和有序操作(跳表)。
- Hash(哈希)类型 、Set 、HashMap 等其他结构:
- 也都基于哈希表,插入、查询、删除的平均复杂度是 O(1)。
- List 或 Stream :
- 大部分情况下是 O(1) 地在头尾插入或弹出(LPUSH、LPOP、RPUSH、RPOP),但随机访问或索引查找时就需要 O(N)(链表或类似结构需遍历)。
6.7.5 如何理解这些复杂度?
- Big-O 并非绝对耗时 :它只描述增长趋势,并不意味着一旦是 O(log N) 就一定比 O(1) 慢,或者 O(N) 就一定比 O(log N) 慢;在小规模数据时,常数和常数因子也会很重要。
- 平均 vs. 最坏:哈希表的 O(1) 通常指平均复杂度;跳表/树型结构的 O(log N) 指大多数情况下都会如此,但在极端情况下也可能退化。
- 选择合适的数据结构 :如果你需要有序查询 、范围操作 、排名 等功能,就需要跳表/树这样支持排序的结构(ZSet)。如果只是key -> value 快速查询(比如 Set/Hash),哈希表就足够好,简单粗暴且平均 O(1)。
6.7.6 小结
- O(1) :操作耗时不随着数据规模变化而显著增长,是平均情况下哈希表操作最有吸引力的特征。
- O(log N):跳表、平衡树等结构能实现高效的有序操作。随着 NN 增大,耗时按照 级别增长,比 O(N) 好很多,但比 O(1) 大。
- Redis ZSet 内部"跳表 + 哈希表"的组合可以理解为:
- 哈希表提供快速定位(O(1) 平均)
- 跳表提供有序操作(O(log N))
- 二者互补,使得 ZSet 在"既需要有序,又要能快速查某元素"的场景中表现优异。
这些就是常见的时间复杂度 概念和在 Redis 内部的具体落地 。当在应用层设计数据结构或分析 Redis 性能时,理解这些复杂度有助于我们选择合适的操作 并提前预估可能的开销。
6.7 消息队列
在消息队列或者消息传递领域,一般会提到 两种常见的消息模式:
- 生产者-消费者(Producer-Consumer)
- 发布-订阅(Publisher-Subscriber,Pub/Sub)
6.7.1 生产者-消费者(Producer-Consumer)
6.7.1.1 模式概念
- 生产者(Producer):负责发送消息或任务到队列(Queue)中。
- 消费者(Consumer):从队列中取出消息或任务并进行处理。
常见的形态是"消息队列":生产者把消息投递到队列,消费者从队列里取出并消费,最终队列中存储的是待处理的消息。
6.7.1.2 工作流程
- 生产者将消息发送到消息队列;
- 消息队列负责临时存储这些消息,充当缓冲区;
- 消费者 从队列中轮询或阻塞获取消息(通常是 FIFO,先进先出 first in,first out),然后执行处理;
- 处理完毕后,消息即被标记为消费完成或从队列移除。
6.7.1.3 主要特点
- 解耦:生产者和消费者可以异步运行,生产者发送后就可以继续处理其他任务,不用等待消费者处理完成。
- 缓冲:消息队列在生产者和消费者之间提供了一个缓冲池,能应对突发的高并发或流量不均衡,避免直接把压力传递给消费者。
- 可靠性(可选):大多数消息队列中间件(RabbitMQ、Kafka、RocketMQ 等)会提供持久化、确认(ACK)机制来保证消息不丢失。
- 竞争消费 :通常一个队列可被多个消费者并行消费,每条消息只会被其中一个消费者处理一次,适合"任务分发"或"分布式工作池"模式。
6.7.1.4 典型应用场景
- 异步任务处理:如发送短信、邮件、图像处理,把耗时任务扔进队列中,再由工作进程去处理。
- 削峰填谷:电商大促时,订单处理可先进入队列以防止瞬时并发过高;消费者慢慢消化队列里积压的订单。
- 分布式系统间解耦:生产者和消费者可以各自升级或故障重启,而不影响对方的逻辑。
6.7.2 发布-订阅(Pub/Sub, Publisher-Subscriber)
6.7.2.1 模式概念
- 发布者(Publisher):向某个主题或频道(Topic/Channel)发布消息。
- 订阅者(Subscriber) :对指定主题或频道进行订阅,一旦有新的消息发布,就会被推送 或接收到。
可以把它理解为"广播"式:所有订阅了某个频道的订阅者,在发布者推送消息后,都会各自收到一份消息。
6.7.2.2 工作流程
- 订阅者先声明对某个主题/频道感兴趣,并订阅之;
- 发布者向该主题/频道发布消息;
- 消息中间件 负责将消息同时分发给所有已订阅该主题/频道的消费者;
- 每个订阅者都能够各自收到该条消息,并进行自己的处理。
6.7.2.3 主要特点
- 一对多 :一个发布者发布的一条消息可以同时被多个订阅者 接收,这与"生产者-消费者"中单条消息只被一个消费者处理截然不同。
- 实时推送 :当发布者有新消息时,订阅者能立即收到;
- 不保证存储(取决于实现):如果某个订阅者离线或没有及时消费,消息可能就错过了(除非使用带持久化/主题回溯的消息系统,例如 Kafka、Redis Streams 也有一定存储能力,但传统"Redis Pub/Sub"则不存储历史消息)。
- 松耦合:发布者不需要知道订阅者是谁,订阅者也不需要知道具体是哪一个发布者发的消息。它们只通过主题来"对接"。
6.7.2.4 典型应用场景
- 广播通知:系统中某个事件需要同时通知多个服务或模块。
- 实时推送:聊天房间、直播间弹幕、股票行情等,需要向所有在线订阅者推送最新数据。
- 异步事件总线:在微服务架构中,一些服务可能只需要监听特定事件来触发自己的逻辑,发布者只管发布事件,多个订阅者各自执行不同流程。
6.7.3 对比与总结
维度 | 生产者-消费者(消息队列) | 发布-订阅(Pub/Sub) |
---|---|---|
消息传递关系 | 一对一 :队列中的一条消息只会被一个消费者处理 | 一对多 :一个消息可同时被多个订阅者接收 |
消息存储 | 通常会存储在队列里,若消费者未及时消费,消息也不会丢 | 取决于系统实现,传统 Pub/Sub 通常不存储历史消息 |
消费模式 | 消费者竞争消费同一个队列;可实现分布式工作池、并行处理 | 所有订阅该主题的订阅者都能各自收到一份消息,并行处理 |
适用场景 | 异步处理、队列缓冲、削峰填谷、解耦分布式任务 | 实时广播通知、事件订阅、多客户端同时接收相同消息 |
消息确认 & 重试 | 通常具备ACK/重试机制,确保消息被正确处理 | 传统简单 Pub/Sub 一般无重试/确认,消息分发后即丢 |
核心关注点 | 可靠消费,队列实现排队、缓冲;保证单条消息只被处理一次 | 消息广播,多消费者同时收到,不保证是否被落地存储 |
6.7.4 延伸与选择
- 生产者-消费者 主要关注:
- 消息排队 、可靠处理 、可控的并发消费、处理完消息后就出队。
- 适用于工作队列、任务处理、订单处理、分布式流水线等。
- 发布-订阅 主要关注:
- 分发:一次消息,多个订阅者同时收到;
- 广播 或多对多模式;
- 适用于实时性推送、广播通知、事件总线等。
- 组合使用 :
- 有时一个系统中既需要排队处理 ,又需要广播通知 ,则会在不同子模块选择不同模式,或者使用支持多种模式的消息中间件(如 Kafka 既支持"订阅分区",也能做分布式队列;RabbitMQ 可用交换器+队列配合实现多播等)。
- 不同系统实现
- 如 RabbitMQ、ActiveMQ、RocketMQ 更偏向队列模型(生产者-消费者),但也能通过不同的 exchange 类型(主题交换、fanout 等)来实现类似发布-订阅;
- Kafka 虽然更多用于订阅(消费者组对 Topic 的订阅),但本质也能做队列(同一个分区内仍是单消费者消费)。
- Redis 的 Pub/Sub 属于较简单的纯广播,不存储历史消息,而 Redis Streams 则提供了可做队列或订阅的能力,也支持 Consumer Group。
6.7.5 小结
- 生产者-消费者 (消息队列模型):
- 核心诉求 :每条消息由唯一消费者处理,注重可靠消费、解耦、延迟容忍,可以做异步处理、削峰填谷。
- 发布-订阅 (Pub/Sub 模型):
- 核心诉求 :一次消息可同时被多个订阅者接收,注重实时广播、事件分发;不一定有存储与确认机制。
两种模式各有适用场景,在实际项目中往往结合使用,或者在同一个消息系统内使用不同的交换策略实现不同效果。选择哪种模式取决于消息要给多少人接收、是否需要队列缓冲、是否要落盘与确认、是否需要多对多的实时通信等业务需求。
6.7.6 Redis 实现消息队列
在许多场景下,Redis 经常被用作轻量级的消息队列(Message Queue),实现简单的生产者-消费者模型或发布订阅功能。根据 Redis 版本和需求的不同,常见的实现方式主要有:
- 使用 List(列表)+ 阻塞弹出 :最传统的方式,利用
LPUSH
/RPUSH
与BLPOP
/BRPOP
组合,构建简单队列。 - 使用 Redis Pub/Sub:更适合实时广播式的消息分发,但消息不能持久化。
- 使用 Redis Streams(5.0+):官方推荐的现代化消息流结构,支持持久化、有序存储、消费组、ACK 等功能,更适合复杂的消息队列场景。
下面依次介绍各方案的特点与适用场景,并总结常见注意事项。
6.7.6.1 使用 List 构建简单队列
6.7.6.1.1 基本原理
- 生产者:在列表尾部或头部插入消息
- 常用
LPUSH queue "message"
或RPUSH queue "message"
- 常用
- 消费者:使用 阻塞 命令
BLPOP
或BRPOP
来弹出消息- 当队列为空时,消费者阻塞等待,直到超时(若指定)或有新消息入队
这样就构成了一个FIFO(先进先出)或类似队列的模型。例如:
bash
# 生产者往右端插入
RPUSH task_queue "task1"
RPUSH task_queue "task2"
# 消费者阻塞等待
BLPOP task_queue 0
# 如果为空,则阻塞等待;有数据就立刻返回
- 如果需要严格的 FIFO,可约定同一端入队,另一端出队,比如:
- 生产者使用
RPUSH
- 消费者使用
BLPOP
- 数据会从右端进,左端出,实现最常见的队列顺序。
- 生产者使用
6.7.6.1.2 特点与适用场景
- 优点 :
- 实现简单,命令直观;
- 无需额外插件或复杂配置,适合小规模队列或临时需求;
BLPOP
/BRPOP
是阻塞模式,避免频繁轮询,实时性好。
- 缺点 :
- 无内置消息确认(ACK) 、重试 、消费组等机制;如果消费者在处理后崩溃,消息就丢失了(除非自行实现"处理后写回"流程)。
- 不便于多消费者并行处理同一个队列里的同一条消息,需要自己设计逻辑避免重复消费。
- 没有持久化机制时,Redis 重启后现有队列内容是否能够完整恢复,需结合 AOF/RDB 机制进行持久化;但如果严格消息可靠性要求很高,Redis 并不是专业的"持久化消息队列"解决方案(可考虑 RabbitMQ、Kafka)。
这种方式非常适合简单任务队列 或小规模异步处理:
- 例如把用户的某些操作异步处理掉,不要求强一致或复杂消息确认;
- 如果对消息丢失容忍度高或可以接受自己做补偿机制。
6.7.6.2 使用 Redis Pub/Sub
6.7.6.2.1 基本原理
- Redis 的 Publish/Subscribe功能实现了发布和订阅模型:
- 发布者 通过
PUBLISH channel message
向指定频道发送消息; - 订阅者 使用
SUBSCRIBE channel
监听该频道并实时接收消息;
- 发布者 通过
- 这是一种广播式 或一对多的消息分发:只要多个订阅者都订阅了同一个 channel,它们都会收到发布者的消息副本。
6.7.6.2.2 特点与应用场景
- 优点 :
- 实时性好,消息一发送,所有订阅者都能立即收到;
- 适合推送 或通知类场景,比如聊天室、服务器实时事件广播等。
- 缺点 :
- 无持久化 :消息不是存储在 Redis 中,而是一经发布即向所有订阅者推送,如果当时订阅者掉线或来不及处理,就会丢失。
- 无法回放历史消息,也没有内置的队列特性(排队、确认、重试)等。
因此,Redis Pub/Sub 不大适合"传统队列"需求,更像实时广播 ,用来做直播聊天室、在线游戏通知、事件总线等。在需要可靠消费的场景中则不合适。
6.7.6.3 使用 Redis Streams(5.0+)
6.7.6.3.1 Streams 简介
- Redis Streams 是 Redis 5.0 引入的新数据结构,专为消息流(log stream)设计。
- 它既可以看作有序存储的消息队列,也可像 Kafka 一样保留历史消息,客户端可以从指定位置开始读取、重复读取并标记消费进度。
6.7.6.3.2 关键概念
- Stream :按时间或插入顺序将消息(Entry)排成有序队列,每条消息带有一个 ID(类似时间戳+序列号)。
- XADD :生产者向 Stream 中添加消息(
XADD mystream * field1 value1 field2 value2 ...
)。 - XREAD / XREADGROUP :消费者读取消息,可指定从哪个 ID 开始读,实现增量消费。
- Consumer Group(消费者组) :允许多个消费者分组消费同一个 Stream 中的数据;Redis 会自动做分配 ,并支持消息ACK 和未确认消息管理,适合多消费者并行且保证消息不会重复消费或丢失太多。
- XACK / XPENDING :通过ACK机制,只有消费者成功处理后才算消息被确认,从而减少消息丢失。未确认的消息可以查看和重新分配。
6.7.6.3.3 Streams 使用示例(简化)
bash
# 生产者
XADD mystream * sensor-id 1 temperature 30.2
# 消费者(若非消费者组)
XREAD COUNT 10 STREAMS mystream 0
# 读取 mystream 中的全部消息(从 id=0 开始)
# 创建消费者组
XGROUP CREATE mystream mygroup $ MKSTREAM
# 消费者在该组内读取
XREADGROUP GROUP mygroup consumer1 COUNT 10 STREAMS mystream >
# 读取从上次读取位置以后的新消息
# 消费后,手动确认
XACK mystream mygroup <message-id1> <message-id2> ...
# 标记这些消息已成功处理
6.7.6.3.4 优势与特点
- 可靠性:通过消费者组和 ACK 机制,避免消费者崩溃导致消息永久丢失。
- 多消费者并发:同一个消费者组可以有多个消费者竞争消费消息,Redis 会自动分配未被其它消费者领取的消息给它们。
- 消息回溯 :默认消息不会自动丢弃,可以根据需要保留一定长度或时间周期,以便后续读重或做故障回放。也可通过
MAXLEN
配置限制流长度。
6.7.6.3.5 适用场景
- 分布式系统 中需要可靠消息队列:既要并发消费,又要防止某台消费者挂了导致消息丢失;
- 需要历史消息回溯(至少在一定范围内),比如日志、事件存档等;
- 相对大规模、严谨的异步消息处理,比 List 方案更"专业",且支持高级特性。
6.7.6.4 选择哪种方式?
- 最简单:List + 阻塞弹出
- 适合轻量级 的单一队列,不需要复杂确认或重复消费管理;
- 代码简单易上手,如果消息丢了问题不大(或能用自定义补偿),就行。
- Pub/Sub
- 适合实时推送 或一对多 通知场景,不做持久化,消息即时投递后即丢弃;
- 不适合需要存储与可靠消费的排队场景。
- Redis Streams
- 适合对消息的可靠性、持久性、多消费者管理有一定要求的场景,提供近似 MQ 的能力;
- 结构更复杂,需要掌握
XREADGROUP
,XACK
,XPENDING
,XCLAIM
等命令; - 如果需要专业、超大规模的队列并发(数十万~百万 QPS 级别)或高级分布式事务,可考虑专业消息系统(Kafka、RabbitMQ、RocketMQ 等);但 Redis Streams 对于中等规模的可靠队列、实时处理依然非常合适。
6.7.6.5 常见注意事项
- Redis 单线程特性
- 所有命令都在单线程内执行,若消息量巨大、消费者或生产者并发极高,可能造成阻塞。
- Redis 6.0 引入多 I/O 线程,但命令执行依然是单线程,需根据实际场景评估性能。
- 持久化
- 如果你希望消息在 Redis 重启后保留,需要启用 RDB / AOF 持久化,但也得考虑性能开销 与数据一致性。
- 如果需要100%不丢消息,需要确保 Redis 持久化策略和硬件环境都可靠。
- 内存限制
- Redis 主要在内存中操作,如果消息积压量非常大,就可能导致内存压力,或大量写入也影响性能。可开启 eviction 策略,但那会导致可能的消息丢失。
- 消费端处理失败重试
- 简单 List 模型下,若处理失败,需要手动将消息再 push 回队列,或记录日志后再做补偿。
- 使用 Streams + 消费者组可以重新分配 未确认的消息(
XPENDING
,XCLAIM
),从而实现一定程度的重试、死信队列等机制。
- 吞吐 vs. 功能
- Redis 消息队列方式更倾向于"轻量、高速、短期缓存/队列"场景;不提供像专业 MQ 那样完善的路由、扩展、跨节点分布功能。
- 如果需求简单、无需过度扩展,Redis 够用且非常快;若是大规模企业级 MQ 场景或需要严格事务和更灵活的路由策略,可考虑 RabbitMQ、Kafka、RocketMQ 等。
6.7.6.6 小结
Redis 作为内存数据库,本身并不是专业的消息中间件,但它速度快、命令简单 ,对很多轻量级消息队列 或实时通知场景来说很实用:
- List +
BLPOP
/BRPOP
:最经典的简易队列方案。适合单队列、简单生产-消费、对可靠性无特殊要求的场景。 - Pub/Sub :适合实时广播的一对多订阅,不保留历史消息,也无确认机制。
- Redis Streams:Redis 5.0+ 官方提供的"日志流"数据结构,支持有序存储、消费者组、ACK 确认、回溯消息等,能满足中等规模、较高可靠性要求的消息队列场景。
根据业务需求,选择合适的模式和实现方式,才能在性能与可靠性之间找到平衡。对简单场景,List 队列最易上手;若需要相对专业的队列功能又想依赖 Redis生态,则 Streams 是更好的选择;若需要高可靠性和大规模分布式集群,可进一步研究专业MQ或结合 Redis 与其他系统。
7.Redis 主从复制
7.1 Redis 主从复制的基本概念
- 主节点(Master)与从节点(Replica)
- 在 Redis 术语中,传统称呼是 Master 和 Slave ,从 5.0 开始社区倾向使用 Master 和 Replica 来替代。
- Master 负责处理写请求,并将写操作同步到 Replica;Replica 负责接收 Master 同步过来的数据,并对外提供读服务(如果配置了
replica-read-only no
,也可以写,但通常不建议这样做)。
- 异步复制(Asynchronous Replication)
- Redis 的主从复制本质上是 异步复制,即 Master 在将写操作返回给客户端后,会在后台将数据同步给 Replica。
- 对于强一致性要求较高的场景,需要做额外的配置或处理(如 Wait、min-replicas-to-write 等机制)。
- Sentinel 与 Cluster 模式
- Sentinel 是 Redis 提供的自动故障检测与主从切换的工具,通过监听主节点的健康度,来进行自动化的故障转移。
- Cluster 模式则是 Redis 官方推荐的多节点分片方案,同样具备部分自动的故障转移能力,且更适合于分布式水平扩展的场景。
- 无论使用 Sentinel 还是 Cluster,本质上都基于主从(主备)复制来实现高可用。
7.2 Redis 主从复制的工作流程
要理解 Redis 7.4 的复制特性,我们先回顾一下 Redis 在 2.8 之后引入的 PSYNC(部分重同步) 机制,以及 6.x、7.x 中的改进。它们在底层架构上没有根本性变化,但对复制的稳定性、内存使用和网络消耗做了优化。
- 初次全量复制(Full Synchronization)
- 当 Replica 节点第一次向 Master 发起复制请求,或者复制状态失效(无法进行部分重同步)时,会触发一次 "全量复制"。
- Master 会执行以下操作:
- 触发保存 RDB 文件(或者使用已有的 RDB 文件)
- 通过网络将 RDB 文件发送给 Replica
- 在 RDB 文件发送完毕后,会将积累的增量命令(AOF 或命令缓存)发送给 Replica
- Replica 在收到 RDB 后会先将其加载到内存,再应用后续增量命令,直到与 Master 同步到相同的 offset(复制偏移量)。
- 部分重同步(Partial Resynchronization)
- 如果 Replica 与 Master 断开连接(网络故障、超时等)后重新连接,Replica 会把自己保存的 Replication Offset (复制偏移量)和 Replication ID(同步 ID)发送给 Master。
- 如果 Master 还能在复制积压缓冲区(Replication Backlog)中找到从 Replica 上次断开后遗漏的那部分数据,就可以直接进行 "部分重同步",只发送缺失的命令给 Replica,避免重复执行一次全量数据同步,从而减少网络和 IO 开销。
- 复制偏移量(Replication Offset)与复制缓冲区(Replication Backlog)
- Master 在处理命令时,会维护一个全局增长的 offset ,同时把最近一段时间的写命令缓存到 Backlog。
- 当 Replica 发起 PSYNC 请求时,会带上自己的 offset 和上一次复制的 Master Replid,Master 检查是否能满足部分重同步,否则只能再次进行全量复制。
- 主从复制的异步特性
- Redis 复制是 异步 的,Master 在写操作完成后就返回给客户端,后台才向 Replica 发送复制数据。
- 如果需要保障数据安全(例如避免主节点宕机时数据丢失过多),需要配合
min-replicas-to-write
、min-replicas-max-lag
等配置项来做一定程度的同步保障。
7.3 配置与运维要点
7.3.1 基本配置
在 redis.conf
或者使用 CONFIG SET
命令,可以进行以下配置来启用和管理复制功能:
- Master 侧配置
一般情况下,主节点不需要额外做什么特殊配置,只要正常提供服务即可。
如果你为 Master 设置了访问密码(例如 requirepass
),你需要在 Slave 端做相应的身份认证配置。
-
Slave 侧配置
bashreplicaof <master-ip> <master-port> replicaof 10.0.0.42 6379
或在 Redis 运行中执行:
bashREPLICAOF <master-ip> <master-port>
-
密码相关
-
如果 Master 设置了访问密码,需要在 Replica 配置:
bashmasterauth <master-password> masterauth 123456
-
Replica 自身的
requirepass
用于客户端访问 Replica 时的密码,与复制用的masterauth
并不相同。
-
-
只读模式
- Redis 默认开启
replica-read-only yes
,从节点只接受读请求,不允许外部写,从而避免写入冲突。
- Redis 默认开启
-
延迟优化
repl-disable-tcp-nodelay
、client-output-buffer-limit
以及网络相关参数可视业务场景进行调优。
bash
# 通过命令行直接设置(Redis的版本要保持一致,如果不一致可能会无法复制)
# 所有从节点执行
127.0.0.1:6379> REPLICAOF 10.0.0.42 6379
127.0.0.1:6379> CONFIG SET masterauth 123456
# slave1 从节点状态
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:10.0.0.42
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_read_repl_offset:64185
slave_repl_offset:64185
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:8ba3aa667f0d3e54f4c6499d4d42ce0f68ccce8f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:64185
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:962
repl_backlog_histlen:63224
127.0.0.1:6379>
# slave2 从节点状态
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:10.0.0.42
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_read_repl_offset:2109
slave_repl_offset:2109
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:8ba3aa667f0d3e54f4c6499d4d42ce0f68ccce8f <-- 主节点的 replid
master_replid2:0000000000000000000000000000000000000000 <-- 备用 replid
master_repl_offset:2109
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:738
repl_backlog_histlen:1372
127.0.0.1:6379>
# 主节点状态
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.0.0.40,port=6379,state=online,offset=1045,lag=0
slave1:ip=10.0.0.41,port=6379,state=online,offset=1045,lag=0
master_failover_state:no-failover
master_replid:8ba3aa667f0d3e54f4c6499d4d42ce0f68ccce8f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1045
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:6
repl_backlog_histlen:1040
127.0.0.1:6379>
role:slave:本实例是从节点。
master_link_status:up:当前和主节点连接正常。
master_last_io_seconds_ago:8:上一次从主节点收到数据到现在,已经过了 8 秒。
master_sync_in_progress:0:表示目前没有在做全量复制或同步过程;如果在进行全量同步,会显示 1。
slave_read_repl_offset、slave_repl_offset:该从节点当前已读取/执行到的复制偏移量。
master_replid:主节点的复制 ID。
master_replid2:备用 ID,默认是全 0,如果没有经历故障转移或者没有开启相关功能,通常不会被使用。
master_repl_offset:主节点上的全局复制偏移量。
repl_backlog_*:与复制积压缓冲区的状态相关。
connected_slaves:0:因为这是一个从节点,不会再带其他从节点,所以其下级从节点数量是 0。
7.3.2 宕机与故障恢复
- 断电 / 宕机后再次启动
- 如果 Replica 的偏移量信息仍然保留,且 Master 的 backlog 中还包含所需的增量数据,则会触发 部分重同步 ;否则进行 全量同步。
- 需要重点关注磁盘剩余空间、内存容量以及网络带宽,避免在高并发或超大 RDB 传输时给系统带来风险。
- 监控与告警
- 建议通过
INFO replication
监控以下指标:role
:节点角色(master / replica)connected_slaves
:当前连接的从节点数量repl_backlog_active
、repl_backlog_size
和 backlog 的使用情况master_link_status
:从节点与主节点的连接状态(up / down)master_sync_in_progress
:从节点是否在进行同步
- 结合其他监控系统(如 Prometheus + Grafana)将复制状态可视化,便于及时发现问题。
- 建议通过
- 手动故障转移
- 如果使用 Sentinel,可以在主节点故障时自动提升某个从节点为新主节点。
- 如果没有使用 Sentinel 或集群,需要手动执行
REPLICAOF NO ONE
将某个从节点晋升为主节点,并重新配置其他节点的复制指向。
7.3.3 性能调优
- 减小全量复制的概率
- 适度增大
repl-backlog-size
,让主节点可以保存更多增量命令,减少重连后出现 "无法部分重同步" 的情况。 - 优化网络状况,缩短复制延迟,降低 Replica 失联几率。
- 适度增大
- 合理使用磁盘复制(Diskless Replication)
- 若磁盘 IO 压力较大或者环境允许,可以打开
repl-diskless-sync yes
。 - 在大规模场景下需要评估内存峰值,避免产生过大的内存消耗导致 OOM。
- 若磁盘 IO 压力较大或者环境允许,可以打开
- 多副本策略
- 如果业务有严格的数据安全需求,可以多实例部署,从节点数量增加后,通过
min-replicas-to-write
和min-replicas-max-lag
机制做一定程度的 "半同步"(写安全)保护。 - 但要注意过多 Replica 也会加大主节点带宽压力和内存开销。
- 如果业务有严格的数据安全需求,可以多实例部署,从节点数量增加后,通过
7.4 主从复制故障恢复
如何在 Redis 中手动配置 主从复制(Master-Replica) 以及在 Master 节点故障后如何手动将 Slave(Replica) 提升为新的主节点的整个过程。
- 纯手动配置
- 基于 Sentinel 的自动/半自动故障转移。
Master 节点故障 & 手动提升 Slave
当 Master 节点 "挂了" 或出现严重故障,需要将 Slave 节点变成新的 Master,这个过程称为 故障转移 或 主从切换。
- 确定 Master 已不可用
- 通过监控、日志或运维工具确认 Master 节点宕机,或者无法在短时间内修复。
- 在 Slave 上停止复制,提升为主
- 登录到 Slave 节点:
bash
redis-cli -h 10.0.0.40 -p 6379
- 执行:
bash
REPLICAOF NO ONE
此命令会让当前节点放弃做从节点的角色,成为一个"独立"的主节点,今后写入将直接在这个节点生效。
- (可选)如果原本 Master 设置了密码,而当前节点也需要对客户端进行认证,可在
redis.conf
中设置requirepass new-password
,或者用CONFIG SET requirepass new-password
。
- 检查新的 Master 状态
- 再次执行
INFO replication
,应当看到role: master
,且connected_slaves: 0
。 - 测试写入:
bash
SET new_test_key "i_am_new_master"
GET new_test_key
如果返回正确,说明新的 Master 工作正常
- 其他节点(如果有多个)重新指向新的 Master
- 如果原来还有其他从节点,需要将它们也切换到指向新的 Master:
bash
redis-cli -h <other-slave-ip> -p 6379 REPLICAOF 10.0.0.40 6379
- 如果之前没有设置 Sentinel 或自动化脚本,需要一台台手动执行。
使用 Sentinel 自动化或半自动化故障转移
如果你觉得 纯手动 的方式在生产环境不够及时或存在人工失误的风险,Redis 提供了 Sentinel 组件,能够实现自动或半自动的故障检测与主从切换。
1. Sentinel 的工作原理简述
- Sentinel 进程会不断地监控主服务器和从服务器的健康状态。
- 当 Sentinel 认为主服务器处于宕机状态(经过若干次判断确认),它会在剩余的 Sentinel 进程间通过投票确定是否真的故障。
- 一旦确认 Master 故障,Sentinel 会将某一台 Slave(Replica)提升为新的 Master,并向其他从节点发布更新,指向新的 Master。
- 客户端可以通过 Sentinel 获取最新的 Master 信息,从而始终向正确的主节点发送写请求。
2. Sentinel 配置示例
假设有三台机器部署了三个 Sentinel 实例(一般生产环境要有奇数个 Sentinel,至少 3 个)。这里简单示范一个 Sentinel 的配置文件(sentinel.conf
)核心片段:
bash
port 26379
# 监控指定Master:<MasterName> <ip> <port> <quorum>
# quorum 是判断宕机所需要的最少 Sentinel 同意票数
sentinel monitor mymaster 192.168.1.10 6379 2
# 如果 Master 设置了密码,请使用:
sentinel auth-pass mymaster <master-password>
# 故障转移时新 Master 的复制配置
sentinel config-epoch mymaster 0
# 开启通知脚本(可选),当故障转移时执行某些通知动作
# sentinel notification-script mymaster /var/redis/scripts/notify.sh
# 故障转移后执行的脚本(可选),比如更改应用配置
# sentinel reconfig-script mymaster /var/redis/scripts/reconfig.sh
# 指定检测 Master、Slave、Sentinel 的周期、超时时间等
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
- 启动 Sentinel:
bash
redis-sentinel /path/to/sentinel.conf
-
通过
redis-cli -p 26379 SENTINEL masters
、SENTINEL slaves mymaster
可以查看当前 Sentinel 监控到的主从信息。 -
一旦主节点故障,Sentinel 会经过判定和投票,将一个 Slave 自动提升为 Master 并更新集群拓扑
提示 :如果希望在故障时人工确认后再切换,也可以结合脚本,让 Sentinel 先发出通知,运维手动执行
SENTINEL failover mymaster
命令触发故障转移,这就变成了"半自动化"方案。
在 Redis 的主从复制机制中,主要存在两种数据同步方式:全量复制(Full Resynchronization)**和**增量复制(Partial Resynchronization)。下面从原理和流程两方面进行介绍。
7.5 全量复制(Full Resynchronization)
当从节点(Slave)第一次连接到主节点(Master),或者因网络抖动、断线导致主从复制状态不一致且无法通过增量复制来追赶时,就需要执行全量复制。
1. 触发条件
- 从节点首次连接主节点:新的从节点刚刚启动或没有复制状态,需要获取主节点的全部数据。
- 断线后 backlog 不足:如果从节点断线时间较久,主节点的复制积压缓冲区(replication backlog)已经覆盖了所需的数据增量,无法完成增量复制,就会触发全量复制。
2. 流程概述
- 从节点向主节点发送
PSYNC
命令- 当从节点第一次连接主节点或者复制信息不一致时,会发送形如
PSYNC ? -1
的命令,表示需要进行全量复制。 - 如果主节点发现这是一个无法进行部分复制的请求(要么是第一次连接,要么 backlog 中无对应 offset),则返回
FULLRESYNC <runid> <offset>
,表示将进入全量复制过程。
- 当从节点第一次连接主节点或者复制信息不一致时,会发送形如
- 主节点执行快照(RDB)并发送给从节点
- 主节点接收到从节点的全量复制请求后,会
fork()
出一个子进程执行BGSAVE
,生成一份 RDB 快照文件。 - 在生成 RDB 的过程中,主节点会将新写入操作(增量数据)放入一个缓冲区(复制缓冲 buffer)中。
- RDB 文件准备好后,主节点会通过套接字把 RDB 文件发送给从节点。
- 主节点接收到从节点的全量复制请求后,会
- 从节点加载 RDB
- 从节点接收到 RDB 文件后,会先清空现有数据,再将 RDB 文件中的数据加载到内存,完成数据的"快照恢复"。
- 传输增量指令
- 当从节点完成 RDB 加载后,主节点会将全量复制期间缓冲的写操作(AOF 同步流或复制缓冲区)一并发送给从节点,从节点会依次执行这些命令,以使数据最终与主节点保持同步。
- 切换到命令持续复制
- 全量复制完成后,从节点会正式进入主从复制状态,此后主节点会将后续的写命令以"命令流"的形式持续发给从节点,从节点实时执行,实现最终一致。
7.6 增量复制(Partial Resynchronization)
增量复制也称为部分重同步,当从节点与主节点之间出现短暂断线或从节点本身保持了"复制偏移量"并且主节点保存了足够的"复制积压缓冲区"(replication backlog)时,就能通过增量复制快速"追上"主节点的写操作。
1. 基本概念
- runid:每个 Redis 实例都有一个唯一运行 ID,用于在从节点追踪主节点身份时识别主节点。
- 复制偏移量(offset):主节点给自己的每条写命令分配一个递增的 offset,从节点通过上报自身已复制的 offset 来确定需要补齐的数据量。
- 复制积压缓冲区(replication backlog):主节点中用环形缓冲区存储最近一段时间的写操作(命令流),以便断线的从节点在恢复连接后,可以从 backlog 补齐这段期间内缺失的指令,而无需执行全量复制。
2. 流程概述
- 从节点向主节点发送
PSYNC <master_runid> <offset>
- 从节点在断线重连后,会携带上一次记录的主节点 runid 和复制 offset,向主节点请求进行部分复制。
- 主节点检查复制 backlog
- 主节点对比从节点发来的 offset 与自身维护的 backlog:
- 如果 offset 在 backlog 的可用范围内(即主节点仍然保留了这部分写命令),则可以进行部分复制。
- 如果 offset 不在 backlog 范围内(丢失了更多数据或主节点 runid 发生变化),则需要转为全量复制。
- 主节点对比从节点发来的 offset 与自身维护的 backlog:
- 执行增量复制
- 如果符合条件,主节点会从 backlog 中取出从节点所缺失的写命令,依次发送给从节点。
- 从节点依次执行这些指令,完成数据追赶。
- 持续复制
- 当 backlog 的数据补齐后,从节点就和主节点重新保持一致,进入正常的命令流复制阶段。
3. 全量复制与增量复制的联系
- 全量复制是一个"重置性"的过程,通常会带来较大开销:
- 主节点需要生成 RDB 文件;
- 传输大量数据;
- 从节点需要清空内存重新加载 RDB。
- 增量复制则更轻量:
- 只要复制偏移量还在主节点的 backlog 范围内,从节点只需接收并执行缺失的指令即可,无需加载全部数据。
Redis 会尽量通过增量复制来节省资源,只有当增量复制无法满足需求时才会降级为全量复制。
4. 总结
- 全量复制
- 适用于从节点初次连接主节点、从节点断线过久导致 backlog 覆盖或主节点重启导致 runid 变化的情形。
- 流程是:从节点请求 →主节点生成 RDB 并发送 → 从节点加载 RDB →同步增量数据。
- 增量复制
- 从节点在与主节点短暂断线且 backlog 尚在有效范围时使用。
- 流程是:从节点携带 offset 请求 →主节点对比 offset 与 backlog → 如果在有效范围则只发送缺失命令流并执行。
在实际生产环境中,通过合理设置 repl-backlog-size
和其他相关配置,可以最大限度地使用增量复制,从而降低全量复制带来的开销和对集群性能的影响。
以下内容基于 Redis 经典(单机版)主从结构(非 Cluster 模式)进行讲解,涵盖了 Redis 主从同步的完整过程,包括初次连接、全量复制、增量复制、日常同步四个阶段。希望能帮助你更系统地理解 Redis 的主从同步机制。
7.7 主从同步完整过程
1. 主从复制的基本原理
Redis 主从结构中,主节点(Master) 负责处理写操作,并将数据变化(命令流)传播给 从节点(Slave)。
- 主节点:只读/读写皆可(视具体配置而定),但通常在生产环境下只对外提供写操作;
- 从节点:只读,避免写与主节点写冲突;从节点的作用通常是分担读压力或在主节点出现故障时快速接管。
Redis 的复制主要通过以下关键要素来维持同步:
- 复制偏移量(offset)
- 主节点会为自己发送的每一条数据(写命令)维护一个全局递增的 offset;
- 从节点也会维护自身已经复制到的 offset;
- 双方通过对比 offset 来确定是否有数据缺失、是否需要补齐。
- 运行 ID(runid)
- 每个 Redis 进程在启动时都会生成一个唯一标识 runid;
- 从节点会记录和识别当前所连主节点的 runid,用以区分不同的主节点。
- 复制积压缓冲区(replication backlog)
- 在主节点上维护的一个环形缓冲区,用于缓存最近一段时间的写操作(命令流);
- 当从节点短暂断线后重连,如果缺失的部分数据仍在该缓冲区内,就可以直接通过此缓冲区补齐,免去全量复制带来的开销。
2. 从节点启动时的主从同步流程
当一个新的从节点启动,或一个断线已久的从节点重新加入时,通常会经历一个 "全量复制" 的过程来获取主节点上的全部数据,再进入持续增量复制阶段。
2.1 从节点与主节点的初始握手
- 从节点配置了
slaveof <master-host> <master-port>
(或replicaof <master-host> <master-port>
)后,在启动时会尝试向主节点发起连接; - 从节点发送
PING
:在正式开始复制前,从节点通常会先与主节点进行一次简单的"心跳"请求(PING
),以检测网络和鉴定主节点是否可访问; - 主节点返回
PONG
:若主节点可访问,会回复PONG
,握手阶段成功。
2.2 触发全量复制(Full Resynchronization)
在握手完成后,从节点会向主节点发送 PSYNC ? -1
命令,意即"我没有任何已知的 offset,也不知道你的 runid,需要进行一次全量复制"。
- 如果主节点检测到从节点的请求无法进行部分(增量)复制(因为是新加入或 backlog 不可用),则会返回
FULLRESYNC <master_runid> <offset>
表示需要进行全量复制。 - 从节点收到
FULLRESYNC
后,会记录下主节点的 runid 和 offset(从节点此时的本地 offset 也将重置)。
2.3 主节点生成 RDB 并发送
- 主节点执行
BGSAVE
- 主节点
fork()
出子进程将内存快照保存为 RDB 文件; - 在生成 RDB 过程中,主节点还会将新产生的写命令存储到 复制缓冲区(或称 "复制积压缓冲区 + 额外 buffer")里,以便后续给从节点补充"快照生成阶段"遗漏的写操作;
- 主节点
- 主节点将 RDB 文件通过网络发送给从节点
- 当子进程完成 RDB 文件的生成后,主节点会把 RDB 文件直接通过 socket 流方式发送给从节点(而不一定是先存盘再读取,这在高版本 Redis 中是可配置/可优化的)。
2.4 从节点加载 RDB
- 清空本地数据
- 为确保和主节点数据完全一致,从节点会先清空内存中的旧数据;
- 加载 RDB 文件
- 从节点将接收到的 RDB 文件内容直接写到内存中,恢复成与主节点一致的数据状态。
2.5 传输并执行增量指令
- 在 RDB 传输和加载的这段时间里,主节点不断有新的写命令被执行,这些命令被存储在主节点的复制缓冲区;
- 从节点加载完 RDB 后,主节点会把复制缓冲区中的所有新写命令发送给从节点;
- 从节点按顺序执行这些写命令,让自身的数据状态追赶到主节点当前最新状态。
2.6 进入持续复制阶段
- 完成上述过程后,主从数据一致;
- 主节点针对后续所有写操作(命令流)会实时发送给从节点,从节点也会实时执行这些操作,使得主从数据保持同步。
小结 :当从节点第一次 连到主节点或断线时间过久 时,就会触发这样一次较为消耗资源的 全量复制。
3. 日常短暂断线时的增量同步流程
在大多数情况下,Redis 会尽量通过 增量复制(Partial Resynchronization) 来同步,以避免全量复制带来的大规模数据传输和主节点磁盘/CPU开销。增量复制发生在:
- 从节点与主节点的网络出现短暂断线;
- 主节点重启后 runid 不变(2.8+ 的 "主节点部分重同步" 场景,但如果重启后 runid 改变则无法增量);
- 主节点保存了足够的 backlog 数据。
3.1 触发增量复制
当从节点重新连上主节点后,会发送命令:
PSYNC <master_runid> <offset>
- 其中
<master_runid>
为从节点上一次记录的主节点 runid; <offset>
为从节点已复制到的最后一个字节/命令偏移量。
3.2 主节点判断能否增量
主节点收到上述请求后,会根据以下条件判断:
- runid 是否匹配
- 如果从节点携带的 runid 和主节点本身的 runid 相同,说明还是同一个主节点(或主节点重启但 runid 复用了);如果不匹配,就必须走全量复制。
- offset 是否在 backlog 范围内
- 主节点检查自身的 replication backlog,判断该 offset 是否仍在 backlog 的可用范围内:
- 如果 backlog 中依然保存了 offset 之后所有的命令,则可以执行"增量复制";
- 如果已经被覆盖(说明从节点缺失太多数据),只能退回到全量复制。
- 主节点检查自身的 replication backlog,判断该 offset 是否仍在 backlog 的可用范围内:
3.3 执行增量复制
- 如果判断可以增量:主节点就从 backlog 中取出 offset 之后的命令,发送给从节点;
- 从节点依次执行,更新自身数据并将 offset 往前推进;
- 当 backlog 的缺失命令全部执行完毕后,从节点与主节点再次到达一致状态,进入正常的实时复制阶段。
增量复制只需发送丢失的数据指令,不需重新加载全量数据,大大减少了网络和 CPU 等资源的开销。
4. 正常主从复制(持续同步)
在从节点完成全量或增量数据同步后,就进入了持续复制 阶段。此时,主节点对外执行的写操作会同时复制给所有从节点,从节点实时(或在毫秒级延迟内)执行这些写命令,保证主从数据最终一致。
这一阶段包含以下细节:
-
命令传播
- 主节点每收到一条写命令,在处理完后会将该命令写入自己的复制流,并将其发送给所有处于"在线"状态的从节点;
-
从节点应用命令
- 从节点收到命令后,顺序执行相应的写操作,保证数据状态与主节点同步;
-
心跳机制
- 主从之间会周期性发送
REPLCONF ACK <offset>
等命令来维持心跳,并更新双方的 offset;
- 主从之间会周期性发送
-
故障转移/选举
(如有 Sentinel 或 Redis Cluster)
- 若主节点故障或网络不可达,从节点可在某些机制(比如 Sentinel)下被提升为新的主节点,其他从节点改为复制这个新的主节点,实现自动故障转移。
5. 小结
- 初次连接或大范围数据丢失 时,从节点会进行 全量复制 :
- 主节点
BGSAVE
生成 RDB 并发送给从节点; - 从节点加载 RDB;
- 同步全量复制期间主节点的增量命令;
- 进入持续复制阶段。
- 主节点
- 短暂断线且 backlog 数据仍有效 时,可以 增量复制 :
- 从节点携带上次已知的 runid 和 offset 发起
PSYNC
请求; - 若 runid 相同且 offset 在 backlog 范围内,则只需发送缺失的命令;
- 完成缺失命令后进入持续复制阶段。
- 从节点携带上次已知的 runid 和 offset 发起
- 持续复制阶段下,主节点的所有写操作会同时广播给从节点,从节点实时执行,以保持与主节点数据一致。
在实际生产环境中,我们会通过合理设置 repl-backlog-size
等参数来增大 backlog 缓冲,尽可能提高增量复制的成功率,降低全量复制带来的压力和风险。这样能够使主从架构在高并发、网络抖动等环境下更稳定地运行。
8.Redis 级联复制
在 Redis 中,所谓"级联复制"(有时也被称作"链式复制"或"多级复制"),是指一个从节点(Replica)不仅可以从主节点(Master)复制数据,也可以从另一个从节点上再复制数据,从而形成一条主---从---从的"链式"结构。这样做的目的通常是为了 分担主节点的网络负载 或 应对跨地域/网络层级的部署需求。
一、级联复制的基本概念
-
传统主从结构
- 典型的 Redis 主从复制拓扑是 "一主多从(Star 型)",所有从节点都直接从主节点复制数据。
- 优点:拓扑简单,便于运维,主节点故障后也能较快地进行故障转移。
- 缺点:当从节点数量很多时,主节点的网络带宽和 CPU 负载压力会增大。
-
级联复制结构
-
在级联复制模式下,可以将一部分从节点配置为 "从节点的从节点",即:
Master -> Replica1 -> Replica2 -> Replica3 -> ...
-
对于 Replica2 而言,它的"上游"节点就是 Replica1,Replica2 会把 Replica1 当作"Master"来复制数据;而 Replica1 实际又是 Master 的从节点。以此类推,可以形成多级 "链式" 复制。
-
-
适用场景
- 跨地域/跨机房:有时希望把数据先从主机房(Master)复制到同城/同可用区的某个从节点,再从这个从节点复制到更远的分支机房,从而减小跨地域的网络延迟和带宽压力。
- 减少 Master 压力:当主节点带宽或 CPU 资源较有限,而需要部署大量只读副本,可考虑让某些副本从"二级副本"甚至"三级副本"拉取数据。
二、Redis 级联复制的工作原理
Redis 的主从复制是通过 PSYNC(部分重同步)机制来实现的,核心包括:
- 复制偏移量(Replication Offset)和主从 ID(Replication ID)
- 每个节点都有一个不断递增的复制偏移量
offset
,Master 在发送写操作时更新 offset,并在 backlog 中保存一段时间的操作命令历史。 - 当 Replica(从节点)向上游请求同步时,会带上自己当前的 offset 和上次复制的 Master Replid,通过这二者来判断是否能进行部分重同步(PSYNC)还是需要全量复制。
- 每个节点都有一个不断递增的复制偏移量
- 全量复制 vs. 部分重同步
- 当一个从节点首次连接到上游节点(不管该上游是 Master 还是另一个 Replica),若无法满足部分同步条件,就会触发全量复制(即发送 RDB + 后续增量命令)。
- 后续短暂断线重连时,如果上游节点的 backlog 还保留着从节点缺失的命令,就能走部分重同步,减少网络和 IO 开销。
- 多级复制的本质
- 不管"上游"节点是 Master 还是另一个 Replica,对下游节点而言都是一个"发送复制数据"的源节点。Redis 内部并没有把"从节点的从节点"当成一种特别的角色,每个 Replica 只知道自己要从"上游"拉取数据并保持同步即可。
三、Redis 级联复制的配置步骤
假设我们有如下节点:
- Master :IP 为
192.168.1.10
,端口6379
- 一级从节点(Replica1) :IP 为
192.168.1.11
,端口6379
- 二级从节点(Replica2) :IP 为
192.168.1.12
,端口6379
1. 配置 Master
在 Master 的 redis.conf
中,通常无需额外开启什么。只要确保能正常对外提供服务即可。若 Master 设有密码,则需要在从节点配置对应的 masterauth
。
2. 配置一级从节点(Replica1)
-
在其
redis.conf
或启动参数中指定:bashreplicaof 192.168.1.10 6379 # 如果 Master 设置了密码: masterauth <master-password>
-
启动后,Replica1 会从 Master 拉取全量数据。可通过
INFO replication
查看master_link_status: up
即表示同步成功。
3. 配置二级从节点(Replica2)
-
在其
redis.conf
中,让它去复制 "一级从节点(Replica1)":bashreplicaof 192.168.1.11 6379 # 如果 Replica1 也启用了 requirepass(对外访问),则需要: masterauth <replica1-password>
-
注意,这里不 是配置
replicaof 192.168.1.10 6379
,而是指向 "一级从节点"。 -
启动后,Replica2 会把 Replica1 当作"Master",向它进行全量或部分重同步。
4. 验证级联复制
-
在真正的 Master 上插入一个测试 Key:
bashredis-cli -h 192.168.1.10 -p 6379 SET test_key "hello_cascading"
-
先在一级从节点(Replica1)验证:
bashredis-cli -h 192.168.1.11 -p 6379 GET test_key # 返回 hello_cascading
-
再在二级从节点(Replica2)验证:
bashredis-cli -h 192.168.1.12 -p 6379 GET test_key # 返回 hello_cascading
-
如果都能查到相同数据,说明级联复制已生效。
四、级联复制的优缺点
1. 优点
- 分担主节点压力
- 如果从节点很多,都直接连到 Master,Master 的网络带宽和 CPU 可能吃紧。级联后,只有一级 Replica 跟 Master 同步,其他节点从该 Replica 同步,可以显著减轻 Master 的负载。
- 跨网络 / 跨机房优化
- 在多机房部署场景下,可以先在本地机房搭建一个从节点,再让其他远程机房的节点从这个本地从节点拉取数据,减少跨数据中心的网络消耗。
2. 缺点
- 增加复制延迟
- 数据从 Master 同步到 Replica1,再到 Replica2,会比直接从 Master 同步多一层延迟,且链越长,延迟越大。
- 潜在单点风险
- 如果中间级节点(例如 Replica1)出现故障,那么它下游的所有 Replica2、Replica3 等都会无法获取更新,需要及时重新配置或修复。
- 运维复杂度更高
- 需要维护一条链上的健康状况,了解每一级节点的状态;故障转移(故障切换)场景下也更复杂。
五、级联复制的故障与运维要点
- 监控每一级节点的复制状态
- 使用
INFO replication
命令查看master_link_status
是否为up
; - 监控
role
、repl_backlog_size
、master_sync_in_progress
以及复制偏移量。
- 使用
- 避免链条过长
- 虽然理论上可以多级复制,但一般不建议层数超过 2~3 级。过长的链会带来较大的复制延迟和较高的故障风险。
- 自动故障转移(Sentinel / Cluster)
- 若要实现自动或半自动的故障转移,可以结合 Redis Sentinel 或者 Redis Cluster。
- 需要注意:Redis Sentinel 在做主从切换时,默认只关注"真正的 Master"与"其直连从节点"的关系;如果使用级联复制,还需要确保 Sentinel 能正确感知到各级节点的状态,以便在故障时进行合适的切换逻辑。
- 网络与带宽规划
- 在跨机房或跨网络环境下,需要关注各个节点间的带宽和延迟,确保不会因为网络问题导致频繁的复制中断或超时。
- 资源使用与扩容
- 级联复制可以减少 Master 带宽或 CPU 压力,但并没有降低整体的资源消耗,尤其在接收数据的一方(Replica1)可能会有额外的网络 I/O 负担。
六、总结
Redis 支持"多级"或"级联"复制,即让某个从节点继续作为其他从节点的上游,从而形成一条或多条复制链。其核心机制与普通主从一致,都依赖 PSYNC 进行全量或部分重同步;区别只在于从节点的 "master" 变成了另一个 Replica。
- 如何配置 :在二级或更多级的从节点上,通过
replicaof <上游节点IP> <端口>
指向另一个 Replica 即可。 - 何时使用:最常见于大规模只读节点部署、跨机房同步、或需要减少 Master 压力的场景;但要权衡延迟和运维复杂度。
- 故障与运维:需要对每一级节点都进行监控,一旦上游节点挂了,下游节点也会同步中断,需要手动或使用 Sentinel/Cluster 自动重建复制关系。
总的来说,级联复制是 Redis 提供的一个灵活选项,但在多数常见部署中(尤其是云环境带宽和资源充足的情况下),仍以"星型"的主从架构为主。如果确实有跨网络或带宽受限场景,需要谨慎设计和规划级联的层级、监控及故障转移策略。
9.Redis 哨兵(Sentinel)
Redis Sentinel(哨兵)是 Redis 官方提供的一套高可用(High Availability)解决方案,用于监控多个 Redis 服务器实例并在主节点出现故障时进行自动故障转移(Failover)。Sentinel 的目标是保证集群的健壮性和可用性,帮助运维人员减少手动干预。
一、核心功能
- 监控(Monitoring)
Redis Sentinel 会持续监控主节点和从节点的状态是否正常。一旦检测到主节点无法正常响应,Sentinel 会标记该节点为主观下线(Subjectively Down),并在多个 Sentinel 达成一致后,进一步认定其为客观下线(Objectively Down)。 - 通知(Notification)
当 Sentinel 识别到主节点宕机或故障转移发生时,它可以通过发布订阅频道(Pub/Sub)、邮件、Webhook 等方式通知运维或相关组件,以方便及时处理。 - 自动故障转移(Automatic Failover)
当主节点被判定不可用时,Sentinel 会从从节点中挑选一个合适的节点将其升级为新的主节点,并重新配置其他从节点指向新的主节点,保证应用能够继续使用 Redis 服务。 - 配置提供(Configuration Provider)
客户端可以通过 Sentinel 获取当前可用的主节点信息,客户端不需要提前知道具体的主节点地址,而是直接向 Sentinel 请求,从而获得更动态的部署方式。
二、工作原理
Sentinel 检测到主节点出现故障后,会经历以下几个重要步骤:
- 主观下线(Subjectively Down, SDOWN)
- 每个 Sentinel 会定期向主从节点发送
PING
命令。 - 如果在配置的时间(
down-after-milliseconds
)内没有收到有效回复,则该 Sentinel 标记此节点为"主观下线"。
- 每个 Sentinel 会定期向主从节点发送
- 客观下线(Objectively Down, ODOWN)
- 当一个 Sentinel 将主节点标记为 SDOWN 后,会询问其他 Sentinel 是否也认为主节点不可用。
- 如果有足够多(达到
quorum
配置)的 Sentinel 都认为主节点已下线,则将主节点标记为"客观下线",确定主节点确实无法对外服务。
- 故障转移选举
- 在检测到主节点 ODOWN 后,Sentinel 之间会先进行一次领导者(Leader)选举:谁来执行故障转移。
- 通过 Raft-like 的投票机制(基于多轮投票、配置时间限制等),选出一个 Sentinel 成为故障转移的执行者。
- 晋升新主节点
- 领导者 Sentinel 会从现有的从节点(Slave)中挑选最合适的一个(考虑
slave-priority
、复制偏移量、延迟、最近是否断线等因素)晋升为新的主节点。 - 晋升完成后,Sentinel 会向其他从节点发送命令,将它们的复制目标指向新的主节点。
- 领导者 Sentinel 会从现有的从节点(Slave)中挑选最合适的一个(考虑
- 通知与更新
- 故障转移成功后,Sentinel 会更新自身的配置,并广播新的主节点信息。
- 其他非领导者 Sentinel 收到更新信息后,也会更新本地配置;客户端如果通过 Sentinel 获取主节点信息,也会知道新的主节点地址,从而实现自动切换。
- 老主节点重新上线
- 当故障恢复后,原主节点会以从节点(Slave)身份自动加入集群,继续对新主节点进行复制。
- 这样,集群又恢复到完整的主从结构,并保持高可用。
三、典型架构
通常在生产环境中,至少部署 3 个 Sentinel 节点(奇数个),奇数个是为了防止出现脑裂现象,分别监控同一个主从集群。这样可以提高一致性和容错能力。
- 主从节点(Master-Slave)
- 至少一个主节点,若干从节点,用于数据复制和负载均衡。
- Sentinel 集群
- 建议 3 个或 5 个 Sentinel,保证在出现网络分区或单点故障时仍能做出正确决策并完成自动故障转移。
- 客户端应用
- 客户端可以配置通过 Sentinel 获取主节点地址,而无需提前硬编码主节点信息。
四、配置要点
详细配置过程参考《Redis的一主二从三哨兵》。
在 sentinel.conf
或相应的配置文件中,需要定义监控的主节点信息,以及故障转移的一些关键参数。
-
监控主节点
bashsentinel monitor mymaster <master-ip> <master-port> <quorum>
mymaster
:自定义的监控主节点名称。<master-ip>
:主节点 IP。<master-port>
:主节点端口。<quorum>
:判断主节点客观下线所需的最少 Sentinel 数。
-
下线判定时间
bashsentinel down-after-milliseconds mymaster 5000
- 指定多少毫秒内未收到正常响应后,标记该主节点为"主观下线"。
-
故障转移超时
bashsentinel failover-timeout mymaster 60000
- 指定在执行故障转移时,若在此时间内无法完成,Sentinel 会放弃当前故障转移流程。
-
通知脚本
bashsentinel notification-script mymaster /path/to/notify.sh
- 在发生主节点故障或故障转移时,Sentinel 会调用该脚本。
-
故障转移脚本
bashsentinel reconfig-script mymaster /path/to/reconfig.sh
- 在执行故障转移后,可运行该脚本完成额外的重新配置操作(如更新负载均衡器等)。
-
手动下线redis节点
bash
[root@Rocky9.4 ~]#redis-cli -p 26379
127.0.0.1:26379> SENTINEL FAILOVER mymaster
OK
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.0.0.40:6379,slaves=2,sentinels=3
# 修改slave优先级
27.0.0.1:6379> CONFIG SET replica-priority 70
OK
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:10.0.0.41
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_read_repl_offset:3991359
slave_repl_offset:3991359
slave_priority:70
......
详细的配置参数
bash
# 三台机器全部这样配置
# 源码编译不会在/apps/redis/etc/下生成sentinel.conf这个文件,需要从源码里面拷贝一份
[root@redis_master redis]#cp /root/redis-7.4.1/sentinel.conf /apps/redis/etc/sentinel.conf
[root@redis_master redis]#vim /apps/redis/etc/sentinel.conf
port 26379
bind 0.0.0.0
daemonize yes
pidfile "/apps/redis/run/redis-sentinel.pid"
logfile "/apps/redis/log/sentinel_26379.log"
dir "/tmp" # 工作目录
sentinel monitor mymaster 10.0.0.42 6379 2 # mymaster是当前一组主从复制集群的名字,sentinel可以监控多组 2代表有两个sentinel认为宕机就可以选举了
sentinel auth-pass mymaster 123456 # mymaster集群中master的密码,这行一定在上面那行的下面
sentinel down-after-milliseconds mymaster 3000 #判断mymaster集群中所有节点的主观下线的时间,单位:毫秒,建议3000
sentinel parallel-syncs mymaster 1 #发生故障转移后,同时向新的master同步数据的slave数量,数字越小总同步时间越长,但可以减轻新master的负载压力
sentinel failover-timeout mymaster 180000 #所有slave指向新的master所需的超时时间,单位:毫秒 三分钟
sentinel deny-scripts-reconfig yes # 禁止修改脚本
五、Sentinel 内部定时任务
在 Redis Sentinel 的内部实现中,有多个定时(周期性)任务来驱动上述流程。可以简要地将其理解为以下几个重要的"循环"或"Timer":
- 定时 PING 检测
- Sentinel 会按照一定频率(默认 1 秒一次,或由配置决定)向主节点、从节点以及其他 Sentinel 发送
PING
命令。 - 若节点在
down-after-milliseconds
内没有响应,则标记为主观下线(SDOWN)。这是主观下线侦测的核心机制。
- Sentinel 会按照一定频率(默认 1 秒一次,或由配置决定)向主节点、从节点以及其他 Sentinel 发送
- 定时 INFO 收集
- Sentinel 也会周期性地向 Redis 节点发送
INFO
命令,以获取节点版本、复制偏移量、角色(主 / 从)、所监控的其他节点信息等。 - 这些信息有助于判断哪个从节点最适合被晋升为新主节点(例如优先级、复制偏移量等)。
- Sentinel 也会周期性地向 Redis 节点发送
- Sentinel 之间的 HELLO 信息交换
- 不同 Sentinel 之间会通过发布订阅(
__sentinel__:hello
频道)来交换信息。 - Sentinel 周期性地把自己监控的主从节点信息发布到频道,同时也从频道中接收其他 Sentinel 的信息,从而实现对集群拓扑的一致认知。
- 不同 Sentinel 之间会通过发布订阅(
- 选举与故障转移状态机定时检查
- 当 Sentinel 认为主节点客观下线,会启动故障转移流程。
- 这一流程也是通过定时任务逐步推进(如:发起投票->收集票数->如果超时则重试->成功后晋升从节点),直到完成或超时失败。
- 配置文件定时保存
- Sentinel 会在故障转移完成后或配置更新后,异步地把最新状态写入到本地配置文件(如
sentinel.conf
),以防止进程重启后丢失最新的拓扑配置。
- Sentinel 会在故障转移完成后或配置更新后,异步地把最新状态写入到本地配置文件(如
这些定时任务共同构成了 Sentinel 的"感知-决策-执行"的循环:感知节点状态、决策是否需要故障转移、执行切换并通知集群。
六、使用与维护建议
- 部署建议
- 为保证高可用,每个 Sentinel 都应该与 Redis 节点分布在不同的物理机或容器中,避免单点故障或网络分区对可用性的影响。
- 建议使用奇数个 Sentinel(至少 3 个),防止脑裂(split-brain)问题。
- 客户端正确使用
- 推荐客户端直接连接 Sentinel 获取主节点信息(通过
SENTINEL get-master-addr-by-name <master-name>
)。 - 部署应用时保证在主节点切换后,客户端可以尽快感知到新的主节点,尽量减少服务中断。
- 推荐客户端直接连接 Sentinel 获取主节点信息(通过
- 监控及报警
- 时刻关注 Sentinels 自身的健康状况:Sentinel 之间的网络连通性、各个 Redis 节点的延迟情况等。
- 合理设置报警机制(如日志监控、Prometheus + Grafana 监控等),在出现告警时及时排查网络和系统问题。
- 版本兼容与升级
- 不同版本的 Redis 可能在 Sentinel 功能上存在差异,部署前要查看官方文档确认兼容性。
- 升级 Redis 或 Sentinel 时,要先在测试环境演练并做好回滚预案。
七、总结
Redis Sentinel 通过持续监控、自动通知与故障转移、配置中心等功能,帮助 Redis 在主从模式下实现高可用。它能最小化人工干预,确保当主节点出现故障后,系统能自动完成从节点晋升、集群重新配置等关键步骤,从而最大程度地保证业务的连续性。在生产环境中,合理地进行 Sentinel 部署、配合主从架构及应用配置的优化,将显著提升整个 Redis 集群的可靠性和稳定性。
八、客户端连接 Sentinel 工作原理
在使用 Redis Sentinel 进行高可用部署时,客户端并不是直接去"猜"主节点(master)的地址,也不会直接同时连所有从节点(replica)来判断谁是主节点,而是通过向 Sentinel 查询当前可用的主节点地址,再去连接该主节点进行读写操作。其核心思想是"客户端将主节点发现和故障转移逻辑交给 Sentinel 去做",客户端只需要在初始化时知道一组 Sentinel 的地址,就能在主节点发生变化时自动感知并切换到新的主节点。
1. 整体流程概览
- 客户端初始化:
客户端会被配置好一个或多个 Sentinel 的地址列表(IP:PORT),以及一个代表"主节点名称"(masterName)的标识。 - 客户端向 Sentinel 查询当前主节点:
客户端向 Sentinel 发送命令(例如SENTINEL get-master-addr-by-name <masterName>
),获取到当前主节点的 IP 和端口。 - 客户端连接 Redis 主节点:
客户端根据上一步返回的 IP 和端口,建立与当前主节点的连接,用于读写操作。 - Sentinel 监控和故障转移:
- Sentinel 在后台持续监控 Redis 主从节点的健康状况。如果主节点不可用,Sentinel 选举出一个从节点晋升为新的主节点,并更新内部配置信息。
- Sentinel 会对外暴露新的主节点信息,客户端再次通过 Sentinel 查询(或在连接异常时重新查询),就能拿到新主节点的地址。
- 客户端重新连接:
如果客户端在使用过程中发现原主节点断开(网络异常、超时等),会再去询问 Sentinel 最新的主节点地址,从而与新主节点建立连接,实现故障恢复和高可用。
通过以上步骤,Redis 集群在后端完成了主从切换和故障转移,而客户端只需要确保能访问至少一个存活的 Sentinel,就可以拿到最新的主节点地址,从而无需关心后端节点的实际变化。
2. 工作原理与关键机制
- Sentinel 的监控机制:
- 每个 Sentinel 节点都会与主从节点(以及其他 Sentinel)定期进行 PING 通信,用于确认节点是否存活;
- 当 Sentinel 判断主节点下线(主观下线 + 客观下线),便会发起故障转移流程;
- 多个 Sentinel 通过 Raft 或者"分布式选举协议"类似的方式,协商并最终确定一个从节点作为新的主节点。
- 客户端与 Sentinel 的交互方式:
- 当客户端启动时,通常会使用某些高层次的客户端库或连接池(如 JedisSentinelPool、Lettuce Sentinel、redisson-spring-boot-starter 等)。这些库实现了与 Sentinel 通信的逻辑:
- 逐个尝试连接配置列表中的 Sentinel;
- 向 Sentinel 发送"获取主节点地址"的命令;
- 收到 Sentinel 返回的 (IP, PORT) 后,建立与该 IP:PORT 的 Redis 连接。
- 在正常读写过程中,客户端并不持续与 Sentinel 保持数据操作上的交互,只在连接创建或重连时才再次询问 Sentinel。
- 当客户端启动时,通常会使用某些高层次的客户端库或连接池(如 JedisSentinelPool、Lettuce Sentinel、redisson-spring-boot-starter 等)。这些库实现了与 Sentinel 通信的逻辑:
- 故障转移与主节点更新:
- 一旦主节点发生故障,Sentinel 通过投票和选举机制切换新的主节点;
- Sentinel 内部会更新自己管理的"主节点信息";
- 客户端如果和原主节点断开或检测到异常,会重新询问 Sentinel,拿到新的主节点地址并连接。
- Sentinel 的高可用:
- 生产环境中往往会部署若干个 Sentinel 进程(一般建议至少 3 个),相互之间协同监控,避免单点故障;
- 只要客户端能够连上任意存活的 Sentinel,就能得到最新主节点的信息。
- 读写分离(可选)
- 有些客户端或框架(例如 Redisson、一些自定义的分布式缓存中间件)支持根据场景选择读从节点或写主节点;
- 但 Sentinel 默认只会向客户端返回主节点地址(用于写入),从节点主要用于冗余或只读场景。
- 如果需要读写分离,通常也需要客户端自行或使用特定的代理层来管理从节点连接。
3. 总结
Redis Sentinel + 客户端的连接工作原理可以概括为:
- 客户端只关注 Sentinel,向其索取主节点地址;
- Sentinel 负责监控 Redis 主从拓扑并在故障时进行自动化的主从切换;
- 客户端在连接或发生异常时,通过 Sentinel 的信息获取新的主节点地址并自动重连。
这种模式避免了客户端自己去判断哪个 Redis 节点是主节点,也不需要在客户端配置中硬编码主节点的 IP/端口,从而极大简化了运维和故障转移的流程,提高了系统整体的高可用性。
下面的内容将为你介绍 Redis Cluster 的实现原理,并对 Redis Cluster 与 Sentinel 在架构设计和使用场景上的差异进行说明。
10.Redis Cluster
Redis Cluster 概述
Redis Cluster 是 Redis 官方提供的分布式解决方案,能够在多台 Redis 节点之间实现数据分片(Sharding)和故障自动转移(Failover),从而在高可用性和可伸缩性方面为 Redis 提供支持。与传统的主从复制模式相比,Redis Cluster 在面对业务快速增长、数据量暴增、服务高可用需求等场景时更具优势。
一、Redis Cluster 实现原理
Redis Cluster 是 Redis 提供的分布式解决方案,旨在解决单机 Redis 在数据量、并发量和高可用方面的瓶颈。它可以在多台服务器之间进行数据分片(sharding),并提供一定程度的故障转移能力。
1. 核心特性
- 数据分片(Sharding):
Redis Cluster 将整个 key 空间划分为 16384 个哈希槽(hash slot,范围为 0~16383)。每个节点可分配到若干槽,并负责存储该槽内的所有数据。客户端访问某个 key 时,通过对 key 进行 CRC16 运算并对 16384 取模来定位该 key 所在的槽位,然后直接请求对应节点。 - 高可用(Replica):
- Redis Cluster 通过为每个主节点(master)配置一个或多个从节点(replica)来进行冗余备份提高可用性。
- 当某个主节点失效时,其从节点会被自动提升为主节点(Failover)。
- 无中心节点:
Redis Cluster 没有专门的中心控制节点,集群中的每个节点既存储数据又保存集群信息,节点之间通过 Gossip 协议(消息交换协议)定期与其他节点通信以保持拓扑更新。集群中不存在单点故障的"中心"节点,一定程度上提升了系统的健壮性。 - Gossip 协议 & 心跳检测:
- 每个节点周期性向其他节点发送 PING 消息并等待回复(PONG),通过超时判定一个节点是否下线。
- 大多数主节点(master)同意某节点下线,才能标记该节点为 FAIL 状态并进行故障转移。
- 客户端重定向
- 若客户端访问了错误的槽位,集群节点会返回
MOVED
或ASK
重定向信息,告诉客户端正确的节点地址。 - 这样客户端可以自动重试,将请求发往正确的节点。
- 若客户端访问了错误的槽位,集群节点会返回
- 读写拆分
在 Redis Cluster 中,客户端的写请求会自动发送到目标主节点,而读请求则可以通过客户端自行配置来从从节点进行读取,从而减轻主节点压力,提高整体的读吞吐量。 - 线性扩展
Redis Cluster 通过简单地增加或减少节点,并对集群中的哈希槽重新分配(Rebalance)来实现扩展。与单节点或者简单主从复制方式相比,Redis Cluster 可以更有效地利用多机资源,从而提高吞吐量和存储容量。
2. Redis Cluster 的架构
一般来说,一个 Redis Cluster 至少需要 3 个主节点 来完成基本的故障自动转移。此外,为了保证高可用,每个主节点可配置一个或多个从节点。如下是一个示例拓扑结构:
+---------+ +---------+ +---------+
| Master1 | | Master2 | | Master3 |
| Slot | | Slot | | Slot |
| 0-5460 | | 5461-10922 | | 10923-16383 |
+----+----+ +----+----+ +----+----+
| | |
+v+ +v+ +v+
|R| |R| |R|
Slave1 Slave2 Slave3
- 主节点(Master):存储并管理哈希槽的数据,响应客户端的读写请求。
-
从节点(Slave):复制主节点的数据。当主节点发生故障时,从节点会自动升级为主节点。
-
Gossip 协议:集群节点之间通过 Gossip 协议交换彼此的心跳和槽位分配等信息,确保集群拓扑的一致性。
二、Redis Cluster 的数据分片机制
Redis Cluster 使用预先定义好的 16384 个哈希槽 来进行数据分片。
- 当客户端执行
SET key value
等命令时,Redis 会先对key
进行 CRC16 运算,然后再将结果对 16384 取模,得到对应的哈希槽号(槽 ID)。 - Redis 决定哪个节点负责管理这些哈希槽,进而决定数据存放在哪个节点上。
- 当需要进行集群扩容或缩容时,可通过
redis-cli
命令对哈希槽进行重新分配(Rebalance),尽量平衡各个节点的负载。
示意:
scss
CRC16(key) % 16384 --> slot_id
slot_id 由哪个节点负责
四、Redis Cluster 的高可用机制
- 主从复制(Master-Replica Replication)
当某个主节点不可用时,对应的从节点会在完成一定条件和集群投票后自动升级为主节点,实现故障转移。 - 故障检测(Failure Detection)
- PFAIL(主观下线) :若节点 A 在
cluster_node_timeout
时间内没有收到节点 B 的正确回复,节点 A 会将 B 标记为主观下线(PFAIL)。 - FAIL(客观下线):若多数节点(包括 A)都认为 B 下线,则会将 B 标记为客观下线(FAIL)。此时便会触发故障转移流程。
- PFAIL(主观下线) :若节点 A 在
- 故障转移(Failover)
当一个主节点被客观下线后,集群会发起选举,等待符合条件的从节点成为新的主节点。新的主节点接管原主节点的哈希槽,并恢复对外提供读写服务。
二、Redis Sentinel 原理
Redis Sentinel 是针对 Redis 的高可用监控与自动故障转移工具,更常用于单主多从架构。它本身并不提供数据分片能力,而是侧重监控和故障转移。
- 监控主从架构:
Sentinel 不断地 PING 主节点、从节点以及其他 Sentinel,以检测节点是否存活。 - 主观下线和客观下线:
- 当一个 Sentinel 认为主节点无法访问,就会标记其为"主观下线"。
- 如果其他 Sentinel 也同意该主观下线,则将其升级为"客观下线",启动故障转移流程。
- 故障转移流程:
- Sentinel 会通过选举机制在剩余的从节点中选出一个最合适的"晋升为主节点"的节点。
- 更新配置,并通知所有从节点改为复制新的主节点。
- 这期间,会向客户端广播新的主节点信息。
- 通知客户端
- 客户端(或者客户端的连接池)只需要连接 Sentinel,而无需手动指定主节点。
- 当主节点切换时,客户端可以通过 Sentinel 查询最新主节点地址,实现自动重连。
- Sentinel 的高可用本身
- 通常生产环境会部署多个 Sentinel 进程,互为备份且通过投票方式确认主节点是否故障。
- 只要有一个 Sentinel 存活,就可向客户端提供最新的主节点信息。
三、Redis Cluster vs Sentinel 的区别
- 核心定位/目标
- Redis Cluster:
- 主要解决 数据分片(水平扩容) 的问题,同时提供基础的高可用。
- 适用于数据量大、吞吐量高,需要进行分布式存储和集群扩展的场景。
- Sentinel:
- 主要用于 单个 Redis 主从架构的高可用;不提供数据分片功能。
- 适用于 Redis 数据规模较小或单节点足以支撑业务,且希望在主节点故障时自动完成主从切换。
- Redis Cluster:
- 数据分布方式
- Redis Cluster:按槽 (hash slot) 进行数据分片,支撑大数据量和高并发场景。
- Sentinel:无分片,通常只是一主多从(或多对主从),数据在所有从节点之间完全复制。
- 故障转移机制
- Redis Cluster:节点间通过 Gossip 协议互相通信,自动判断失效节点并进行主从切换;集群会将一部分槽转移到新的主节点上。
- Sentinel:Sentinel 进程会监控主从结构,通过投票对主节点下线作出判断,再自动提拔新的从节点为主节点。
- 客户端访问模式
- Redis Cluster:客户端需要支持 cluster 模式,访问时根据 key 自动定位到对应的节点,如果访问错误节点需要重定向。
- Sentinel:客户端只需要连接 Sentinel 并获取当下可用的主节点地址,无需关心后端的节点信息。
- 部署复杂度
- Redis Cluster:需要至少 3 个主节点(加上各自从节点)才能在节点故障时正常工作,还需客户端/应用支持;整体部署配置较为复杂,但可提供水平扩展。
- Sentinel:在单主多从的基础上额外部署若干个 Sentinel 进程即可,相对简单;适合中小规模或单机足以承载的业务场景。
- 适用场景
- Redis Cluster:
- 超大规模数据存储,需要分片扩容;
- 希望在一套 Redis 上承载高并发、高可用且数据量较大的业务。
- Sentinel:
- 数据规模较小或单机可承载,重点是主从的高可用(自动切换);
- 对运维和部署的复杂度要求较低。
- Redis Cluster:
四、总结
- Redis Cluster
- 分布式架构:提供自动分片与基本的高可用;
- 适合数据量大、需要负载均衡、水平扩容的场景;
- 部署更复杂,对客户端要求更高。
- Redis Sentinel
- 监控+高可用:在单主多从的基础上实现自动主从切换;
- 不提供数据分片,只解决高可用;
- 部署简单、对客户端改动较小,适合中小规模业务。
根据项目需求,如果您只是希望 Redis 能够在主节点故障时自动切换到从节点,那么 Sentinel 就足够满足高可用需求;但如果您需要在多台机器间分片存储 海量数据并同时兼顾高可用,则需要使用 Redis Cluster 。有时也会出现 Redis Cluster + Sentinel 混合使用的场景(但更常见的是 Redis Cluster 自己负责高可用,Sentinel 仅用于传统单体 Redis 部署)。
选择合适的方案,才能在保证高可用的同时兼顾部署成本与运维复杂度。
11.原生命令部署 Cluster
11.1 在所有节点上安装redis并启动cluster功能
bash
# 先通过脚本编译安装Redis,6台Redis
node1 10.0.0.40
node2 10.0.0.41
node3 10.0.0.42
node4 10.0.0.50
node5 10.0.0.112
node6 10.0.0.113
# 手动修改配置文件
bind 0.0.0.0
masterauth 123456 #建议配置,否则后期的master和slave主从复制无法成功,还需在配置
requirepass 123456
cluster-enabled yes #取消此行注释,必须开启集群,开启后Redis 进程会有Cluster标识
cluster-config-file nodes-6379.conf #取消此行注释,此为集群状态文件,记录主从关系即slot范围信息,由redis cluster 集群自动创建和维护
cluster-require-full-coverage no #默认值为yes,设为no可以防止一个节点不可用导致整个cluster不可用
# 修改配置文件相关设置-6台都执行
sed -i.bak -e 's/bind 127.0.0.1/bind 0.0.0.0/' -e '/masterauth/a masterauth 123456' -e '/# requirepass/a requirepass 123456' -e '/# cluster-enabled yes/a cluster-enabled yes' -e '/# cluster-config-file nodes-6379.conf/a cluster-config-file nodes-6379.conf' -e '/cluster-require-full-coverage yes/c cluster-require-full-coverage no' /apps/redis/etc/redis.conf
# 查看当前redis进程
[root@node5 ~]# ps aux | grep redis
redis 43944 0.3 0.4 134120 7952 ? Ssl 22:09 0:08 /apps/redis/bin/redis-server 0.0.0.0:6379
root 44019 0.0 0.1 6408 2156 pts/0 S+ 22:49 0:00 grep --color=auto redis
# 重启服务-6台执行
[root@node1 ~]#systemctl restart redis
# 再次查看redis进程,发现有cluster标识
[root@node1 ~]#ps aux | grep redis
redis 22009 0.1 0.8 136680 8192 ? Ssl 22:54 0:00 /apps/redis/bin/redis-server 0.0.0.0:6379 [cluster]
root 22033 0.0 0.2 6408 2176 pts/0 S+ 22:54 0:00 grep --color=auto redis
[root@node1 ~]#
11.2 执行meet操作实现相互通信
bash
redis-cli -h <已存在节点IP> -p <已存在节点端口> \
cluster meet <新节点IP> <新节点端口>
# 如果在已存在节点ip这台机器上执行, <已存在节点IP> -p <已存在节点端口> 这个可以省略不写
在 Redis 集群中,使用
CLUSTER MEET
命令是为了将新节点引入到集群中,而一旦新节点被引入,它会与集群中的所有其他节点进行通信。这种通信行为是 Redis 集群设计的结果,因为 Redis 集群本质上是 全连接 的。原因:Redis 集群的全连接机制
- Redis 集群中的节点相互感知:
- 当你通过
CLUSTER MEET
命令将一个新节点(比如10.0.0.41
)引入到集群中时,这个节点会从你执行CLUSTER MEET
的节点那里获取集群的拓扑信息。- 获取到的拓扑信息中包含了集群中所有其他节点的地址。
- 自动建立连接:
- 新加入的节点会主动与集群中其他所有节点建立通信连接(包括心跳包、槽信息同步等)。
- 同样,其他所有节点也会与新加入的节点建立通信连接,从而形成一个全连接的网络。
- 为什么其他节点之间也通信:
- 当你通过
CLUSTER MEET
命令将多个节点加入到集群中(比如10.0.0.42
和10.0.0.50
),这些节点会逐步通过集群的拓扑信息互相发现对方,并建立通信连接。- 因此,不仅本机节点与其他节点通信,所有节点之间都会建立通信。
集群通信设计的目的
Redis 集群设计为全连接网络,其目的是:
- 保证高可用性: 每个节点都能快速检测到其他节点的状态(通过心跳包机制),如果某个节点出现故障,集群可以迅速感知并触发故障转移(failover)。
- 槽分布同步: 每个节点都会知道集群中所有槽(slots)的分布,方便在请求转发或槽迁移时快速找到目标节点。
- 数据一致性: 副本节点需要定期与主节点同步数据,而这种全连接的设计让任何节点都能快速找到需要通信的目标节点。
bash
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
167cc05be9ab506a5fa687e8c9dded4dc2b633a5 :6379@16379 myself,master - 0 0 0 connected
# 执行meet
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster meet 10.0.0.41 6379
root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster meet 10.0.0.42 6379
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster meet 10.0.0.50 6379
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster meet 10.0.0.112 6379
root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster meet 10.0.0.113 6379
# 查看连接情况-与其他5台机器进行通信
[root@node1 ~]#ss -nt
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 10.0.0.40:56390 10.0.0.42:16379
ESTAB 0 0 10.0.0.40:56116 10.0.0.112:16379
ESTAB 0 0 10.0.0.40:22 10.0.0.1:4078
ESTAB 0 0 10.0.0.40:16379 10.0.0.112:55198
ESTAB 0 0 10.0.0.40:16379 10.0.0.113:59318
ESTAB 0 0 10.0.0.40:16379 10.0.0.50:42352
ESTAB 0 0 10.0.0.40:16379 10.0.0.41:46130
ESTAB 0 0 10.0.0.40:35310 10.0.0.41:16379
ESTAB 0 0 10.0.0.40:45462 10.0.0.113:16379
ESTAB 0 0 10.0.0.40:16379 10.0.0.42:38686
ESTAB 0 0 10.0.0.40:52672 10.0.0.50:16379
[root@node1 ~]#ss -nlt
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 0.0.0.0:6379 0.0.0.0:*
LISTEN 0 511 0.0.0.0:16379 #集群的工作端口 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 511 [::1]:16379 [::]:*
LISTEN 0 511 [::1]:6379 [::]:*
[root@node1 ~]#
# 查看cluster的所有节点相互通信
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster nodes
167cc05be9ab506a5fa687e8c9dded4dc2b633a5 10.0.0.40:6379@16379 myself,master - 0 0 1 connected
5b4b959cec186ee66836d176305b6db55f0ac451 10.0.0.41:6379@16379 master - 0 1736781760000 0 connected
3e9cf23bbfbcbd673d8c5b4abaf3f0e722c94b2a 10.0.0.112:6379@16379 master - 0 1736781758000 5 connected
2758daa041534a8eaef4ac279b430b0836d1e7f4 10.0.0.113:6379@16379 master - 0 1736781761000 4 connected
14468ef2bc32bcf9819b97c72c1c567916a93354 10.0.0.42:6379@16379 master - 0 1736781759894 2 connected
2d56d8d0b9ee2afce646c61577c8b622b87bafe3 10.0.0.50:6379@16379 master - 0 1736781762048 3 connected
[root@node1 ~]#
# 当前状态
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster info
cluster_state:fail
cluster_slots_assigned:0 #无槽位分配
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6 #已知的节点有6台
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_ping_sent:1239
cluster_stats_messages_pong_sent:1279
cluster_stats_messages_meet_sent:5
cluster_stats_messages_sent:2523
cluster_stats_messages_ping_received:1279
cluster_stats_messages_pong_received:1244
cluster_stats_messages_received:2523
total_cluster_links_buffer_limit_exceeded:0
[root@node1 ~]#
11.3 为各个master节点指派槽位范围
方法1:手动分配槽
bash
# 例如给新节点分配 [5461-6000] 这 540 个 slots
redis-cli -a 123456 -h <新节点IP> -p <新节点端口> cluster addslots {5461..6000}
方法2:脚本自动化分配
bash
# 创建添加槽位的脚本(为master节点分,slave不需要,会自动同步master)
[root@node1 ~]#cat addslot.sh
#!/bin/bash
host=$1
port=$2
start=$3
end=$4
pass=123456
for slot in $(seq ${start} ${end}); do
echo "Adding slot: $slot"
redis-cli -h ${host} -p ${port} -a ${pass} --no-auth-warning \
cluster addslots ${slot}
done
bash
[root@node1 ~]#./addslots.sh 10.0.0.40 6379 0 5460
[root@node1 ~]#./addslots.sh 10.0.0.41 6379 5461 10922
[root@node1 ~]#./addslots.sh 10.0.0.42 6379 10922 16383
#查看集群信息
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster info
cluster_state:ok #当前 Redis 集群的整体状态(ok:集群正常运行。fail:集群有问题(如未分配槽位,或某些节点不可用)。)
cluster_slots_assigned:16384 #分配的槽位数量,Redis 集群总共有 16384 个槽位。( 0,表示没有分配任何槽位,这会导致 cluster_state:fail 的原因之一。)
cluster_slots_ok:16384 #状态正常的槽位数量。
cluster_slots_pfail:0 #部分失败(PFAIL) 的槽位数量。某些节点的状态在短时间内没有响应心跳,但尚未被正式标记为失败。
cluster_slots_fail:0 #失败(FAIL) 的槽位数量。表明某些槽的主节点已经被标记为失败,需要手动修复。
cluster_known_nodes:6 #集群中已知的节点数量,包括主节点和从节点。
cluster_size:3 #当前集群中有几个主节点(通常指已分配槽位的主节点数量)。
cluster_current_epoch:5 #当前集群的全局元数据版本,每当集群拓扑发生变化(如添加或删除节点、重新分配槽位)时,此值会递增。
cluster_my_epoch:1 #当前节点的元数据版本。通常情况下,cluster_my_epoch 反映了该节点的初始化时的分配版本。
cluster_stats_messages_ping_sent:1894 #当前节点已发送的 PING 消息数量,用于与其他节点的心跳通信。
cluster_stats_messages_pong_sent:1953 #当前节点已发送的 PONG 消息数量,用于响应其他节点的 PING 消息。
cluster_stats_messages_meet_sent:5 #当前节点已发送的 MEET 消息数量,用于引入新节点时的握手通信。
cluster_stats_messages_sent:3852 #当前节点发送的所有类型消息的总数量。
cluster_stats_messages_ping_received:1953 #当前节点收到的 PING 消息数量。
cluster_stats_messages_pong_received:1899 #当前节点收到的 PONG 消息数量。
cluster_stats_messages_received:3852 #当前节点收到的所有类型消息的总数量。
total_cluster_links_buffer_limit_exceeded:0 #集群中连接缓冲区超出限制的总次数。如果值大于 0,表示可能有性能瓶颈,需要检查集群的网络连接或缓冲区配置。
#查看集群节点
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster nodes
167cc05be9ab506a5fa687e8c9dded4dc2b633a5 10.0.0.40:6379@16379 myself,master - 0 0 1 connected 0-5460
5b4b959cec186ee66836d176305b6db55f0ac451 10.0.0.41:6379@16379 master - 0 1736782488967 0 connected 5461-10922
3e9cf23bbfbcbd673d8c5b4abaf3f0e722c94b2a 10.0.0.112:6379@16379 master - 0 1736782485000 5 connected
2758daa041534a8eaef4ac279b430b0836d1e7f4 10.0.0.113:6379@16379 master - 0 1736782486000 4 connected
14468ef2bc32bcf9819b97c72c1c567916a93354 10.0.0.42:6379@16379 master - 0 1736782486809 2 connected 10923-16383
2d56d8d0b9ee2afce646c61577c8b622b87bafe3 10.0.0.50:6379@16379 master - 0 1736782487891 3 connected
[root@node1 ~]#
若不小心分配错了槽位,如何删除/重新分配?
bash
# 1.删除槽(前提:槽内无数据或不关心数据)
# 删除单个槽
redis-cli -h <wrong_host> -p <wrong_port> -a 123456 cluster delslots 100
# 或删除多个槽
redis-cli -h <wrong_host> -p <wrong_port> -a 123456 cluster delslots 100 101 102
#或者使用批量脚本一次性删除整个区间:
for slot in $(seq 0 5000); do
redis-cli -h <wrong_host> -p <wrong_port> -a 123456 cluster delslots ${slot}
done
#重新分配到正确节点
# 到正确节点 (correct_host, correct_port) 上添加这些槽
for slot in $(seq 0 5000); do
redis-cli -h <correct_host> -p <correct_port> -a 123456 cluster addslots ${slot}
done
如果槽内有数据,该怎么处理?
如果槽内已经存有键值数据,不能直接 delslots
,否则会造成集群不一致或报错;此时需要先将数据迁移 到目标节点,然后再执行 delslots
。简要流程如下:
bash
# 1.将槽标记为待迁移
# 在源节点上执行
redis-cli -h <src_host> -p <src_port> -a <pass> cluster setslot <slot> migrating <dest_node_id>
# 在目标节点执行:
redis-cli -h <dest_host> -p <dest_port> -a <pass> cluster setslot <slot> importing <src_node_id>
# 2.使用 migrate 命令搬移数据(在源节点上,对所有属于该槽位的 key 执行 migrate 到目标节点;也可以用 redis-cli --cluster reshard 交互式自动迁移。或者逐条 DUMP + RESTORE 的方式迁移,也可以脚本化。)
# 3.将槽标记到新节点
# 在目标节点执行:
redis-cli -h <dest_host> -p <dest_port> -a <pass> cluster setslot <slot> node <dest_node_id>
# 在源节点执行 cluster delslots <slot>(如果不再需要该槽)。
# 实际生产环境 中,使用 redis-cli --cluster reshard 或 redis-cli --cluster move 命令会自动完成 importing/migrating 等过程,并避免手动操作失误
11.4 指定各个节点的主从关系
bash
# 通过上面cluster nodes 查看master的ID信息,执行下面操作,将对应的slave 指定相应的master节点,实现三对主从节点
# master:10.0.0.40-slave:10.0.0.50(后面的ID对应的master的id,前面写的是slave的ip)
[root@node1 ~]#redis-cli -h 10.0.0.50 -a 123456 --no-auth-warning cluster replicate 167cc05be9ab506a5fa687e8c9dded4dc2b633a5
# master:10.0.0.41-slave:10.0.0.112
[root@node1 ~]#redis-cli -h 10.0.0.112 -a 123456 --no-auth-warning cluster replicate 5b4b959cec186ee66836d176305b6db55f0ac451
# master:10.0.0.42-slave:10.0.0.13
[root@node1 ~]#redis-cli -h 10.0.0.113 -a 123456 --no-auth-warning cluster replicate 14468ef2bc32bcf9819b97c72c1c567916a93354
# 查看节点间关系
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster nodes
167cc05be9ab506a5fa687e8c9dded4dc2b633a5 10.0.0.40:6379@16379 myself,master - 0 0 1 connected 0-5460
5b4b959cec186ee66836d176305b6db55f0ac451 10.0.0.41:6379@16379 master - 0 1736782929293 0 connected 5461-10922
3e9cf23bbfbcbd673d8c5b4abaf3f0e722c94b2a 10.0.0.112:6379@16379 slave 5b4b959cec186ee66836d176305b6db55f0ac451 0 1736782927129 0 connected
2758daa041534a8eaef4ac279b430b0836d1e7f4 10.0.0.113:6379@16379 slave 14468ef2bc32bcf9819b97c72c1c567916a93354 0 1736782930369 2 connected
14468ef2bc32bcf9819b97c72c1c567916a93354 10.0.0.42:6379@16379 master - 0 1736782928211 2 connected 10923-16383
2d56d8d0b9ee2afce646c61577c8b622b87bafe3 10.0.0.50:6379@16379 slave 167cc05be9ab506a5fa687e8c9dded4dc2b633a5 0 1736782927000 1 connected
[root@node1 ~]#redis-cli -a 123456 --no-auth-warning cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3 # 3组集群
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_ping_sent:2376
cluster_stats_messages_pong_sent:2450
cluster_stats_messages_meet_sent:5
cluster_stats_messages_sent:4831
cluster_stats_messages_ping_received:2450
cluster_stats_messages_pong_received:2381
cluster_stats_messages_received:4831
total_cluster_links_buffer_limit_exceeded:0
[root@node1 ~]#
11.5 客户端连接自动计算槽位
bash
# 根据key计算槽位所在哪个master
[root@node1 ~]#redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> set class m44
(error) MOVED 7755 10.0.0.41:6379
# 集群模式连接,会自动重定向写入到计算的机器
[root@node1 ~]#redis-cli -a 123456 -c
10.0.0.41:6379> get class
"m44"
10.0.0.41:6379> set title ceo
-> Redirected to slot [2217] located at 10.0.0.40:6379
OK
10.0.0.40:6379> set cto hello
-> Redirected to slot [7151] located at 10.0.0.41:6379
OK
10.0.0.41:6379>
12.基于redis7.4的redis cluster部署
12.1 在所有节点上安装redis并启动cluster功能
bash
# 先通过脚本编译安装Redis,6台Redis
node1 10.0.0.40
node2 10.0.0.41
node3 10.0.0.42
node4 10.0.0.43
node5 10.0.0.44
node6 10.0.0.45
# 手动修改配置文件
bind 0.0.0.0
masterauth 123456 #建议配置,否则后期的master和slave主从复制无法成功,还需在配置
requirepass 123456
cluster-enabled yes #取消此行注释,必须开启集群,开启后Redis 进程会有Cluster标识
cluster-config-file nodes-6379.conf #取消此行注释,此为集群状态文件,记录主从关系即slot范围信息,由redis cluster 集群自动创建和维护
cluster-require-full-coverage no #默认值为yes,设为no可以防止一个节点不可用导致整个cluster不可用
cluster-node-timeout #节点之间通信超时时间。
# 修改配置文件相关设置-6台都执行
sed -i.bak -e 's/bind 127.0.0.1/bind 0.0.0.0/' -e '/masterauth/a masterauth 123456' -e '/# requirepass/a requirepass 123456' -e '/# cluster-enabled yes/a cluster-enabled yes' -e '/# cluster-config-file nodes-6379.conf/a cluster-config-file nodes-6379.conf' -e '/cluster-require-full-coverage yes/c cluster-require-full-coverage no' /apps/redis/etc/redis.conf
12.2 创建集群
bash
# --cluster-replicas 1 意味着每个主节点会有一个从节点。
[root@node1 ~]#redis-cli -a 123456 --cluster create 10.0.0.40:6379 10.0.0.41:6379 10.0.0.42:6379 10.0.0.43:6379 10.0.0.44:6379 10.0.0.45:6379 --cluster-replicas 1
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 10.0.0.44:6379 to 10.0.0.40:6379
Adding replica 10.0.0.45:6379 to 10.0.0.41:6379
Adding replica 10.0.0.43:6379 to 10.0.0.42:6379
# M=>master S=>slave
M: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots:[0-5460] (5461 slots) master
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[5461-10922] (5462 slots) master
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[10923-16383] (5461 slots) master
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
S: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
replicates 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10
S: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
replicates c9b13ad218d3c8aaf62153738bb21d08a59a66c8
Can I set the above configuration? (type 'yes' to accept): yes #输入yes自动创建集群
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
>>> Performing Cluster Check (using node 10.0.0.40:6379)
M: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots:[0-5460] (5461 slots) master #已经分配的槽位
1 additional replica(s) #分了一个slave
S: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots: (0 slots) slave #slave没有分配槽位
replicates c9b13ad218d3c8aaf62153738bb21d08a59a66c8 #对应master的ID,也就是10.0.0.41
S: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots: (0 slots) slave
replicates 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
[root@node1 ~]#
# 观察以上结果,可以看到三组master/slave
master:10.0.0.40-->slave:10.0.0.44
master:10.0.0.41-->slave:10.0.0.45
master:10.0.0.42-->slave:10.0.0.43
# 查看主从状态
[root@node1 ~]#redis-cli -a 123456 info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=10.0.0.44,port=6379,state=online,offset=686,lag=0
master_failover_state:no-failover
master_replid:686dd579b02a3975b8c8df7e4f707dfa612519f1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:686
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:686
# 查看指定master节点的slave节点信息
[root@node3 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 master - 0 1736845843217 2 connected 5461-10922
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 slave 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 0 1736845844228 1 connected
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 master - 0 1736845842000 1 connected 0-5460
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736845842208 3 connected
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 myself,master - 0 0 3 connected 10923-16383
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 slave c9b13ad218d3c8aaf62153738bb21d08a59a66c8 0 1736845841000 2 connected
[root@node3 ~]#
# 查看指定master节点的slave节点信息
[root@node1 ~]#redis-cli -a 123456 cluster slaves 767c6fc628516c09dd63fe4dbdc36cecee3808b3
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
1) "12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736845978620 3 connected"
[root@node1 ~]#
# 验证集群的状态
[root@node1 ~]#redis-cli -a 123456 cluster info
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:763
cluster_stats_messages_pong_sent:710
cluster_stats_messages_sent:1473
cluster_stats_messages_ping_received:705
cluster_stats_messages_pong_received:763
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:1473
total_cluster_links_buffer_limit_exceeded:0
[root@node1 ~]#
# 查看任意节点的集群状态
[root@node1 ~]#redis-cli -a 123456 --cluster info 10.0.0.40:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.40:6379 (334ecb12...) -> 0 keys | 5461 slots | 1 slaves.
10.0.0.41:6379 (c9b13ad2...) -> 0 keys | 5462 slots | 1 slaves.
10.0.0.42:6379 (767c6fc6...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
[root@node1 ~]#
12.3 测试写入数据
在运行代码之前,请确保使用以下命令安装
redis-py-cluster
:pip install redis-py-cluster
bash
from rediscluster import RedisCluster
# 定义 Redis Cluster 的启动节点
startup_nodes = [
{"host": "10.0.0.40", "port": 6379},
{"host": "10.0.0.41", "port": 6379},
{"host": "10.0.0.42", "port": 6379},
{"host": "10.0.0.43", "port": 6379},
{"host": "10.0.0.44", "port": 6379},
{"host": "10.0.0.45", "port": 6379}
]
# 创建 RedisCluster 连接
redis_conn = RedisCluster(startup_nodes=startup_nodes, password='123456', decode_responses=True)
# 批量插入并获取键值对
for i in range(0, 10000):
key = 'key' + str(i)
value = 'value' + str(i)
redis_conn.set(key, value) # 设置键值
print(f'{key}: {redis_conn.get(key)}') # 输出键值对
13.cluster和--cluster
下面这两条命令虽然都跟 Redis Cluster 相关,但它们代表了两套不同的命令体系,用途和选项也不尽相同。可以简单地理解为:
redis-cli cluster help
------服务端层面的 Cluster 子命令 。这些子命令本质上是向 Redis 服务端发送CLUSTER <subcommand>
命令,由 Redis 服务端执行,帮助我们在Redis 实例内部完成各种操作(例如查看节点信息、设置 slots 等)。redis-cli --cluster help
------客户端层面的 Cluster Manager 子命令 。这是 Redis 命令行工具(redis-cli
)提供的一个集群管理功能,通过它可以在客户端对多个节点进行统一管理、检查、rebalance、reshard 等高层次操作,而不需要逐节点去执行相应命令。
bash
[root@node1 ~]#redis-cli -a 123456 --cluster help
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
Cluster Manager Commands:
create host1:port1 ... hostN:portN
--cluster-replicas <arg>
check <host:port> or <host> <port> - separated by either colon or space
--cluster-search-multiple-owners
info <host:port> or <host> <port> - separated by either colon or space
fix <host:port> or <host> <port> - separated by either colon or space
--cluster-search-multiple-owners
--cluster-fix-with-unreachable-masters
reshard <host:port> or <host> <port> - separated by either colon or space
--cluster-from <arg>
--cluster-to <arg>
--cluster-slots <arg>
--cluster-yes
--cluster-timeout <arg>
--cluster-pipeline <arg>
--cluster-replace
rebalance <host:port> or <host> <port> - separated by either colon or space
--cluster-weight <node1=w1...nodeN=wN>
--cluster-use-empty-masters
--cluster-timeout <arg>
--cluster-simulate
--cluster-pipeline <arg>
--cluster-threshold <arg>
--cluster-replace
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
del-node host:port node_id
call host:port command arg arg .. arg
--cluster-only-masters
--cluster-only-replicas
set-timeout host:port milliseconds
import host:port
--cluster-from <arg>
--cluster-from-user <arg>
--cluster-from-pass <arg>
--cluster-from-askpass
--cluster-copy
--cluster-replace
backup host:port backup_directory
help
For check, fix, reshard, del-node, set-timeout, info, rebalance, call, import, backup you can specify the host and port of any working node in the cluster.
Cluster Manager Options:
--cluster-yes Automatic yes to cluster commands prompts
[root@node1 ~]#
bash
[root@node1 ~]#redis-cli -a 123456 cluster help
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
1) CLUSTER <subcommand> [<arg> [value] [opt] ...]. Subcommands are:
2) COUNTKEYSINSLOT <slot>
3) Return the number of keys in <slot>.
4) GETKEYSINSLOT <slot> <count>
5) Return key names stored by current node in a slot.
6) INFO
7) Return information about the cluster.
8) KEYSLOT <key>
9) Return the hash slot for <key>.
10) MYID
11) Return the node id.
12) MYSHARDID
13) Return the node's shard id.
14) NODES
15) Return cluster configuration seen by node. Output format:
16) <id> <ip:port@bus-port[,hostname]> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ...
17) REPLICAS <node-id>
18) Return <node-id> replicas.
19) SLOTS
20) Return information about slots range mappings. Each range is made of:
21) start, end, master and replicas IP addresses, ports and ids
22) SHARDS
23) Return information about slot range mappings and the nodes associated with them.
24) ADDSLOTS <slot> [<slot> ...]
25) Assign slots to current node.
26) ADDSLOTSRANGE <start slot> <end slot> [<start slot> <end slot> ...]
27) Assign slots which are between <start-slot> and <end-slot> to current node.
28) BUMPEPOCH
29) Advance the cluster config epoch.
30) COUNT-FAILURE-REPORTS <node-id>
31) Return number of failure reports for <node-id>.
32) DELSLOTS <slot> [<slot> ...]
33) Delete slots information from current node.
34) DELSLOTSRANGE <start slot> <end slot> [<start slot> <end slot> ...]
35) Delete slots information which are between <start-slot> and <end-slot> from current node.
36) FAILOVER [FORCE|TAKEOVER]
37) Promote current replica node to being a master.
38) FORGET <node-id>
39) Remove a node from the cluster.
40) FLUSHSLOTS
41) Delete current node own slots information.
42) MEET <ip> <port> [<bus-port>]
43) Connect nodes into a working cluster.
44) REPLICATE <node-id>
45) Configure current node as replica to <node-id>.
46) RESET [HARD|SOFT]
47) Reset current node (default: soft).
48) SET-CONFIG-EPOCH <epoch>
49) Set config epoch of current node.
50) SETSLOT <slot> (IMPORTING <node-id>|MIGRATING <node-id>|STABLE|NODE <node-id>)
51) Set slot state.
52) SAVECONFIG
53) Force saving cluster configuration on disk.
54) LINKS
55) Return information about all network links between this node and its peers.
56) Output format is an array where each array element is a map containing attributes of a link
57) HELP
58) Print this help.
一、redis-cli cluster help
及其子命令详解
当我们执行:
bash
redis-cli -a 123456 cluster help
(-a 123456
只是指定了访问 Redis 的密码,与你要执行的 CLUSTER HELP
命令本身无关)
返回的内容是 Redis 服务端对外暴露的Cluster 子命令 列表,即 CLUSTER <subcommand>
。下面对其进行简要说明(按照它显示的顺序):
- COUNTKEYSINSLOT
<slot>
- 功能 :返回指定
<slot>
中包含的 key 的数量(仅当前节点存储的部分)。 - 用法示例 :
CLUSTER COUNTKEYSINSLOT 1234
- 场景:当你想知道某个 slot 上有多少 key,尤其在做 reshard 之前,想先确认数据量是多少时有用。
- 功能 :返回指定
- GETKEYSINSLOT
<slot>
<count>
- 功能 :返回当前节点在指定
<slot>
中存储的最多<count>
个 key 名。 - 用法示例 :
CLUSTER GETKEYSINSLOT 1234 10
- 场景 :一般配合
MIGRATE
命令进行迁移时,需先找到属于某个 slot 的具体 key。
- 功能 :返回当前节点在指定
- INFO
- 功能:返回集群的总体信息,包括集群状态(ok 或 fail)、当前集群配置纪元(epoch)等。
- 用法示例 :
CLUSTER INFO
- KEYSLOT
<key>
- 功能:计算某个 key 所对应的 hash slot 值。
- 用法示例 :
CLUSTER KEYSLOT mykey
- 场景:用于测试或验证某个 key 会被路由到哪个 slot。
- MYID
- 功能:返回当前 Redis 节点在集群中的 node id。
- 用法示例 :
CLUSTER MYID
- MYSHARDID
- 功能:返回当前节点所在的 shard 的 ID(Redis 7.0+ 出现,内部概念类似 node id,主要用于分片标识)。
- 用法示例 :
CLUSTER MYSHARDID
- NODES
- 功能:返回当前节点所看到的整个集群拓扑配置。
- 用法示例 :
CLUSTER NODES
- 返回格式:多行文本,每一行代表一个节点的信息,包括其 ID、IP、端口、flags、master/slave 关系等。
- REPLICAS
<node-id>
- 功能 :返回指定
<node-id>
的所有从节点信息(相当于老版本命令SLAVES <node-id>
)。 - 用法示例 :
CLUSTER REPLICAS 07c37dfeb2352f2f2bae
- 功能 :返回指定
- SLOTS
- 功能:以区间形式返回集群中所有 slot 的分配信息,包括每个区间有哪些节点负责。
- 用法示例 :
CLUSTER SLOTS
- SHARDS
- 功能 :Redis 7.0+ 新增命令,与
SLOTS
类似,但会返回更详细的节点信息(按 shard 分组)。 - 用法示例 :
CLUSTER SHARDS
- 功能 :Redis 7.0+ 新增命令,与
- ADDSLOTS
<slot> [<slot> ...]
- 功能 :将指定的 slot 集合分配给当前节点。
- 用法示例 :
CLUSTER ADDSLOTS 1 2 3
- ADDSLOTSRANGE
<start slot> <end slot> [<start slot> <end slot> ...]
- 功能 :一次性将连续区间的 slot 分配给当前节点。
- 用法示例 :
CLUSTER ADDSLOTSRANGE 0 1000 1001 2000
- BUMPEPOCH
- 功能:手动让当前集群的配置纪元(epoch)+1,通常用于测试或特殊情况下触发集群新的配置更新流程。
- 用法示例 :
CLUSTER BUMPEPOCH
- COUNT-FAILURE-REPORTS
<node-id>
- 功能:查看有多少节点报告了指定节点的失败(故障)。
- 用法示例 :
CLUSTER COUNT-FAILURE-REPORTS 07c37dfeb2352f2f2bae
- DELSLOTS
<slot> [<slot> ...]
- 功能:从当前节点中删除这些 slot 的归属关系(让它处于无主状态)。
- 用法示例 :
CLUSTER DELSLOTS 1 2 3
- DELSLOTSRANGE
<start slot> <end slot> [<start slot> <end slot> ...]
- 功能:批量删除指定区间的 slot 归属关系。
- 用法示例 :
CLUSTER DELSLOTSRANGE 0 1000 1001 2000
- FAILOVER
[FORCE|TAKEOVER]
- 功能:在复制结构(主从结构)中,使当前节点从从节点晋升为主节点。
- FORCE :强制故障转移,哪怕主节点在线;TAKEOVER:更高优先级,会忽略部分检查。
- 用法示例 :
CLUSTER FAILOVER FORCE
- FORGET
<node-id>
- 功能:从集群信息中"遗忘"某个节点,让该节点从集群拓扑中移除。
- 用法示例 :
CLUSTER FORGET 07c37dfeb2352f2f2bae
- FLUSHSLOTS
- 功能:清空当前节点对所有 slot 的归属信息,但并不会对集群其他节点的视图造成影响。
- 用法示例 :
CLUSTER FLUSHSLOTS
- MEET
<ip> <port> [<bus-port>]
- 功能:让当前节点尝试与指定 IP 和端口的节点建立集群握手,使其加入集群。
- 用法示例 :
CLUSTER MEET 192.168.1.100 7001
- REPLICATE
<node-id>
- 功能:将当前节点设置为指定节点的从节点。
- 用法示例 :
CLUSTER REPLICATE 07c37dfeb2352f2f2bae
- RESET
[HARD|SOFT]
- 功能:重置当前节点的集群状态。
- SOFT:保留部分集群信息,只删除 slots 等。
- HARD:彻底重置,清除所有集群相关信息。
- 用法示例 :
CLUSTER RESET HARD
- SET-CONFIG-EPOCH
<epoch>
- 功能:手动设置当前节点的配置纪元值。
- 用法示例 :
CLUSTER SET-CONFIG-EPOCH 99
- SETSLOT
<slot>
(IMPORTING <node-id> | MIGRATING <node-id> | STABLE | NODE <node-id>)
- 功能:设置指定 slot 的状态,例如标记为迁入中、迁出中,或指定新的归属节点等。
- 用法示例 :
CLUSTER SETSLOT 1234 MIGRATING 07c37dfeb2352f2f2bae
- SAVECONFIG
- 功能 :强制保存集群配置到本地磁盘(通常是
nodes.conf
文件)。 - 用法示例 :
CLUSTER SAVECONFIG
- 功能 :强制保存集群配置到本地磁盘(通常是
- LINKS
- 功能:查看当前节点与其他节点之间的网络连接详细信息。
- 用法示例 :
CLUSTER LINKS
- HELP
- 功能 :打印出所有可用的
CLUSTER <subcommand>
命令说明。
- 功能 :打印出所有可用的
小结:
cluster help
列出的这些命令都属于服务端内置的子命令 ,它们是针对当前连接的这个 Redis 节点,来查看或操作集群信息的。比如ADDSLOTS
/DELSLOTS
就是告诉"当前节点"去声明 或释放某些 slots 的归属。
二、redis-cli --cluster help
及其子命令详解
当我们执行:
bash
redis-cli -a 123456 --cluster help
同样会出现一系列"Cluster Manager"命令。这些命令并不是简单的 CLUSTER <subcommand>
,而是Redis CLI 工具对集群管理封装的一套功能。它会在客户端跑一些逻辑,然后对多个节点发送相应的 Redis 命令,从而帮我们完成创建集群、检查或修复集群等高级操作。
常见的子命令如下:
-
create
host1:port1 ... hostN:portN
-
功能:在给定的若干 Redis 实例之间创建一个新的集群,并进行 slot 分配。
-
常见选项:
--cluster-replicas <arg>
:指定每个主节点分配多少从节点。
-
用法示例:
bashredis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 \ --cluster-replicas 1
-
-
check
<host:port>
- 功能:检查指定节点所在的集群是否存在配置或数据异常,例如 slot 冲突、主从同步异常等。
- 可选 :
--cluster-search-multiple-owners
:当存在多个节点同时持有相同 slot 时,尝试自动检测并显示。
-
info
<host:port>
- 功能:显示指定节点所在集群的整体信息,包括主从节点分布、slot 分配、可能的问题等。
- 用法示例 :
redis-cli --cluster info 127.0.0.1:7001
-
fix
<host:port>
- 功能:针对检查出的问题尝试进行修复,比如修正 slot 的 owner、清理不正确的配置等。
- 常见选项:
--cluster-search-multiple-owners
:检测并尝试修复多个节点同时拥有同一 slot 的冲突问题。--cluster-fix-with-unreachable-masters
:即使主节点无法访问,也要尝试进行修复。
- 用法示例 :
redis-cli --cluster fix 127.0.0.1:7001 --cluster-search-multiple-owners
-
reshard
<host:port>
-
功能:对集群中的 slot 进行重新分配(手动或交互式),实现数据在各节点之间重新均衡。
-
常见选项:
--cluster-from <node id>
:指定从哪个节点或节点集合中抽取 slot。--cluster-to <node id>
:指定 slot 将迁移到哪个节点。--cluster-slots <个数>
:要迁移多少个 slot。--cluster-yes
:自动确认(不再提示交互)。--cluster-timeout <arg>
:迁移超时设置。--cluster-pipeline <arg>
:迁移时使用 pipeline 发送多少个 key。--cluster-replace
:若目标节点已拥有该 slot 的信息,也强行替换。
-
用法示例:
bashredis-cli --cluster reshard 127.0.0.1:7001 \ --cluster-from 07c37dfeb2352f2f2bae \ --cluster-to 09a49dfeb2352f2f2cde \ --cluster-slots 100 \ --cluster-yes
-
-
rebalance
<host:port>
- 功能:在集群中执行自动负载均衡,让各节点的 slot 数量大体趋于平均。
- 常见选项:
--cluster-weight <node1=w1...nodeN=wN>
:给节点设置"权重",使 rebalance 时并非绝对平均,而是按权重分配 slot。--cluster-use-empty-masters
:在 rebalance 时也会考虑那些目前没有 slots 的主节点。--cluster-timeout <arg>
:操作超时。--cluster-simulate
:只模拟操作,并不真正执行。--cluster-pipeline <arg>
:迁移 key 时的 pipeline 大小。--cluster-threshold <arg>
:只有当节点 slot 数差异超过一定阈值才进行迁移。--cluster-replace
:迁移中如遇 slot 冲突允许替换。
- 用法示例 :
redis-cli --cluster rebalance 127.0.0.1:7001 --cluster-use-empty-masters --cluster-yes
-
add-node
new_host:new_port existing_host:existing_port
-
功能:向已经在运行的集群中新增加一个节点(可以指定是作为从节点,或者让它自己成为主节点)。
-
常见选项:
--cluster-slave
:以从节点的角色加入集群。--cluster-master-id <arg>
:如果要作为从节点,指明它的主节点 ID。
-
用法示例:
bashredis-cli --cluster add-node 192.168.1.100:7004 192.168.1.100:7001 \ --cluster-slave \ --cluster-master-id 07c37dfeb2352f2f2bae
-
-
del-node
host:port node_id
- 功能:从集群拓扑中删除指定 node_id 的节点。
- 用法示例 :
redis-cli --cluster del-node 127.0.0.1:7001 07c37dfeb2352f2f2bae
-
call
host:port command arg arg .. arg
- 功能:对集群的所有节点(或只对主节点/从节点)执行一个自定义命令。
- 常见选项:
--cluster-only-masters
:只对主节点执行。--cluster-only-replicas
:只对从节点执行。
- 用法示例 :
redis-cli --cluster call 127.0.0.1:7001 CONFIG GET maxmemory --cluster-only-masters
-
set-timeout
host:port milliseconds
- 功能 :设置集群节点的
cluster-node-timeout
参数。 - 用法示例 :
redis-cli --cluster set-timeout 127.0.0.1:7001 2000
- 功能 :设置集群节点的
-
import
host:port
-
功能:将单节点 Redis 的数据导入到指定的集群节点中。
-
常见选项:
--cluster-from <arg>
:源 Redis 的地址。--cluster-from-user <arg>
/--cluster-from-pass <arg>
/--cluster-from-askpass
:指定源 Redis 的认证方式。--cluster-copy
:复制数据而不是迁移数据。--cluster-replace
:如 slot 中已有 key 是否直接覆盖。
-
用法示例:
bashredis-cli --cluster import 127.0.0.1:7001 \ --cluster-from 127.0.0.1:6379 \ --cluster-from-pass 123456 \ --cluster-copy \ --cluster-replace
-
-
backup
host:port backup_directory
- 功能:从集群中每个节点获取 RDB 快照并保存到本地指定目录,做集群级的统一备份。
- 用法示例 :
redis-cli --cluster backup 127.0.0.1:7001 /data/redis_backups/
-
help
- 功能:打印出以上 Cluster Manager 命令的帮助信息。
小结:
--cluster
模式下的命令可以看作是一个"批处理管理工具",它会自动遍历或连接集群中多个节点执行相应操作(创建、检查、修复、分片迁移等),简化了人工逐个节点执行命令的麻烦。
三、两者的主要区别与使用场景
- 作用层次不同
cluster help
:侧重于服务端 提供的低层次操作接口,直接通过CLUSTER <subcommand>
与 Redis 节点交互。--cluster help
:侧重于客户端的集群管理脚本功能,相当于在本地批量调用若干 Redis 命令来完成更复杂的集群管理操作。
- 使用场景不同
cluster help
:常用于日常运维或在线查看集群状态、slot 信息,或在做某些手动精细化操作(比如精确地给节点添加 slot、指定迁移 slot 状态)时使用。--cluster help
:常用于一键式操作,比如:- 创建集群(
create
) - 自动均衡(
rebalance
) - 重分片(
reshard
) - 检查/修复(
check
/fix
) - 备份(
backup
)
- 创建集群(
- 选项涵盖面
cluster help
:Redis 服务端自带,子命令相对固定,围绕 slot 与节点元信息进行管理。--cluster help
:Redis CLI 扩展,提供了更多"综合管理"选项(如一次性在多个节点执行命令、自动处理交互、自动计算 slot 分配等)。
总结
redis-cli cluster help
展示的是 Redis 服务端自身支持的CLUSTER
子命令,适合对集群进行细粒度、内置命令级的操作或查询。redis-cli --cluster help
则是 Redis CLI 工具提供的高级管理命令集合,让我们在客户端就能对整个集群进行一站式的部署、检查、修复、迁移、备份等操作。
在实际运维中,两者经常是结合使用的 :当需要快速创建或重分片时,用 --cluster
;当需要深入到某个具体节点查看或调整 slot 时,就使用 CLUSTER <subcommand>
。两种方式熟练掌握,可以大大提升对 Redis Cluster 的管理效率和灵活度。
14.Redis cluster集群动态扩容
因公司业务发展迅猛,现有的三主三从的redis cluster架构可能无法满足现有业务的并发写入需求,因此公司紧急采购两台服务器10.0.0.46,10.0.0.47,需要将其动态添加到集群当中,但不能影响业务使用和数据丢失。
注意:生产环境一般建议master节点为奇数个,防止脑裂现象。
14.1 添加新的master节点到集群
bash
# 首先安装redis,再执行下面命令
[root@Rocky9 ~]# sed -i.bak -e 's/bind 127.0.0.1/bind 0.0.0.0/' -e '/masterauth/a masterauth 123456' -e '/# requirepass/a requirepass 123456' -e '/# cluster-enabled yes/a cluster-enabled yes' -e '/# cluster-config-file nodes-6379.conf/a cluster-config-file nodes-6379.conf' -e '/cluster-require-full-coverage yes/c cluster-require-full-coverage no' /apps/redis/etc/redis.conf
[root@Rocky9 ~]# systemctl restart redis
[root@Rocky9 ~]# ps aux | grep redis
redis 19937 0.3 0.8 136680 8192 ? Ssl 16:57 0:00 /apps/redis/bin/redis-server 0.0.0.0:6379 [cluster]
root 19947 0.0 0.2 6408 2048 pts/0 S+ 16:57 0:00 grep --color=auto redis
# 46加入到40所在的集群节点
[root@Rocky9 ~]# redis-cli -a 123456 --cluster add-node 10.0.0.46:6379 10.0.0.40:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 10.0.0.46:6379 to cluster 10.0.0.40:6379
>>> Performing Cluster Check (using node 10.0.0.40:6379)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots: (0 slots) slave
replicates c9b13ad218d3c8aaf62153738bb21d08a59a66c8
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Getting functions from cluster
>>> Send FUNCTION LIST to 10.0.0.46:6379 to verify there is no functions in it
>>> Send FUNCTION RESTORE to 10.0.0.46:6379
>>> Send CLUSTER MEET to node 10.0.0.46:6379 to make it join the cluster.
[OK] New node added correctly.
# 查看集群节点,46加入了集群节点,并成为master
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1736953737000 3 connected 10923-16383
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1736953737211 7 connected 0-5460
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736953736201 3 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 master - 0 1736953736000 2 connected 5461-10922
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 master - 0 1736953738220 0 connected
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 slave c9b13ad218d3c8aaf62153738bb21d08a59a66c8 0 1736953736000 2 connected
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 7 connected
14.2 在新的master上重新分配槽位
新的node节点加入到集群之后,默认是master节点,但是没有slots,需要重新分配。添加主机之后需要对添加至集群中的新主机重新分片,否则其他没有分片也就无法写入数据。
注意:重新分配槽位需要清空数据,所以需要先备份数据,扩容后再恢复数据。
bash
# 方法1:手动分配槽(Slots),40是当前任意节点即可(40-45)
[root@Rocky9 ~]# redis-cli -a 123456 --cluster reshard 10.0.0.40:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing Cluster Check (using node 10.0.0.40:6379)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[0-1364],[12287-16383] (5462 slots) master
1 additional replica(s)
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[1365-6825] (5461 slots) master
1 additional replica(s)
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[6826-12286] (5461 slots) master
1 additional replica(s)
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379 #新加入的master节点已经列出来了
slots: (0 slots) master
S: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots: (0 slots) slave
replicates c9b13ad218d3c8aaf62153738bb21d08a59a66c8
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 要迁移多少个槽
How many slots do you want to move (from 1 to 16384)? 4096 #新分配多少个槽位=16384/master个数
# 槽的目标节点
What is the receiving node ID? c882647e71c8f3cc1b40ea20cc242fecca371cd4 #输入接收的master的ID(这里就是46这台机器的ID)
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: all #将哪些源主机的槽位分配给新节点,all是自动在所有的redis node选择划分,如果是从redis cluster删除某个主机可以使用此方式将指定主机上的槽位全部移动到别的redis主机上
Ready to move 4096 slots.
Source nodes: #槽的来源节点,从下面三个master上移动槽位给46
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[0-1364],[12287-16383] (5462 slots) master
1 additional replica(s)
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[1365-6825] (5461 slots) master
1 additional replica(s)
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[6826-12286] (5461 slots) master
1 additional replica(s)
Destination node: #槽的目标节点
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots: (0 slots) master
Resharding plan:
Moving slot 0 from 767c6fc628516c09dd63fe4dbdc36cecee3808b3
Moving slot 1 from 767c6fc628516c09dd63fe4dbdc36cecee3808b3
...... #这里省略类似信息
Moving slot 8190 from c9b13ad218d3c8aaf62153738bb21d08a59a66c8
Do you want to proceed with the proposed reshard plan (yes/no)? yes #确认分配
......
Moving slot 8189 from 10.0.0.41:6379 to 10.0.0.46:6379: .
Moving slot 8190 from 10.0.0.41:6379 to 10.0.0.46:6379: .
# 方法2:通过rebalance进行自动分配,不需要计算要分配的槽位(下面会重点讲解这两个命令的区别reshard和rebalance)
[root@Rocky9 ~]# redis-cli -a 123456 --cluster rebalance 10.0.0.40:6379 --cluster-use-empty-masters
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing Cluster Check (using node 10.0.0.40:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 4 nodes. Total weight = 4.00
Moving 1366 slots from 10.0.0.41:6379 to 10.0.0.46:6379 #从41上移动了1366个slots
###################################################################################################################################################################
......
Moving 1365 slots from 10.0.0.44:6379 to 10.0.0.46:6379 # 从44上移动了1365个slots
###################################################################################################################################################################
......
Moving 1365 slots from 10.0.0.42:6379 to 10.0.0.46:6379 #从42上移动了1365个slots
###################################################################################################################################################################
......
[root@Rocky9 ~]#
# 查看集群节点分配情况
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1736992172987 12 connected 12288-16383
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1736992170964 10 connected 2730-6825
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736992171000 12 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 master - 0 1736992169957 11 connected 8191-12286
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 master - 0 1736992172000 13 connected 0-2729 6826-8190 12287 #槽位已经分配好了
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 slave c9b13ad218d3c8aaf62153738bb21d08a59a66c8 0 1736992168000 11 connected
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 10 connected
[root@node1 ~]#
# 只查看槽位分配情况
[root@node1 ~]#redis-cli -a 123456 --cluster check 10.0.0.46:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.46:6379 (c882647e...) -> 2506 keys | 4096 slots | 0 slaves. #4096个slots
10.0.0.44:6379 (5023eb92...) -> 2494 keys | 4096 slots | 1 slaves.
10.0.0.42:6379 (767c6fc6...) -> 2500 keys | 4096 slots | 1 slaves.
10.0.0.41:6379 (c9b13ad2...) -> 2500 keys | 4096 slots | 1 slaves.
[OK] 10000 keys in 4 masters.
0.61 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.46:6379)
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots:[0-2729],[6826-8190],[12287] (4096 slots) master
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[2730-6825] (4096 slots) master
1 additional replica(s)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[12288-16383] (4096 slots) master
1 additional replica(s)
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[8191-12286] (4096 slots) master
1 additional replica(s)
S: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots: (0 slots) slave
replicates c9b13ad218d3c8aaf62153738bb21d08a59a66c8
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
[root@node1 ~]#
reshard和rebalance区别:
在 Redis Cluster 中,reshard 和 rebalance 都可以实现对槽(slots)的重新分配,但二者在使用场景和操作方式上有所区别:
- reshard
- 命令 :
redis-cli --cluster reshard <host>:<port>
- 定位:手动指定要从哪些节点(源节点)移动多少个槽到目标节点。
- 典型场景:
- 当你想精细地控制某些节点的槽分配时,比如把部分槽从节点 A 精准地移动到节点 B,而其他节点不受影响。
- 有些场景想只给某个新节点分配指定范围或数量的槽,而无需动到整个集群的槽分布。
- 操作方式:在交互式命令行中会让你选择:
- 移动多少个槽;
- 从哪些节点搬出槽;
- 把这些槽搬到哪个节点。
然后 Redis CLI 会一步步执行迁移并进行数据复制。
- 命令 :
- rebalance
- 命令 :
redis-cli --cluster rebalance <host>:<port>
- 定位 :自动在所有主节点之间重新平衡槽的分配,使槽尽量平均地分布在各个节点。
- 典型场景:
- 当新加入一个主节点,希望自动把集群里的槽分担一部分给它,让槽(以及数据)在所有主节点之间尽量平均。
- 或者有些主节点负载过重,自动让脚本来做"平均化"处理,而无需手动挑选要移动多少槽、从哪个节点移动到哪个节点。
- 操作方式:一条命令,Redis 会扫描所有主节点的槽占用情况,尝试让每个节点大致拥有同样数量的槽,并执行槽迁移和数据复制。
- 命令 :
二者的主要区别
- 操作的灵活度
- reshard :更加"手动",可以精细化指定源节点和目标节点,以及槽的数量或范围。适合需要对槽迁移进行自定义控制的场景。
- rebalance :更加"自动",Redis 自己计算出如何把槽分配得更均匀。适合大多数快速均衡场景。
- 使用难易程度
- reshard:需要你逐步确认哪些节点要移动多少槽,操作繁琐但灵活度高。
- rebalance:一键式自动均衡,简单快捷,但不能过多自定义移动细节。
- 应用场景
- reshard:
- 精确控制某些节点的负载;
- 只想调整部分节点,而不希望影响到其他节点。
- rebalance:
- 新加主节点后,让整个集群自动均衡槽分布;
- 集群某些主节点负载严重不均衡,希望一键平衡。
- reshard:
总结:
- reshard :更适合精细化 、指定范围 或单一节点的槽转移场景。
- rebalance :更适合全局 、自动化的槽再平衡场景。
在实际生产中,如果只是单纯要让新节点 上线并分担部分流量,通常用
rebalance
就够了;如果需要对槽分配高度自定义 ,则可使用reshard
。
14.3 为新的master添加新的slave节点
需要再向当前Redis集群中添加一个Redis单机服务器10.0.0.47,用于解决当前10.0.0.46单机的潜在宕机问题,即实现响应的高可用问题,有两种方式:
方法1:在新节点添加到集群时,直接将之设置为slave
bash
# 直接加为slave节点<10.0.0.47>是加入的slave节点,<10.0.0.46>是加入到的集群任意一个节点
[root@node1 ~]#redis-cli -a 123456 --cluster add-node 10.0.0.47:6379 10.0.0.46:6379 --cluster-slave --cluster-master-id c882647e71c8f3cc1b40ea20cc242fecca371cd4
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 10.0.0.47:6379 to cluster 10.0.0.46:6379
>>> Performing Cluster Check (using node 10.0.0.46:6379)
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[1365-5460] (4096 slots) master
1 additional replica(s)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[12288-16383] (4096 slots) master
1 additional replica(s)
S: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots: (0 slots) slave
replicates c9b13ad218d3c8aaf62153738bb21d08a59a66c8
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[6827-10922] (4096 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 10.0.0.47:6379 to make it join the cluster.
Waiting for the cluster to join
>>> Configure node as replica of 10.0.0.46:6379.
[OK] New node added correctly.
# 验证是否成功
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1736954647000 3 connected 12288-16383
8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379@16379 slave c882647e71c8f3cc1b40ea20cc242fecca371cd4 0 1736954647000 8 connected #47成为了46的slave
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1736954646000 7 connected 1365-5460
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736954648000 3 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 master - 0 1736954648253 2 connected 6827-10922
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 master - 0 1736954649263 8 connected 0-1364 5461-6826 10923-12287
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 slave c9b13ad218d3c8aaf62153738bb21d08a59a66c8 0 1736954646000 2 connected
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 7 connected
meet和add-node的区别:
bash
redis-cli --cluster add-node <new_node_host:port> <existing_node_host:port>
它其实会在内部帮你完成原生的 CLUSTER MEET <ip> <port>
操作,并且还会做一些额外的检查和提示(比如版本兼容、集群状态等)。也就是说:
redis-cli --cluster add-node ...
是一个更"高级"的封装,一条命令即可把新节点加入集群。CLUSTER MEET <ip> <port>
是 Redis Cluster 原生底层命令,属于纯手动操作方式。
二者本质上都能让新节点与现有集群"握手",最终效果相同。只不过,--cluster add-node
更适合对新手或追求自动化的用户,而手动 CLUSTER MEET
方式更加原始、灵活。一般来说:
- 使用
--cluster add-node
- 需要的命令更少,更自动化。
- 如果后面还需要自动分配槽(slots),还可以直接配合
--cluster rebalance
等子命令。
- 使用
CLUSTER MEET
- 需要对 Redis Cluster 有一定了解。
- 可以配合
cluster replicate <node_id>
或cluster addslots
等手工方式一步步细粒度地操作。
因此,你在命令行中看到的是:
bash
redis-cli -a 123456 --cluster add-node 10.0.0.46:6379 10.0.0.40:6379
而不是手动执行:
bash
redis-cli -h 10.0.0.40 -p 6379 -a 123456 cluster meet 10.0.0.46 6379
方法2:先将新节点加入集群,在修改为slave
为新的master添加slave节点
bash
# 把10.0.0.47:6379添加到集群中
[root@Rocky9 ~]# redis-cli -a 123456 --cluster add-node 10.0.0.47:6379 10.0.0.46:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 10.0.0.47:6379 to cluster 10.0.0.46:6379
>>> Performing Cluster Check (using node 10.0.0.46:6379)
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots:[0-2729],[6826-8190],[12287] (4096 slots) master
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[2730-6825] (4096 slots) master
1 additional replica(s)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[12288-16383] (4096 slots) master
1 additional replica(s)
M: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots:[8191-12286] (4096 slots) master
1 additional replica(s)
S: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots: (0 slots) slave
replicates c9b13ad218d3c8aaf62153738bb21d08a59a66c8
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Getting functions from cluster
>>> Send FUNCTION LIST to 10.0.0.47:6379 to verify there is no functions in it
>>> Send FUNCTION RESTORE to 10.0.0.47:6379
>>> Send CLUSTER MEET to node 10.0.0.47:6379 to make it join the cluster.
[OK] New node added correctly.
[root@Rocky9 ~]#
# 查看当前集群节点
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1736998066832 12 connected 12288-16383
8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379@16379 master - 0 1736998065000 0 connected #47已经加入了,但是角色是master
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1736998064814 10 connected 2730-6825
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736998066000 12 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 master - 0 1736998067843 11 connected 8191-12286
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 master - 0 1736998065000 13 connected 0-2729 6826-8190 12287
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 slave c9b13ad218d3c8aaf62153738bb21d08a59a66c8 0 1736998066000 11 connected
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 10 connected
[root@node1 ~]#
更改新节点状态为slave:
需要手动将其指定为某个master的slave,否则其默认角色为master。
bash
redis-cli -h <新节点IP> -p <新节点端口> \
cluster replicate <目标主节点ID>
bash
[root@Rocky9 ~]# redis-cli -a 123456 -h 10.0.0.47 -p 6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.47:6379> CLUSTER NODES
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 slave c9b13ad218d3c8aaf62153738bb21d08a59a66c8 0 1736998213000 11 connected
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 master - 0 1736998213000 13 connected 0-2729 6826-8190 12287
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736998214000 12 connected
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 slave 5023eb920093b0da73426572ef189d98f337023e 0 1736998216178 10 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 master - 0 1736998215167 11 connected 8191-12286
8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379@16379 myself,master - 0 0 0 connected # master
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1736998214000 12 connected 12288-16383
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1736998214156 10 connected 2730-6825
10.0.0.47:6379> CLUSTER REPLICATE c882647e71c8f3cc1b40ea20cc242fecca371cd4 #将其设为slave,命令格式 cluster replicate MASTERID
OK
10.0.0.47:6379> CLUSTER NODES
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 slave c9b13ad218d3c8aaf62153738bb21d08a59a66c8 0 1736998293993 11 connected
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 master - 0 1736998291000 13 connected 0-2729 6826-8190 12287
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1736998294000 12 connected
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 slave 5023eb920093b0da73426572ef189d98f337023e 0 1736998291000 10 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 master - 0 1736998295006 11 connected 8191-12286
8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379@16379 myself,slave c882647e71c8f3cc1b40ea20cc242fecca371cd4 0 0 13 connected # 是46的slave
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1736998293000 12 connected 12288-16383
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1736998292982 10 connected 2730-6825
10.0.0.47:6379>
15.Redis cluster集群动态缩容
实战案例:
由于10.0.0.46服务器使用年限已经超过三年,已经超过厂商质保期而且硬盘出现异常报警,经运维部架构师提交方案并同开发同事开会商议,决定将现有Redis集群的8台主服务器中的master 10.0.0.46和对应的slave 10.0.0.47 临时下线,三台服务器的并发写入性能足够支出未来1-2年的业务需求。
删除节点过程:
添加节点的时候是先添加node节点到集群,然后分配槽位,删除节点的操作与添加节点的操作正好相反,是先将被删除的Redis node上的槽位迁移到集群中的其他Redis node节点上,然后再将其删除,如果一个Redis node节点上的槽位没有完全被迁移,删除该node的时候会提示有数据且无法删除。
在 Redis Cluster 中,如果打算缩容(移除一个主节点),需要先把该节点上所有槽 (slots) 和数据迁移到其它主节点,然后再执行删除操作。
15.1 迁移master的槽位至其他master节点
方法1:手动迁移
bash
# 查看集群节点
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1737090417358 12 connected 12288-16383
8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379@16379 slave c882647e71c8f3cc1b40ea20cc242fecca371cd4 0 1737090416000 13 connected #要删除的slave节点
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1737090415000 10 connected 2730-6825
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1737090416346 12 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 slave 637a013fee8c69a87d4ca9d503f01d3817680f4c 0 1737090418370 14 connected
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 master - 0 1737090414000 13 connected 0-2729 6826-8190 12287 #要删除的master节点
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 master - 0 1737090416000 14 connected 8191-12286
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 10 connected
# 查看当前状态
[root@Rocky9 ~]# redis-cli -a 123456 --cluster check 10.0.0.46:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.46:6379 (c882647e...) -> 2506 keys | 4096 slots | 1 slaves.
10.0.0.44:6379 (5023eb92...) -> 2494 keys | 4096 slots | 1 slaves.
10.0.0.42:6379 (767c6fc6...) -> 2500 keys | 4096 slots | 1 slaves.
10.0.0.45:6379 (637a013f...) -> 2500 keys | 4096 slots | 1 slaves.
[OK] 10000 keys in 4 masters.
0.61 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.46:6379)
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots:[0-2729],[6826-8190],[12287] (4096 slots) master # 46master节点slot的范围
1 additional replica(s)
S: 8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379
slots: (0 slots) slave
replicates c882647e71c8f3cc1b40ea20cc242fecca371cd4
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[2730-6825] (4096 slots) master
1 additional replica(s)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[12288-16383] (4096 slots) master
1 additional replica(s)
S: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots: (0 slots) slave
replicates 637a013fee8c69a87d4ca9d503f01d3817680f4c
M: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots:[8191-12286] (4096 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 连接到任意集群节点,4096/3=1365(约等于)平均每个节点应该分配1365个槽位
# 1.将1365个slot从10.0.0.46移动到10.0.0.44上(这样移动的原因是44的槽位是从[2730-6825],而46的第一部分槽位是从[0-2729],所以这样就连续住了)
[root@Rocky9 ~]# redis-cli -a 123456 --cluster reshard 10.0.0.46:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing Cluster Check (using node 10.0.0.46:6379)
# 分片之前会自动检查当前集群的节点信息(包括槽位的区间,node id,槽位的个数,slave对应master)
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots:[0-2729],[6826-8190],[12287] (4096 slots) master
1 additional replica(s)
S: 8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379
slots: (0 slots) slave
replicates c882647e71c8f3cc1b40ea20cc242fecca371cd4
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[2730-6825] (4096 slots) master
1 additional replica(s)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[12288-16383] (4096 slots) master
1 additional replica(s)
S: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots: (0 slots) slave
replicates 637a013fee8c69a87d4ca9d503f01d3817680f4c
M: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots:[8191-12286] (4096 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 1365 #本次要迁移槽位的个数
What is the receiving node ID? 5023eb920093b0da73426572ef189d98f337023e #接收槽位的主节点的id
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: c882647e71c8f3cc1b40ea20cc242fecca371cd4 #源主节点的id
Source node #2: done #输入done
Ready to move 1365 slots.
Source nodes:
M: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots:[0-2729],[6826-8190],[12287] (4096 slots) master
1 additional replica(s)
Destination node:
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[2730-6825] (4096 slots) master
1 additional replica(s)
Resharding plan:
Moving slot 0 from c882647e71c8f3cc1b40ea20cc242fecca371cd4
......
Moving slot 1364 from c882647e71c8f3cc1b40ea20cc242fecca371cd4
Do you want to proceed with the proposed reshard plan (yes/no)?yes #输入yes
Moving slot 0 from 10.0.0.46:6379 to 10.0.0.44:6379: ..
......
Moving slot 1364 from 10.0.0.46:6379 to 10.0.0.44:6379:
#非交互的方式
#再将1365个slot从10.0.0.46移动到第二个master节点10.0.0.45上
[root@Rocky9 ~]# redis-cli -a 123456 --cluster reshard 10.0.0.46:6379 --cluster-slots 1365 --cluster-from c882647e71c8f3cc1b40ea20cc242fecca371cd4 --cluster-to 637a013fee8c69a87d4ca9d503f01d3817680f4c --cluster-yes
# 最后的slot从10.0.0.46移动到第三个master节点10.0.0.42上
[root@Rocky9 ~]#redis-cli -a 123456 --cluster reshard 10.0.0.46:6379 --cluster-slots 1366 --cluster-from c882647e71c8f3cc1b40ea20cc242fecca371cd4 --cluster-to 767c6fc628516c09dd63fe4dbdc36cecee3808b3 --cluster-yes
#确认10.0.0.46的所有slot都移走了,并且在7系列版本中master自动会变成slave,并且之前它的slave,一起变成其他master的slave
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1737093990000 17 connected 6826-8190 12287-16383
8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1737093990641 17 connected #自动切换成42的slave
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1737093991650 15 connected 0-1364 2730-6825
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1737093992660 17 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 slave 637a013fee8c69a87d4ca9d503f01d3817680f4c 0 1737093989000 16 connected
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1737093991000 17 connected #切换成42的slave
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 master - 0 1737093991000 16 connected 1365-2729 8191-12286
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 15 connected
[root@node1 ~]#redis-cli -a 123456 --cluster check 10.0.0.40:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.42:6379 (767c6fc6...) -> 3345 keys | 5462 slots | 3 slaves.
10.0.0.44:6379 (5023eb92...) -> 3314 keys | 5461 slots | 1 slaves.
10.0.0.45:6379 (637a013f...) -> 3341 keys | 5461 slots | 1 slaves.
[OK] 10000 keys in 3 masters.
0.61 keys per slot on average.
>>> Performing Cluster Check (using node 10.0.0.40:6379)
S: 334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379
slots: (0 slots) slave
replicates 5023eb920093b0da73426572ef189d98f337023e
M: 767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379
slots:[6826-8190],[12287-16383] (5462 slots) master
3 additional replica(s)
S: 8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: 5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379
slots:[0-1364],[2730-6825] (5461 slots) master
1 additional replica(s)
S: 12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
S: c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379
slots: (0 slots) slave
replicates 637a013fee8c69a87d4ca9d503f01d3817680f4c
S: c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379
slots: (0 slots) slave
replicates 767c6fc628516c09dd63fe4dbdc36cecee3808b3
M: 637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379
slots:[1365-2729],[8191-12286] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
# 查看42的主从复制情况,有三个slave
[root@node1 ~]#redis-cli -a 123456 -h 10.0.0.42 info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:3
slave0:ip=10.0.0.43,port=6379,state=online,offset=488319,lag=0
slave1:ip=10.0.0.46,port=6379,state=online,offset=488319,lag=0
slave2:ip=10.0.0.47,port=6379,state=online,offset=488319,lag=1
master_failover_state:no-failover
master_replid:b4e315107bb91caf6f49e76488cf53329f776ab5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:488319
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:488319
[root@node1 ~]#
# 查看集群情况
[root@node1 ~]#redis-cli -a 123456 -h 10.0.0.40 --no-auth-warning cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:8
cluster_size:3 #有3个主节点
cluster_current_epoch:17
cluster_my_epoch:15
cluster_stats_messages_ping_sent:135374
cluster_stats_messages_pong_sent:128159
cluster_stats_messages_fail_sent:7
cluster_stats_messages_update_sent:2
cluster_stats_messages_sent:263542
cluster_stats_messages_ping_received:128157
cluster_stats_messages_pong_received:151740
cluster_stats_messages_meet_received:2
cluster_stats_messages_fail_received:2
cluster_stats_messages_auth-req_received:1
cluster_stats_messages_update_received:1
cluster_stats_messages_received:279903
total_cluster_links_buffer_limit_exceeded:0
[root@node1 ~]#
方法2:自动迁移
bash
[root@Rocky9 ~]# redis-cli -a 123456 --cluster rebalance 10.0.0.40:6379 --cluster-weight c882647e71c8f3cc1b40ea20cc242fecca371cd4=0 --cluster-use-empty-masters
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing Cluster Check (using node 10.0.0.40:6379)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 4 nodes. Total weight = 3.00
Moving 1366 slots from 10.0.0.46:6379 to 10.0.0.42:6379
###################################################################################################################################################################
......
Moving 1365 slots from 10.0.0.46:6379 to 10.0.0.44:6379
###################################################################################################################################################################
......
Moving 1365 slots from 10.0.0.46:6379 to 10.0.0.45:6379
###################################################################################################################################################################
......
[root@Rocky9 ~]# redis-cli -a 123456 -h 10.0.0.40 -p 6379 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1737105281417 19 connected 0-1365 12288-16383
8b4c349f957e7166dc2834cc1f7109d73ee25542 10.0.0.47:6379@16379 slave 637a013fee8c69a87d4ca9d503f01d3817680f4c 0 1737105278000 21 connected
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1737105277383 20 connected 1366-6826
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1737105279000 19 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 slave 637a013fee8c69a87d4ca9d503f01d3817680f4c 0 1737105280000 21 connected
c882647e71c8f3cc1b40ea20cc242fecca371cd4 10.0.0.46:6379@16379 slave 637a013fee8c69a87d4ca9d503f01d3817680f4c 0 1737105280409 21 connected
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 master - 0 1737105278000 21 connected 6827-12287
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 20 connected
[root@Rocky9 ~]#
--cluster-weight <NodeID>=0
:将节点的权重设为 0,表示不要给它分配槽,并把它目前所有槽都迁走。
--cluster-use-empty-masters
:允许将新的空节点或指定节点也被纳入 rebalance 的考量之中。
在 rebalance 过程中,Redis 会把该节点上的全部槽迁移到其他主节点上(取决于当前集群状态,可能会平均迁移到多个节点)。
15.2 从集群删除节点
虽然槽位已经迁移完成了,但是服务器IP信息还在集群当中,因此还需要将IP信息从集群中删除
注意:删除服务器前,必须清除主机上面的槽位,否则会删除失败。
bash
# redis-cli -a 123456 --cluster del-node 10.0.0.40:6379 <NodeID_of_46>
[root@Rocky9 ~]# redis-cli -a 123456 --cluster del-node 10.0.0.46:6379 c882647e71c8f3cc1b40ea20cc242fecca371cd4
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Removing node c882647e71c8f3cc1b40ea20cc242fecca371cd4 from cluster 10.0.0.46:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
15.3 删除多余的slave节点
bash
[root@Rocky9 ~]# redis-cli -a 123456 --cluster del-node 10.0.0.47:6379 8b4c349f957e7166dc2834cc1f7109d73ee25542
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Removing node 8b4c349f957e7166dc2834cc1f7109d73ee25542 from cluster 10.0.0.47:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
# 查看集群节点(46和47都没有了)
[root@node1 ~]#redis-cli -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
767c6fc628516c09dd63fe4dbdc36cecee3808b3 10.0.0.42:6379@16379 master - 0 1737101127000 17 connected 6826-8190 12287-16383
5023eb920093b0da73426572ef189d98f337023e 10.0.0.44:6379@16379 master - 0 1737101128171 15 connected 0-1364 2730-6825
12b6dbf4fd222db57a57e0bbd42694377a107ca5 10.0.0.43:6379@16379 slave 767c6fc628516c09dd63fe4dbdc36cecee3808b3 0 1737101127159 17 connected
c9b13ad218d3c8aaf62153738bb21d08a59a66c8 10.0.0.41:6379@16379 slave 637a013fee8c69a87d4ca9d503f01d3817680f4c 0 1737101127000 16 connected
637a013fee8c69a87d4ca9d503f01d3817680f4c 10.0.0.45:6379@16379 master - 0 1737101125139 16 connected 1365-2729 8191-12286
334ecb1234284cfa2a60ced7c5a8d29f4cd5de10 10.0.0.40:6379@16379 myself,slave 5023eb920093b0da73426572ef189d98f337023e 0 0 15 connected
[root@node1 ~]#