Redis这么快,竟然也有阻塞点?

Redis 中的优秀设计有很多,今天我们一起来聊聊群友刷题遇到的:“Redis的异步机制 —— redis有哪些阻塞点以及如何解决?”

前言

说到 Redis,大家自然而然的会想到基于内存、单线程执行等。话说回来,Redis 真的只有单线程吗?这篇文章来说说 Redis 的异步机制。

图片

有哪些影响redis性能的因素

我们从 Redis 内部及外部因素总结一下,主要有:

  1. redis 内部的阻塞式操作

  2. CPU 核和 NUMA 架构的影响

  3. Redis 关键系统配置

  4. Redis 内存碎片

  5. Redis 缓冲区

首先说说 Redis 实例的阻塞点:

  • 客户端:网络 IO,键值对的增删改查操作,数据库操作;

  • 磁盘:生成 RDB 快照,记录 AOF 日志,AOF 日志重写;

  • 主从节点:主库生成、传输 RDB 文件,从库接收 RDB 文件、清空数据库、加载 RDB 文件;

  • 切片集群实例:向其他实例传输哈希槽信息,数据迁移。

客户端的阻塞

因 Redis 使用了 IO 多路复用机制,能避免主线程一直处于等待状态,网络 IO 不是导致 Redis 阻塞的因素。

而键值对的增删改查是主线程的主要工作,复杂度高的操作当然会阻塞Redis了。我们去判断复杂度高不高的标准就是看操作的复杂度是否为O(N),也就是否要全表扫描。比如hgetall, smembers等操作就属于复杂度高的了。

然后还要注意的一个点就是数据的删除。删除本质上来说就是对键值对的内存空间进行释放。在释放内存时,操作系统需要将释放掉的内存块插入一个空闲内存块的链表,以便后续管理和再分配。这个过程会阻塞当前释放内存的应用程序。

如果这个键值对数据很大,比如一个 zset 包含大量元素,就会释放大量的内存。有测试过删除 100 万个元素的集合时,删除时间会达到 2s,要知道 Redis 的响应是毫秒级别的。所以这种 bigkey 的删除也会成为 Redis 的阻塞点。

清空数据库(flushdb、flushall)也涉及到删除和释放所有的键值对,也是 Redis 的阻塞点。

磁盘带来的阻塞

AOF重写和RDB快照,Redis都用了子进程的方式操作,所以不会阻塞主线程。但Redis直接记录AOF日志,若有大量的写操作,并且配置的是同步写回的话,就会阻塞主线程了。

主从节点带来的阻塞

在主从集群中,主库生成 RDB 文件,并传输给从库。主从复制过程的创建和传输 RDB 都是子进程处理的,不会阻塞主线程。但是从库在接收了 RDB 文件后,需要使用 FLUSHDB 命令清空当前数据库,这又是一个阻塞点。而且,在从库清空数据库后,需要将 RDB 文件加载到内存,快慢和 rdb 文件大小相关。加载 RDB 文件又是一个阻塞点。

切片集群的阻塞

切片集群的实例在负载均衡或者实例增加删除时,数据迁移是渐进式操作的,所以不会阻塞主线程。

总结一下,Redis 就有 5 个阻塞点:

  • 集合全量查询和聚合操作;

  • bigkey 删除;

  • 清空数据库;

  • AOF 日志同步写;

  • 从库加载 RDB 文件。

异步机制解决阻塞

我们通过异步的方式,去解决可能阻塞的场景。但也不是每个操作都能用异步的方式去解决。如果一个操作能够异步执行,说明客户端不需要马上得到具体值,在Redis中描述为「若一个操作能异步执行,就意味着它不是主线程的关键路径的操作。」

对于第一个阻塞点,因为读操作需要等待数据的返回,所以「第一个阻塞点不能异步执行。」 第二个阻塞点和第三个阻塞点,因为删除不需要返回具体的结果,因此都可以用子线程去异步执行。「第四个阻塞点“AOF日志同步写”,也可启动子线程操作,不用让主线程等待AOF日志的写完成。」 第五个阻塞点 「“从库加载 RDB 文件”,从库要想对客户端提供数据存取服务,就必须把RDB文件加载完成,不能启用子进程。」

异步是如何进行的

Redis 主线程启动后,会使用操作系统提供的 pthread_create 函数创建 3 个子线程,分别由它们负责 AOF 日志写操作、键值对删除以及文件关闭的异步执行。

异步删除lazy-free

lazy-free 机制是 Redis 收到删除指令后,主线程会将这个操作放入队列,然后马上给客户端返回一个完成信息。实际上删除还没执行呢。lazy-free 是 Redis4.0 之后才有的功能,需要手动开启。需要注意的是,即使开启了 lazy-free,如果直接使用 DEL 命令还是会同步删除 key,只有使用 UNLINK 命令才会可能异步删除 key。而且 Redis 在删除一个 key 时,首先会评估删除的时间成本,如果成本小,也不会异步执行,直接用主线程就完成返回了。

小结

