前言

大家好呀,我们都知道 Redis 很快,它 QPS 可达 10 万(每秒请求数)。Redis 为什么这么快呢,本文将跟大家一起学习。

基于内存实现

我们都知道内存读写是比磁盘读写快很多的。Redis 是基于内存存储实现的数据库,相对于数据存在磁盘的数据库,就省去磁盘磁盘 I/O 的消耗。MySQL 等磁盘数据库,需要建立索引来加快查询效率,而 Redis 数据存放在内存,直接操作内存,所以就很快。

高效的数据结构

我们知道,MySQL 索引为了提高效率,选择了 B+树的数据结构。其实合理的数据结构,就是可以让你的应用/程序更快。先看下 Redis 的数据结构 &内部编码图:

SDS 简单动态字符串

struct sdshdr { //SDS简单动态字符串int len;    //记录buf中已使用的空间int free;   // buf中空闲空间长度char buf[]; //存储的实际内容
}

复制代码

字符串长度处理

在 C 语言中,要获取捡田螺的小男孩这个字符串的长度,需要从头开始遍历,复杂度为 O(n); 在 Redis 中, 已经有一个 len 字段记录当前字符串的长度啦,直接获取即可,时间复杂度为 O(1)。

减少内存重新分配的次数

在 C 语言中,修改一个字符串,需要重新分配内存,修改越频繁,内存分配就越频繁,而分配内存是会消耗性能的。而在 Redis 中,SDS 提供了两种优化策略:空间预分配和惰性空间释放。

空间预分配

当 SDS 简单动态字符串修改和空间扩充时,除了分配必需的内存空间,还会额外分配未使用的空间。分配规则是酱紫的:

SDS 修改后,len 的长度小于 1M,那么将额外分配与 len 相同长度的未使用空间。比如 len=100,重新分配后,buf 的实际长度会变为 100(已使用空间)+100(额外空间)+1(空字符)=201。

SDS 修改后, len 长度大于 1M,那么程序将分配 1M 的未使用空间。

惰性空间释放

当 SDS 缩短时,不是回收多余的内存空间,而是用 free 记录下多余的空间。后续再有修改操作,直接使用 free 中的空间,减少内存分配。

哈希

Redis 作为一个 K-V 的内存数据库,它使用用一张全局的哈希来保存所有的键值对。这张哈希表,由多个哈希桶组成,哈希桶中的 entry 元素保存了*key 和*value 指针,其中*key 指向了实际的键,*value 指向了实际的值。

哈希表查找速率很快的,有点类似于 Java 中的 HashMap,它让我们在 O(1) 的时间复杂度快速找到键值对。首先通过 key 计算哈希值,找到对应的哈希桶位置,然后定位到 entry,在 entry 找到对应的数据。

有些小伙伴可能会有疑问:你往哈希表中写入大量数据时,不是会遇到哈希冲突问题嘛,那效率就会降下来啦。

哈希冲突: 通过不同的 key,计算出一样的哈希值,导致落在同一个哈希桶中。

Redis 为了解决哈希冲突,采用了链式哈希。链式哈希是指同一个哈希桶中,多个元素用一个链表来保存,它们之间依次用指针连接。

有些小伙伴可能还会有疑问:哈希冲突链上的元素只能通过指针逐一查找再操作。当往哈希表插入数据很多,冲突也会越多,冲突链表就会越长,那查询效率就会降低了。

为了保持高效,Redis 会对哈希表做 rehash 操作,也就是增加哈希桶,减少冲突。为了 rehash 更高效,Redis 还默认使用了两个全局哈希表,一个用于当前使用,称为主哈希表,一个用于扩容,称为备用哈希表。

跳跃表

跳跃表是 Redis 特有的数据结构,它其实就是在链表的基础上,增加多级索引,以提高查找效率。跳跃表的简单原理图如下:

  • 每一层都有一条有序的链表,最底层的链表包含了所有的元素。

  • 跳跃表支持平均 O(logN),最坏 O(N)复杂度的节点查找,还可以通过顺序性操作批量处理节点。

压缩列表 ziplist

压缩列表 ziplist 是列表键和字典键的的底层实现之一。它是由一系列特殊编码的内存块构成的列表, 一个 ziplist 可以包含多个 entry, 每个 entry 可以保存一个长度受限的字符数组或者整数,如下:

  • zlbytes :记录整个压缩列表占用的内存字节数

  • zltail: 尾节点至起始节点的偏移量

  • zllen : 记录整个压缩列表包含的节点数量

  • entryX: 压缩列表里包含的各个节点

  • zlend : 特殊值 0xFF(十进制 255),用于标记压缩列表末端

