内容详情 您现在的位置是: 首页> 其他随笔

Redis系列1:深刻理解高性能Redis的本质

发布时间:2022-11-21 17:50 已围观:2138

摘要Redis系列1:深刻理解高性能Redis的本质

1 背景

分布式系统绕不开的核心之一的就是数据缓存,有了缓存的支撑,系统的整体吞吐量会有很大的提升。通过使用缓存,我们把频繁查询的数据由磁盘调度到缓存中,保证数据的高效率读写。
当然,除了在内存内运行还远远不够,我们今天就以具有代表性的缓存中间件Redis为例子,分析下,它是如何达到飞起的效率。

2 Redis高效性能分析

Redis之所以能够提供超高的执行效率,主要从以下几个维度来实现的:
image

  • 存储模式:基于内存实现,而非磁盘
  • 数据结构:基于不同业务场景的高效数据结构
    • 动态字符串(REDIS_STRING):整数(REDIS_ENCODING_INT)、字符串(REDIS_ENCODING_RAW)
    • 双端列表(REDIS_ENCODING_LINKEDLIST)
    • 压缩列表(REDIS_ENCODING_ZIPLIST)
    • 跳跃表(REDIS_ENCODING_SKIPLIST)
    • 哈希表(REDIS_HASH)
    • 整数集合(REDIS_ENCODING_INTSET)
  • 线程模型: Redis 的网络 IO 以及键值对指令读写是由单个线程来执行的,避免了不必要的contextswitch和竞选
  • I/O 模型: 基于I/O多路复用模型,非阻塞的I/O模型
  • 恰单的数据编码: 根据实际数据类型,选择合理的数据编码

2.1 官网的性能报告

Redis官方站点中,有对Redis性能做了比较详细的压测,可以参考官方这一篇 How fast is Redis?
在较高的配置基准下(比如 8C 16G +),在连接数为0~10000的时候,最高QPS可达到120000。Redis以超过60000个连接为基准,仍然能够在这些条件下维持50000个q/s,体现了超高的性能。下图中横轴是连接数,纵轴是QPS。
image
下面这张图为data size 与整体吞吐量之间的趋向关系:
image

这个大概可以得出一个容量预估,比如你的服务用户量是多少,预估峰值QPS是多少,集群需要配置多少个实例(虽然实例的多少不能线性计算),可以大致推算出去。

2.2 基于内存实现

Redis的读写操作都是在内存中实现了,相对其他的持久化存储(如MySQL、File等,数据持久化在磁盘上),性能会高很多。因为在们在操作数据的时候,需要通过 IO 操作先将数据读取到内存里,增加工作成本。

image

上面那张图来源于网络,可以看看他的金字塔模型,越往上执行效率越高,价格也就越贵。下面给出每一层的执行耗时对比:

  • 寄存器:0.3 ns
  • L1高速缓存:0.9 ns
  • L2高速缓存:2.8 ns
  • L3高速缓存:12.9 ns
  • 主存:120 ns
  • 本地二级存储(SSD):50~150 us
  • 远程二级存储:30 ms
    这样可能不直观,我们举个L1和SSD的对比,如果L1耗时1s的话,SSD中差不多要15~45小时。
    因为 CPU 内部集成了内存控制器,所以CPU直接控制了内存,给予通信上的最优带宽。上面的部分数据引用自《性能之巅:洞悉系统、企业与云计算》。

2.3 适配多元场景的高效数据结构

在 Redis 缓存中,常用的主要数据类型有五种,如下:

  • 字符串/REDIS_STRING:适用于 缓存、计数、共享Session、IP统计、分布式锁等。
  • 列表/REDIS_LIST: 链表、消息队列、栈、有序的对象列表(如朋友圈的点赞顺序列表、评论顺序列表)。
  • 哈希表/REDIS_HASH: 购物车信息、用户信息、Hash类型的(key, field, value)存储对象等。
  • 集合/REDIS_SET:无序的唯一的键值结构: 好友、关注、粉丝、感兴趣的人集合等。
  • 有序集合/REDIS_ZSET:访问排行榜、点赞排行、粉丝数排行等。

上面这5种Redis 支持的数据类型,能够满足不同业务场景下的数据结构需求。而对于这几类数据类型的区分和支持,目的无非也是为了效率,具体的业务中使用恰当的数据结构才能保证得到应有的效率。

这5种数据类型都有一种或者多种数据结构来支撑,底层数据结构有 7 种。关系如下:
image
限免我们对这些数据结构一个个的分析。

2.3.1 SDS 简单动态字符串

Redis使用简单动态字符串(simple dynamic string,SDS)来表示字符串,Redis中字符串类型包含的数据结构有:整数(R_INT) 、 字符串(R_RAW)。我们以字符串为例子,常规的字符串,如 "Brand",如果要获取他的长度,需要从头开始遍历,直至遇到

声明:本文内容摘自网络,版权归原作者所有。如有侵权,请联系处理,谢谢~
转发:Hello-Brand--https://www.cnblogs.com/wzh2010/p/15886787.html

赞一个 (309)