分布式架构Redis中有哪些数据结构及底层实现原理


Posted in Redis onMarch 13, 2022

引言

面完了负载均衡,正向代理,反向代理,终于松了一口气,然后话题转向了缓存Redis,为什么是这个顺序呢?

回想了一下系统架构,我大概知道原因了。

分布式架构Redis中有哪些数据结构及底层实现原理

Redis 处于服务最上层。面试官是按照这个顺序从上到下考察我对整个系统设计能力,围着整个系统自顶向下的结构考察基础。不纠结这么多,反正先问后问,Redis一定是你必须掌握的。

1、面试官:我看你提到,项目中使用了Reids作为缓存,为什么是Reids而不是其他,Redis有什么优势吗?

问题分析: Redis的设计理念已经成了很多一线互联网公司自主研发分布式缓存框架的标杆,因为相比传统的 Memcache ,Redis 丰富的数据结构实在太香。

答:

  • 首先 Redis 支持丰富的数据结构,新版本数据结构从最初的5种变成9种。
  • 其次 Redis 是读写单进程单线程,不用考虑并发读写的复杂场景,速度也快。
  • Reids 功能完备,支持数据持久化,支持主从复制和集群。
  • 还有Lua脚本,事务,发布订阅模型,Reids 都支持。

在高并发请求时,为何我们频繁提到缓存技术?最直接的原因是,磁盘IO及网络开销是直接请求内存IO千百上千倍,做个简单计算,如果我们需要某个数据,该数据从数据库磁盘读出来需要0.0045S,经过网络请求传输需要0.0005S,那么每个请求完成最少需要0.005S,该数据服务器每秒最多只能响应200个请求,而如果该数据存于本机内存里,读出来只需要100us,那么每秒能够响应10000个请求。通过将数据存储到离CPU更近的未位置,减少数据传输时间,提高处理效率,这就是缓存的意义。

给您列举一个我利用Reids把项目QPS提到几十万级别的案例:

一个风控系统在日常24H中 Redis集群 QPS 曲线图,从业务低峰期几千或晚高峰最高30W,一个 Redis 集群都可轻松应对,30WQPS 在大型系统中流量并不算高,且不是核心系统,如果在多几倍几十倍多流量,一个结构优良的Redis 集群都可轻松应对,这充分说明了我们为什么要使用缓存,缓存可以把系统响应能力提高N个数量级,远高于传统基于硬盘的关系型数据库

分布式架构Redis中有哪些数据结构及底层实现原理

 面试官心想:看来是做足了功课。

2、面试官:刚刚你提到Redis是单线程,为什么单线程模型的 Redis 性能不减。

 问题分析:成功挖坑,提到单线程肯定会问我为什么要这样设计。

 答:

单线程不代表一定就慢,单线程有一个最大好处就是节省线程切换的开销,更不用考虑并发读写带来的复杂操作场景,这就大大节省了线程间切换的时间了。

单线程模型避免了多线程的频繁上下文切换,这也避免了多线程可能产生的竞争问题。

Reids 是基于内存的读写操作,内存肯定比传统磁盘IO数据库快。

Reids 核心是基于非阻塞的IO多路复用机制。

3、面试官:那你刚刚说的Redis数据结构都有哪几种,如何选择使用哪种?

问题分析: 常用的5种,重点学会这5种数据结构的使用足够了。

 答:比较常用的有5种

字符串 String: 字符串是 Redis 中最为基础的数据存储类型,数据结构简单,可存储文本,Json,图片数据等任何二进制文件。如姓名,订单号等,对于一些特殊的数据结构,比如List、Set等,建议采用相应的下面介绍的List和Set数据结构进行存储,这样不仅可以节省存储空间还可以提高操作效率。

列表 List: 类似 Java 中的 List ,按照插入顺序排序的字符串链表,在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。

集合 Set: 类似 Java 中的set,但它是一个无序集合,用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。可以使用Redis的Set数据类型跟踪一些唯一性数据,比如访问系统的唯一IP地址,唯一用户ID等信息,再比如在微博应用中,每个人的好友存在一个集合(set)中,这样求两个人的共同好友的操作,可能就只需要用求交集命令即可。

有序集合 Sorted Set: 类似 Java 中的 TreeSet,支持从小到大排序的 set,适用于排行榜结构的数据存储。

