🌺The Begin🌺点点关注,收藏不迷路🌺

📌 前言

Redis作为业界最流行的缓存数据库,单机QPS可达10万+,是MySQL的几十倍。很多人知道Redis快是因为“基于内存”和“单线程”,但背后的技术细节远不止于此。本文将深入剖析Redis高性能的七大核心原因,从I/O模型到数据结构,从内存管理到网络协议,全方位解密Redis的快。


一、Redis性能概览

Redis为什么快

纯内存操作

读写直接访问内存

纳秒级延迟

无磁盘IO瓶颈

单线程模型

避免上下文切换

无锁竞争开销

简化数据结构

I/O多路复用

epoll/kqueue

单线程处理万级连接

高效数据结构

SDS动态字符串

ZipList压缩列表

SkipList跳表

对象共享

整数对象池

避免重复创建

高效序列化

RESP协议简单

二进制安全

渐进式Rehash

避免长时间阻塞


二、七大核心原因详解

2.1 纯内存操作(最核心)

Redis

查询

内存寻址

返回结果

MySQL

查询

查找索引

磁盘IO读取数据

返回结果

性能对比

存储介质 访问延迟 吞吐量 说明
L1/L2/L3缓存 1-10纳秒 - CPU缓存
内存(RAM) 50-100纳秒 10GB/s+ Redis主战场
SSD固态硬盘 10-100微秒 500MB/s 1000倍差距
机械硬盘 5-10毫秒 100MB/s 10万倍差距

Redis纯内存优势

  • 读写操作纳秒级完成
  • 无磁盘IO等待
  • 无磁头寻道时间
2.2 单线程模型(简化并发)

Redis单线程优势

单线程

无锁操作

无上下文切换

可预测性能

多线程模型问题

线程1

加锁

线程2

线程3

竞争/阻塞

上下文切换

性能损耗

为什么单线程反而更快?

开销类型 多线程代价 Redis单线程
锁竞争 加锁/解锁/等待
上下文切换 CPU保存/恢复状态
CPU缓存失效 线程切换导致 命中率高
代码复杂度 高(并发控制)

单线程瓶颈与突破

  • Redis 6.0引入多线程I/O(网络读写多线程,命令执行仍单线程)
  • 解决网络I/O瓶颈,大包场景提升明显
2.3 I/O多路复用

Redis I/O多路复用

客户端1

epoll
事件轮询器

客户端2

客户端3

客户端N

Redis单线程

传统BIO模型

客户端1

线程1

客户端2

线程2

客户端3

线程3

客户端N

线程N

I/O多路复用核心

系统调用 特点 连接数 Redis选择
select 最多1024连接
poll 链表存储,无上限
epoll 事件驱动,O(1) ✅ Linux
kqueue 同epoll ✅ BSD/macOS
// Redis epoll 核心事件循环伪代码
while (true) {
    // 阻塞等待事件就绪
    int numEvents = epoll_wait(epfd, events, maxEvents, timeout);
    
    for (int i = 0; i < numEvents; i++) {
        if (events[i].events & EPOLLIN) {
            // 读事件:客户端发送命令
            readQueryFromClient(events[i].data.fd);
        }
        if (events[i].events & EPOLLOUT) {
            // 写事件:向客户端返回结果
            sendReplyToClient(events[i].data.fd);
        }
    }
    
    // 执行待处理的命令
    processCommand();
}
2.4 高效的数据结构设计

核心特性

底层编码

Redis数据类型

String
字符串

List
列表

Hash
哈希

ZSet
有序集合

Set
集合

SDS
简单动态字符串

QuickList
快速列表

Dict 字典

ZipList 压缩列表

SkipList 跳表

ZipList 压缩列表

Dict 字典

IntSet 整数集合

✅ O(1)获取长度
✅ 杜绝缓冲区溢出
✅ 二进制安全

✅ 双向链表+ZipList
✅ 内存与性能平衡

✅ 渐进式Rehash
✅ 链地址法解决冲突

✅ 多层级索引
✅ O(logN)查询效率

Redis vs 传统数据结构对比

数据类型 传统C字符串 Redis SDS
获取长度 O(n)遍历 O(1)直接读取
缓冲区安全 容易溢出 自动扩容
二进制安全 不支持 支持
内存预分配 有(减少分配次数)
2.5 对象共享与整数池
# Redis 整数对象池(0-9999)
127.0.0.1:6379> SET key1 100
OK
127.0.0.1:6379> SET key2 100
OK
# key1和key2共享同一个整数对象

# 字符串对象不共享(开销太大)
127.0.0.1:6379> SET key3 "hello"
OK
127.0.0.1:6379> SET key4 "hello"
OK
# key3和key4是不同对象

对象共享机制

数据类型 是否共享 原因
整数(0-9999) ✅ 共享 高频使用,内存节省
整数(其他范围) ❌ 不共享 预分配成本高
字符串 ❌ 不共享 比较开销大
List/Hash/Set/ZSet ❌ 不共享 结构复杂
2.6 渐进式Rehash

