你以为 Redis 这么快仅仅因为单线程和基于内存?
那么你想得太少了,我个人认为 Redis 的快是基于多方面的:不但是单线程和内存,还有底层的数据结构设计,网络通信的设计,主从、哨兵和集群等等方面的设计~
下面,我将 360° 为你揭开 Redis QPS达到10万/秒的神秘面纱。
一、底层数据结构设计
1、底层架构:
首先值得称赞的第一点:Redis 底层使用的数据结构很多,但是却没有直接使用这些数据结构来实现键值对数据库,而是基于数据结构创建了一个对象(redisObject)系统。(是不是觉得有点面向对象编程的意思 😂 ~)
对象系统里面包括了字符串对象,列表对象,哈希对象、集合对象和有序集合对象。
使用对象的好处:
- Redis 在执行命令之前,可以根据对象的类型判断这个对象是否可以执行给定的命令。
- 可以针对不用的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
一个对象怎么设置不同的数据结构实现?
在讲解前,我们必须要了解 Redis 对象的结构。
它三个重要的部分:type 属性、encoding 属性,和 ptr 属性。
我们用字符串对象为例:
我们都知道,Redis 的 SET 命令其实是针对字符串的,但是它也可以设置数值。那底层是怎么做的呢?
它会将 String 对象的 encoding 属性标识为 REDIS_ENCODING_INT,表示这个键对应的值是 Long 类型的整数。

而当我们利用 APPEND 命令往值后面添加字符串呢?
此时会将 String 对象的 encoding 属性的标识为 REDIS_ENCODING_RAW,表示这个值此时是简单动态字符串。

正是因为使用对象,通过 type、encoding和prt 属性,使得同一个对象可以适应在不同的场景下,使得不同的改变不需要创建新的键值对,这样使得 Redis 的对象使用效率非常的高。
2、灵活的字符串对象
Redis 的字符串对象采用三种编码:int、embstr 和 raw。
int 编码就不用说了,就是为了兼容 SET 命令可以设置数值。
而 embstr 和 raw 最大的区别就是内存分配操作次数:
- embstr 编码专门用于保存短字符串,所以它是通过调用一次内存分配函数来分配一块连续的空间,空间包含 redisObject 和 sdsshdr 两个结构,这样可以很好地利用缓存带来的优势。
- raw 编码则是用于保存长字符串,它通过调用两次内存分配函数来分别创建 redisObject 结构和 sdshdr 结构
3、绝妙的字符串优化策略
Redis 中字符串对象的底层是使用 SDS (Simple Dynamic String)实现的。
SDS 有三部分:
-
len:记录 buf 数组中已使用字节的数量,等于 SDS 锁保存字符串的长度
-
free:记录 buf 数组中未使用字节的数量
-
buf[]:字节数组,用于保存字符串
首先介绍一下使用 len 属性和 free 属性的好处:
得益于 SDS 有 len 属性,获取字符串长度的复杂度为 O(1);
得益于 SDS 有 free 属性,可以杜绝缓冲区溢出,字符串扩展前可以根据 free 属性来判断是否满足直接扩展,不满足则需要先执行内存重分配操作,然后再扩展字符串。
我们都知道修改字符串长度很有可能导致触发内存重分配操作
揭秘Redis高性能之谜

本文深入探讨Redis如何通过底层数据结构、I/O多路复用、主从复制及集群技术,实现高达10万QPS的性能。从对象系统、字符串优化到字典渐进rehash,再到Reactor模式、主从心跳检测与集群Gossip协议,全方位解析Redis的高效设计。
最低0.47元/天 解锁文章
3378

被折叠的 条评论
为什么被折叠?