Hash: 类型相当于Java中的HashMap,所以该类型非常适合于存储值对象的信息,比如用户基本信息对象含有昵称、性别和Age等属性,可以使用Hash来存储User对象,Key可以为用户的唯一ID属性。

除此之外,新版本的Redis还提供了位图,地理坐标,流几种结构。

深入分析

曾经有面试官问我,你看过Reids源码吗,我说没有看过,他说有精力可以研究一下,Redis那几种常用的数据结构底层实现原理还是值得学习的。

1、简单动态字符串结构,Redis字符串的实现方式

简单动态字符串(simple dynamic string)简称SDS。Redis使用C语言编写,但是传统的C字符串使用长度为 N+1 的字符串数组来表示长度为N的字符串,所以为了获取一个长度为C字符串的长度,必须遍历整个字符串。和C字符串不同,动态字符串的数据结构中,有专门用于保存字符串长度的变量,我们可以通过获取len属性的值,直接知道字符串长度,从一定程度上提高了读取效率。

分布式架构Redis中有哪些数据结构及底层实现原理

 Redis源码中,动态字符串的定义:

/*  
 * 保存字符串对象的结构  
 */  
struct sdshdr {  
    // buf 中已占用空间的长度  
    int len;  
    // buf 中剩余可用空间的长度  
    int free;  
    // 数据空间  
    char buf[];  
};

len 变量,用于记录buf 中已经使用的空间长度。

free 变量,用于记录buf 中还空余的空间,初次分配空间,一般没有空余,在对字符串修改的时候,会有剩余空间出现,这样做是为了杜绝C语言中缓冲区溢出的可能性,当我们需要对一个SDS进行修改的时候,Redis 会在执行拼接操作之前,预先检查给定SDS空间是否足够,如果不够,会先拓展SDS的空间,然后再执行拼接操作。

buf 字符数组,用于记录我们的字符串(记录Redis)。

2、链表数据结构,List 底层结构

链表还是常规的普通双端链表,可以支持反向查找和遍历,更方便操作,通过增删节点来灵活地调整链表的长度,双端链表在Redis内部也是被多次使用:

  • 事务模块使用双端链表依序保存输入的命令。
  • 服务器模块使用双端链表来保存多个客户端。
  • 订阅/发送模块使用双端链表来保存订阅模式的多个客户端。
  • 事件模块使用双端链表来保存时间事件(time event)。

分布式架构Redis中有哪些数据结构及底层实现原理

3、跳跃表,sorted set底层结构

 Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,(如果你还不了解红黑树,需要先额外补补功课),HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

那为什么Redis的作者使用 SkipList 结构而不是红黑树?

红黑树:红黑树的查找效率很高,但是在进行重新平衡时,会涉及到大量节点的变化,因此实现和操作起来都比较复杂。

跳跃表:通过简单的多层索引结构,实现简单,且能达到近似于红黑树的查找效率,插入节点(多层插入)不需要像红黑树那样有额外操作。而且跳跃表还能实现范围查找及输出,而红黑树只支持单个元素查找,对于范围查找效率低。

关于缓存的一些算法

(偷偷告诉你,这几个关于Reids的算法很大概率也会被问到,需要多少知道几种)

常用缓存数据淘汰策略

缓存是非常宝贵的资源,不能把所有数据都放入缓存,只能把最重要的或者要求查询速度最快的数据缓存起来,比如微博热门话题排行榜功能,通常使用缓存查询,而不是数据库。

FIFO(First In First Out): 先进先出算法,即先放入缓存的先被移除。

LRU(Least Recently Used): 最近最少使用算法,使用时间距离现在最久的那个被移除。

LFU(Least Frequently Used): 最不常用算法,一定时间段内使用次数(频率)最少的那个被移除。

缓存数据更新策略

  • 定时任务从数据库直接更新缓存:适用于对时间不敏感的数据。
  • 查询时写缓存,即查询优先查询缓存,若缓存未命中,查询数据库,将返回结果写入缓存,数据更新时先 delete缓存,再更新缓存。
  • MQ 消息异步更新缓存,后文中会针对MQ的应用做单独讲解。

总结

这一节重点讲解分布式缓存 Redis ,本地缓存不一定每个项目都会使用,但是 Redis 数据设计合理,保证超高命中率,集群足够稳定,那完全可以替代一级本地缓存。所以 Redis 非常值得你花更多时间学习。分布式缓存是面试必问。

Redis 是建设高性能网站后台不可缺少的工具,无论你是面试业务开发工程师还是架构师,都需要熟练掌握。