渐进式Rehash过程

字典

ht0: 原哈希表

ht1: 新哈希表
扩容2倍

rehashidx: 迁移进度

每次CRUD迁移一个桶

写操作

同时写入ht0和ht1

读操作

先查ht0,再查ht1

// 渐进式Rehash核心逻辑
int dictRehash(dict *d, int n) {
    int empty_visits = n * 10;
    while (n-- && d->ht[0].used != 0) {
        // 跳过空桶
        while (d->ht[0].table[d->rehashidx] == NULL) {
            d->rehashidx++;
            if (--empty_visits == 0) return 1;
        }
        // 迁移一个桶
        dictEntry *de = d->ht[0].table[d->rehashidx];
        while (de) {
            dictEntry *next = de->next;
            // 重新计算hash并插入ht[1]
            h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;
            d->ht[0].used--;
            d->ht[1].used++;
            de = next;
        }
        d->ht[0].table[d->rehashidx] = NULL;
        d->rehashidx++;
    }
    return 0;
}

渐进式Rehash优势

  • 避免一次性迁移大量数据导致阻塞
  • 将O(n)操作分摊到每次CRUD中
  • 保证Redis始终响应及时
2.7 RESP协议简洁高效

RESP协议格式

+OK\r

简单字符串

-ERR\r

错误信息

:100\r

整数

$5\r
hello\r

批量字符串

*2\r
$3\r
GET\r
$3\r
key\r

数组

RESP协议特点

特性 说明 优势
文本协议 人类可读 调试方便
二进制安全 支持特殊字符 数据无损
解析简单 无复杂编码 CPU开销小
管道支持 批量命令 减少RTT

三、完整的请求处理流程

内存数据库 Redis主线程 内核epoll 客户端 内存数据库 Redis主线程 内核epoll 客户端 1. 发送命令请求 2. epoll通知读事件 3. read()读取命令 4. 解析RESP协议 5. 查找命令表 6. 执行命令 7. 返回数据 8. 格式化RESP响应 9. write()写入缓冲 10. 返回响应

四、Redis vs Memcached性能对比

对比维度 Redis Memcached
数据结构 丰富(5种+) 仅String
持久化 ✅ RDB/AOF ❌ 不支持
单线程模型
多线程I/O ✅ 6.0+
内存管理 jemalloc Slab分配
适用场景 复杂数据结构 简单KV缓存

五、性能测试数据

# Redis基准测试
redis-benchmark -q -n 100000 -c 50

# 典型输出(单机)
PING_INLINE: 85000 requests per second
PING_BULK: 82000 requests per second
SET: 78000 requests per second
GET: 83000 requests per second
LPUSH: 75000 requests per second

性能影响因素

因素 影响程度 优化建议
网络延迟 使用Pipeline/集群
键值大小 控制单key大小<10KB
慢命令 避免KEYS/大范围查询
持久化 合理配置RDB/AOF
内存碎片 定期内存整理

六、面试高频问题

Q1:Redis真的是单线程吗?

Redis核心命令执行是单线程,但:

  • 6.0之前:网络I/O也是单线程
  • 6.0开始:网络I/O可配置多线程
  • 后台任务:RDB/AOF/Rehash/过期清理由独立线程处理

Q2:单线程如何利用多核CPU?

启动多个Redis实例(哨兵/集群模式),每个实例绑定不同CPU核心。

Q3:Redis单线程为什么还能处理10万+QPS?

  1. 纯内存操作(快)
  2. 非阻塞I/O + epoll(不等待)
  3. 命令执行快(无复杂计算)

Q4:哪些命令会导致Redis阻塞?

  • KEYS *(扫全库)
  • FLUSHALL/FLUSHDB
  • 大key删除(DEL大hash/list)
  • 大key范围查询(SMEMBERS大set)
  • 复杂聚合(SORT、ZUNIONSTORE)

七、总结

Redis高性能总结

内存为王

纳秒级访问

1000倍磁盘优势

单线程智慧

无锁竞争

可预测性能

I/O多路复用

epoll支撑万级连接

事件驱动模型

数据结构优化

SDS/QuickList/ZipList

渐进式Rehash

设计取舍

追求低延迟

放弃某些功能

层级 优化点 性能提升
硬件层 纯内存 10万倍+
系统层 epoll 万级并发
应用层 单线程 避免锁开销
数据结构层 SDS/字典 O(1)操作
协议层 RESP 解析高效

📌 下一篇预告:Redis中的缓存击穿、缓存穿透和缓存雪崩是什么?——三大缓存问题与解决方案

如果觉得本文对你有帮助,欢迎点赞、收藏、评论三连支持!

在这里插入图片描述


🌺The End🌺点点关注,收藏不迷路🌺

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