首頁  >  文章  >  資料庫  >  深入解析一下Redis為什麼這麼快?

深入解析一下Redis為什麼這麼快?

青灯夜游
青灯夜游轉載
2021-09-16 19:46:342616瀏覽

深入解析一下Redis為什麼這麼快?

我們都知道Redis很快,它QPS可達10萬(每秒請求數)。 Redis為什麼這麼快呢,這篇文章將跟大家一起學習。 【相關推薦:Redis影片教學

深入解析一下Redis為什麼這麼快?

基於記憶體實作

我們都知道記憶體讀寫是比磁碟讀寫快很多的。 Redis是基於記憶體儲存實作的資料庫,相對於資料存在磁碟的資料庫,就省去磁碟磁碟I/O的消耗。 MySQL等磁碟資料庫,需要建立索引來加快查詢效率,而Redis資料存放在內存,直接操作內存,所以就很快。

深入解析一下Redis為什麼這麼快?

高效率的資料結構

我們知道,MySQL索引為了提高效率,選擇了B 樹的資料結構。其實合理的資料結構,就是可以讓你的應用程式/程式更快。先看下Redis的資料結構&內部編碼圖:

深入解析一下Redis為什麼這麼快?

##SDS簡單動態字串

深入解析一下Redis為什麼這麼快?

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指向了實際的值。

深入解析一下Redis為什麼這麼快?

哈希表查找速率很快的,有點類似Java中的

HashMap,它讓我們在O(1) 的時間複雜度快速找到鍵值對。首先透過key計算哈希值,找到對應的哈希桶位置,然後定位到entry,在entry找到對應的資料。

有些小夥伴可能會有疑問:你往雜湊表中寫入大量資料時,不是會遇到

雜湊衝突問題嘛,那效率就會降下來啦。

哈希衝突: 透過不同的key,計算相同的雜湊值,導致落在同一個雜湊桶中。

Redis為了解決哈希衝突,採用了

鍊式哈希。鍊式雜湊是指同一個雜湊桶中,多個元素用一個鍊錶來保存,它們之間依序用指標連接。

深入解析一下Redis為什麼這麼快?

有些小夥伴可能還會有疑問:雜湊衝突鏈上的元素只能透過指標逐一尋找再操作。當往哈希表插入資料很多,衝突也會越多,衝突鍊錶就會越長,那麼查詢效率就會降低了。

為了保持高效,Redis 會對雜湊表做

rehash操作,也就是增加雜湊桶,減少衝突。為了rehash更有效率,Redis還預設使用了兩個全域雜湊表,一個用於目前使用,稱為主雜湊表,一個用於擴容,稱為備用雜湊表。

跳跃表

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

深入解析一下Redis為什麼這麼快?

  • 每一层都有一条有序的链表,最底层的链表包含了所有的元素。
  • 跳跃表支持平均 O(logN),最坏 O(N)复杂度的节点查找,还可以通过顺序性操作批量处理节点。

压缩列表ziplist

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

深入解析一下Redis為什麼這麼快?

  • 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等命令。

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

  • 比如你在看一本英文小说,你看到某一页,发现有个单词不会读,你加了个书签,然后去查字典。查完字典后,你回来从书签那里继续开始读,这个流程就很舒畅。
  • 如果你一个人读这本书,肯定没啥问题。但是如果你去查字典的时候,别的小伙伴翻了一下你的书,然后溜了。你再回来看的时候,发现书不是你看的那一页了,你得花时间找到你的那一页。
  • 一本书,你一个人怎么看怎么打标签都没事,但是人多了翻来翻去,这本书各种标记就很乱了。可能这个解释很粗糙,但是道理应该是一样的。

深入解析一下Redis為什麼這麼快?

I/O 多路复用

什么是I/O多路复用?

  • I/O :網路 I/O
  • 多路 :多個網路連線
  • 重複使用:重複使用同一個執行緒。
  • IO多路復用其實就是一種同步IO模型,它實作了一個執行緒可以監視多個檔案句柄;一旦某個檔案句柄就緒,就能夠通知應用程式進行對應的讀寫操作;而沒有文件句柄就緒時,就會阻塞應用程序,交出cpu。

深入解析一下Redis為什麼這麼快?

多路I/O重複使用技術可以讓單一執行緒高效的處理多個連線請求,而Redis則使用epoll作為I/O多路復用技術的實現。而Redis本身的事件處理模型將epoll中的連接、讀寫、關閉都轉換為事件,不在網路I/O上浪費過多的時間。

虛擬記憶體機制

Redis直接自己建構了VM機制 ,不會像一般的系統會呼叫系統函數處理,會浪費一定的時間去移動和請求。

Redis的虛擬記憶體機制是啥呢?

虛擬記憶體機制就是暫時把不經常存取的資料(冷資料)從記憶體交換到磁碟中,從而騰出寶貴的記憶體空間用於其它需要存取的資料(熱數據)。透過VM功能可以實現冷熱資料分離,使熱資料仍在記憶體中、冷資料儲存到磁碟。這樣就可以避免因為記憶體不足而造成存取速度下降的問題。

更多程式相關知識,請造訪:程式設計影片! !

以上是深入解析一下Redis為什麼這麼快?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:掘金--捡田螺的小男孩。如有侵權,請聯絡admin@php.cn刪除