由于内存是连续分配的,所以遍历速度很快。。

合理的数据编码

Redis 支持多种数据基本类型,每种基本类型对应不同的数据结构,每种数据结构对应不一样的编码。为了提高性能,Redis 设计者总结出,数据结构最适合的编码搭配。

Redis 是使用对象(redisObject)来表示数据库中的键值,当我们在 Redis 中创建一个键值对时,至少创建两个对象,一个对象是用作键值对的键对象,另一个是键值对的值对象。

typedef struct redisObject{//类型unsigned type:4;//编码unsigned encoding:4;//指向底层数据结构的指针void *ptr;//...}robj;

复制代码

redisObject 中,type 对应的是对象类型,包含 String 对象、List 对象、Hash 对象、Set 对象、zset 对象。encoding 对应的是编码。

  • String:如果存储数字的话,是用 int 类型的编码;如果存储非数字,小于等于 39 字节的字符串,是 embstr;大于 39 个字节,则是 raw 编码。

  • List:如果列表的元素个数小于 512 个,列表每个元素的值都小于 64 字节(默认),使用 ziplist 编码,否则使用 linkedlist 编码

  • Hash:哈希类型元素个数小于 512 个,所有值小于 64 字节的话,使用 ziplist 编码,否则使用 hashtable 编码。

  • Set:如果集合中的元素都是整数且元素个数小于 512 个,使用 intset 编码,否则使用 hashtable 编码。

  • Zset:当有序集合的元素个数小于 128 个,每个元素的值小于 64 字节时,使用 ziplist 编码,否则使用 skiplist(跳跃表)编码

合理的线程模型

单线程模型:避免了上下文切换

Redis 是单线程的,其实是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等等,实际是由额外的线程执行的。

Redis 的单线程模型,避免了 CPU 不必要的上下文切换竞争锁的消耗。也正因为是单线程,如果某个命令执行过长(如 hgetall 命令),会造成阻塞。Redis 是面向快速执行场景的内存数据库,所以要慎用如 lrange 和 smembers、hgetall 等命令。

什么是上下文切换?举个例子:

比如你在看一本英文小说,你看到某一页,发现有个单词不会读,你加了个书签,然后去查字典。查完字典后,你回来从书签那里继续开始读,这个流程就很舒畅。

如果你一个人读这本书,肯定没啥问题。但是如果你去查字典的时候,别的小伙伴翻了一下你的书,然后溜了。你再回来看的时候,发现书不是你看的那一页了,你得花时间找到你的那一页。

一本书,你一个人怎么看怎么打标签都没事,但是人多了翻来翻去,这本书各种标记就很乱了。可能这个解释很粗糙,但是道理应该是一样的。

I/O 多路复用

什么是 I/O 多路复用?

  • I/O :网络 I/O

  • 多路 :多个网络连接

  • 复用:复用同一个线程。

  • IO 多路复用其实就是一种同步 IO 模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出 cpu。

多路 I/O 复用技术可以让单个线程高效的处理多个连接请求,而 Redis 使用用 epoll 作为 I/O 多路复用技术的实现。并且 Redis 自身的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件,不在网络 I/O 上浪费过多的时间。

虚拟内存机制

Redis 直接自己构建了 VM 机制 ,不会像一般的系统会调用系统函数处理,会浪费一定的时间去移动和请求。

Redis 的虚拟内存机制是啥呢?

虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过 VM 功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。

小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。

原文作者:java后端框架

原文出处:xie.infoq.cn/article/0eca2b418bbc7cc38050a1dea

大厂 Java 后端经典面试题:Redis 为什么这么快?相关推荐

  1. 各大厂800道Java后端经典面试题合集

    前言 在茫茫的互联网海洋中寻寻觅觅,我收藏了800+道Java经典面试题,分享给你们.建议大家收藏起来,在茶余饭后拿出来读一读,以备未雨绸缪之需.另外,面试题答案的话,可以私信我, 希望大家都能找到自 ...

  2. 收藏了800道Java后端经典面试题,共享给大家

    在茫茫的互联网海洋中寻寻觅觅,我收藏了800+道Java经典面试题,共享给你们.建议大家收藏起来,在茶余饭后拿出来读一读,以备未雨绸缪之需.另外,面试题答案的话,我打算后面慢慢完善在github, 希 ...

  3. 收藏了800道Java后端经典面试题,分享给大家,希望你找到自己理想的Offer呀~

    在茫茫的互联网海洋中寻寻觅觅,我收藏了800+道Java经典面试题,分享给你们.建议大家收藏起来,在茶余饭后拿出来读一读,以备未雨绸缪之需.另外,面试题答案的话,我打算后面慢慢完善在github, 希 ...

  4. java 1.8有没有jshell_收藏了800道Java后端经典面试题,分享给大家,希望你找到自己理想的Offer呀~...

    前言 在茫茫的互联网海洋中寻寻觅觅,我收藏了800+道Java经典面试题,分享给你们.建议大家收藏起来,在茶余饭后拿出来读一读,以备未雨绸缪之需.另外,面试题答案的话,我打算后面慢慢完善在github ...

  5. 800道Java后端经典面试题,希望你找到自己理想的Offer呀~

    Java 基础 Java 集合 Java 并发 && 多线程 JVM 篇 数据库 缓存/Redis 计算机网络 消息队列 mybatis 操作系统 Spring Netty/tomca ...

  6. Top 10国际大厂人工智能岗位经典面试题精选

    Top 10国际大厂人工智能岗位经典面试题精选 https://www.toutiao.com/a6635196559355019780/ 2018-12-15 20:31:25 AI专业应届毕业生年 ...

  7. java后端工程师面试题(笔试):2022-11-04 经历(一)

    java后端工程师面试题(笔试):2022-11-04: 面试题:总分100 1.关于盒子模型(5分) 1)盒子模型的种类有几种?分别是什么?(1分) 2种,分别是1.W3C标准盒子模型 2.IE盒子 ...

  8. Java后端真实面试题大全(有详细答案)--高频/真题

    原文网址:Java后端真实面试题大全(有详细答案)--高频/真题_IT利刃出鞘的博客-CSDN博客 简介 说明 本文分享Java后端真实高频面试题,有详细答案,保你稳过面试.题目包括:Java基础.多 ...

  9. Java 面试必问题目,Java 后端校招面试题

    字节跳动一面: 自我介绍,主要讲讲做了什么和擅长什么 看你项目做 Spring 比较多, 问一下 Spring 相关的东西, IoC 是什么概念? Bean 的默认作用范围是什么?其他的作用范围? 索 ...