关于Redis,推荐阅读黄建宏的《Redis 设计与实现》,能够掌握Redis的5种数据结构,Redis 的持久化方式 RDB 和 AOF,两者有什么优点和缺点,如何选型,以及了解高可用 Redis 集群的建设方案。

以上就是分布式架构Redis中有哪些数据结构及底层实现原理的详细内容,更多关于分布式架构Redis底层数据结构及原理的资料请关注三水点靠木其它相关文章!

Redis 相关文章推荐
解决redis sentinel 频繁主备切换的问题
Apr 12 Redis
redis内存空间效率问题的深入探究
May 17 Redis
5分钟教你docker安装启动redis全教程(全新方式)
May 29 Redis
使用Redis实现实时排行榜功能
Jul 02 Redis
Redis如何实现分布式锁
Aug 23 Redis
基于Redis zSet实现滑动窗口对短信进行防刷限流的问题
Feb 12 Redis
Spring Boot实战解决高并发数据入库之 Redis 缓存+MySQL 批量入库问题
Feb 12 Redis
解决Redis启动警告问题
Feb 24 Redis
解决redis批量删除key值的问题
Mar 23 Redis
Redis实战高并发之扣减库存项目
Apr 14 Redis
Redis数据同步之redis shake的实现方法
Apr 21 Redis
Redis全局ID生成器的实现
Jun 05 Redis
Redis之RedisTemplate配置方式(序列和反序列化)
Mar 13 #Redis
浅谈Redis跟MySQL的双写问题解决方案
解决linux下redis数据库overcommit_memory问题
Feb 24 #Redis
解决Redis启动警告问题
分布式Redis Cluster集群搭建与Redis基本用法
Redis命令处理过程源码解析
Redis+Lua脚本实现计数器接口防刷功能(升级版)
You might like
基于simple_html_dom的使用小结
2013/07/01 PHP
PHP变量的定义、可变变量、变量引用、销毁方法
2013/12/20 PHP
教你如何用php实现LOL数据远程获取
2014/06/10 PHP
浅谈mysql_query()函数的返回值问题
2016/09/05 PHP
Laravel学习基础之migrate的使用教程
2017/10/11 PHP
jQuery实现将页面上HTML标签换成另外标签的方法
2015/06/09 Javascript
js实现简洁的滑动门菜单(选项卡)效果代码
2015/09/04 Javascript
AngularJS基础 ng-copy 指令实例代码
2016/08/01 Javascript
AngularJS教程之简单应用程序示例
2016/08/16 Javascript
vue.js初学入门教程(1)
2016/11/03 Javascript
jquery实现图片跟随鼠标的实例
2017/10/17 jQuery
windows下更新npm和node的方法
2017/11/30 Javascript
tangram.js库实现js类的方式实例分析
2018/01/06 Javascript
解决vue单页使用keep-alive页面返回不刷新的问题
2018/03/13 Javascript
el-select 下拉框多选实现全选的实现
2019/08/02 Javascript
createObjectURL方法实现本地图片预览
2019/09/30 Javascript
JS快速实现简单计算器
2020/04/08 Javascript
[47:43]Alliance vs KG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
python+matplotlib演示电偶极子实例代码
2018/01/12 Python
Python提取支付宝和微信支付二维码的示例代码
2019/02/15 Python
PyQt 图解Qt Designer工具的使用方法
2019/08/06 Python
关于TensorFlow新旧版本函数接口变化详解
2020/02/10 Python
Python基于pyjnius库实现访问java类
2020/07/31 Python
html5调用app分享功能示例(WebViewJavascriptBridge)
2018/03/21 HTML / CSS
The Athlete’s Foot新西兰:新西兰最大的运动鞋零售商
2019/12/23 全球购物
写好自荐信的要点
2013/11/06 职场文书
审计主管岗位职责
2014/01/31 职场文书
元旦活动感言
2014/03/08 职场文书
电子专业自荐信
2014/07/01 职场文书
业绩倒数第一的检讨书
2014/09/24 职场文书
汽车机电维修工求职信
2014/09/30 职场文书
学校群众路线专项整治方案
2014/10/31 职场文书
财务负责人岗位职责
2015/02/03 职场文书
春季运动会加油词
2015/07/18 职场文书
民警忠诚教育心得体会
2016/01/23 职场文书
php实例化对象的实例方法
2021/11/17 PHP