本文总结 Redis 有哪些阻塞点,以及这些阻塞点是否可用异步机制去解决。但我们在使用 Redis 时,还是要避免 bigkey 的使用。

<think>好的,我需要回答用户关于Redis具体使用场景及其优势的问题。首先,我要回顾用户提供的引用内容,特别是引用[3]、[4]、[5],因为它们详细描述了Redis的数据结构和使用场景。用户之前问了使用场景和优势,所以需要结构清晰地分点说明。 首先,确定主要使用场景,如缓存、会话存储、排行榜、消息队列、实时分析等。每个场景需要结合具体例子,比如缓存对象或页面,用字符串类型;会话存储用哈希或字符串。排行榜使用有序集合,消息队列用列表或流。实时分析可能需要集合或位图。 接下来,每个场景要分析Redis的优势,比如高性能、丰富的数据类型、原子操作、持久化等。例如,缓存场景得益于内存存储和高速读写,排行榜利用有序集合和原子递增。消息队列使用列表的LPUSH/BRPOP实现队列,并支持阻塞操作。 需要确保每个部分都引用对应的引用资料,比如引用[3]提到字符串用于缓存和计数器,引用[4]提到排行榜和消息队列,引用[5]提到地理位置服务。同时,避免使用用户不可见的格式,保持回答自然,但按照用户要求的结构分点,使用标题和子标题,用中文,数学公式正确应用$...$和$$...$$格式。 可能还需要检查是否有遗漏的场景,如分布式锁、LBS服务,引用[3]和[5]提到这些,要补充进去。同时,优势分析部分要结合引用[2]和[4],比如速度、数据类型支持、事务性等。 最后,生成相关问题时要基于已提到的内容,比如不同数据结构的应用、持久化配置、性能优化等。确保问题相关且有深度,帮助用户进一步探索。</think>### Redis 详细使用场景及优势分析 #### 一、核心使用场景 1. **缓存系统** - **场景**:高频读取低频写入的数据(如用户信息、商品详情、新闻内容),可降低数据库压力。 - **实现方式**:使用字符串(String)或哈希(Hash)存储结构化数据,例如: ```bash SET user:1 "{name: 'Alice', age: 28}" ``` - **优势**: - 内存读写速度达 $O(1)$,响应时间在微秒级[^2]; - 支持过期时间(TTL),自动清理冷数据[^3]。 2. **会话存储(Session Storage)** - **场景**:分布式系统中存储用户登录状态、购物车信息。 - **实现方式**:采用哈希表存储多字段会话信息: ```bash HSET session:abc123 last_active 1620000000 cart_items "[item1, item2]" ``` - **优势**: - 数据集中存储,避免多服务器间同步问题; - 原子操作确保一致性[^4]。 3. **实时排行榜与计数器** - **场景**:直播打赏榜、短视频点赞数统计。 - **实现方式**:有序集合(Sorted Set)按分数排序,例如: ```bash ZINCRBY video_rank 1 "video:789" ``` - **优势**: - 原子操作保证并发安全; - 支持范围查询(如 Top 10)和实时更新[^5]。 4. **消息队列** - **场景**:异步处理邮件发送、订单状态更新。 - **实现方式**:列表(List)实现 FIFO 队列,或流(Stream)支持多消费者: ```bash LPUSH task_queue "send_email:[email protected]" ``` - **优势**: - 支持阻塞读取(BRPOP),避免轮询资源浪费; - 持久化防止消息丢失[^4]。 5. **地理位置服务(LBS)** - **场景**:滴滴叫车、附近餐厅搜索。 - **实现方式**:有序集合存储经纬度(GeoHash 编码),例如: ```bash GEOADD restaurants 116.40 39.90 "KFC" GEORADIUS restaurants 116.41 39.91 5 km ``` - **优势**: - 速计算两点距离(GEODIST); - 高效范围查询(GEORADIUS)[^5]。 #### 二、不同场景下的优势对比 | 场景 | 核心优势 | 适用数据结构 | |--------------|--------------------------------------------------------------------------|-----------------------| | 缓存 | 内存读写速度 $10^5$ 次/秒级响应[^2] | String/Hash | | 排行榜 | 原子操作避免竞态条件,复杂度 $O(\log N)$ 的排序[^5] | Sorted Set | | 消息队列 | 支持阻塞操作和持久化,吞吐量达 $10^4$ 条/秒[^4] | List/Stream | | 分布式锁 | SETNX 命令实现互斥锁,RedLock 算法增强可靠性[^3] | String | | 实时分析 | HyperLogLog 统计 UV 误差率 <1%,位图(Bitmap)记录用户签到[^3] | HyperLogLog/Bitmap | #### 三、局限性及应对 - **内存限制**:单实例数据量受物理内存限制,可通过 Redis Cluster 分片扩展[^4]。 - **持久化风险**:RDB 照可能丢失最近数据,需结合 AOF 日志(append-only file)增强可靠性[^4]。 - **冷数据成本**:内存成本较高,建议对低频数据设置 TTL 或启用 LRU 淘汰策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汤姆yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值