最新文章

  1. linux 查看 文档 不显示注释 命令
  2. 进度条三方库tdqm trange用法
  3. 金三银四面试季来临,最新BAT面试资料分享给大家
  4. JSP知识点笔记-常用技术方法
  5. Unbutu下安装mysql服务并允许远程登录
  6. shader 4 杂 一些和函数名词、数据结构
  7. java mysql nclob_java语言操作Oracle数据库中的CLOB数据类型 (转)
  8. uboot 的 bootcmd 和bootargs参数详解
  9. 使用ajax局部更新Razor页面
  10. AngularJS入门(用ng-repeat指令实现循环输出)
  11. 目标检测(三)--DPM
  12. sharepoint 2007 无法自动跳转到default.aspx
  13. oracle视图、函数、循环、case when
  14. 思维方式-《成功与运气》书中的精髓:成功到底是靠天赋、努力,还是运气?
  15. python数据可视化学习之随机漫步
  16. 怎样把d盘改成c盘!如何把收藏夹和桌面的路径设成D盘
  17. 小米iot业务_一文看懂小米2019上半年财报:IoT平台连接设备达1.96亿台
  18. redis分布式锁unlock方法
  19. ddd 访问权限_Lind.DDD.Authorization用户授权介绍
  20. Kali2021.1 关于w3af的一些问题

热门文章

  1. 程序员如何高效提升学习能力?做到这三点,你就会与众不同
  2. python jupyter notebook 多个excel文档合并
  3. Python神笔马良案例集简介
  4. 【数字IC手撕代码】Verilog偶数分频|题目|原理|设计|仿真(二分频,四分频,六分频,八分频,偶数分频及特殊占空比)
  5. dx绘制2d图像_在DirectX 中进行2D渲染
  6. 一文读懂时序预测模型(1)
  7. STM32系列(HAL库)——F103C8T6通过MFRC522、RFID射频卡、门禁卡模块读取卡片ID
  8. 成熟男人的修炼-国王、祭祀、诗人、武士
  9. python中uppercase是什么意思_Python string.ascii_uppercase方法代码示例
  10. 江苏电信服务器托管/1U托管/4G硬防