10万+QPS 真的只是因为单线程和基于内存?

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

你以为 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 属性来判断是否满足直接扩展,不满足则需要先执行内存重分配操作,然后再扩展字符串。

我们都知道修改字符串长度很有可能导致触发内存重分配操作

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值