日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis的设计与实现之对象

發布時間:2024/4/18 数据库 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis的设计与实现之对象 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Redis中對象概述

在前面說的是一些Redis的底層數據結構,那些數據結構是對象實現的基礎。在Reids的對象系統中有五種類型的對象。

  • 字符串對象
  • 列表對象
  • 哈希對象
  • 集合對象
  • 有序集合對象

在Java中也有對象,也可以將這些對象看作為相對獨立的個體。還有一個和Java中很相似的地方就是Redis對象系統實現了內存回收機制(基于引用計數技術),當程序不需要的話就會被自動釋放。Redis還可以在引用計數技術下,在適當的條件,可以讓多個數據庫鍵共享同一個對象來節約資源。在Redis對象中還有比較好的一點是,Redis對象帶有訪問事件的記錄信息(可以用作我們后邊將會講到的計算空轉)。

在了解具體對象之前,先來了解以下對象的主要數據結構。

對象的數據結構

typedef struct redisObject{//類型unsigned type;//編碼unsigned encoding;//指向底層數據結構的指針void *ptr;.... }

比如我們在數據庫加入一個鍵值對,在這個過程中至少會創建兩個對象。

  • type:記錄著這個對象是什么類型的對象。也就是開篇中提到的五種對象之一。
  • encoding:在Redis中有一些編碼常量被用來表示不同對象類型。基于任意一種type,type和encoding之間的一些組合。這些后邊也會看到。比如:set類型可以和intset組合就是使用整數集合實現的集合對象。
  • ptr:指向對象實現底層的數據結構,這些數據結構由encoding來決定

type和encoding的區別是:type展示的是最外層的形態,encoding掌握的是底層的原子實現。底層由encoding來掌控可以增加Redis的靈活性,因為如果數據結構的決定因素由多個組成,那就很難分配。例如:在列表對象包含的元素比較少的時候,它的encoding是壓縮列表,當它大的時候,encoding就會變為雙端鏈表。這大大提高的靈活性。

深入了解Redis的五大對象

字符串對象

字符串對象的編碼可以是int,raw,embstr。

  • int如果字符串對象里保存的是整數。

  • 這個整數可以用long類型的來表示,那么ptr的返回值會從void變為long,并將字符串對象的編碼設置為int。

  • 如果超過long的長度就是embstr和raw類型。

  • embstr:當保存的字符串長度小于等于39字節,那字符串對象將使用embstr編碼的方式來保存。

  • embstr是專門用來保存短字符串的優化編碼方式,它采用的是使用一塊連續的內存空間依次保存redisObject和sdshdr。而raw就不用,它要連續的分配兩次內存空間分別給redisobject和sdshdr。

  • 釋放embstr因此也只需要釋放一次資源。

  • 字符串對象的所有數據保存在連續的單元里,能夠更好的利用緩存帶來的優勢。

  • raw:當字符串長度大于39,那字符串對象使用SDS來保存這個對象,并將編碼設置為raw。

  • 在前面對SDS已經了解它的底層了,SDS對于embstr的優勢就是它可以存儲長度很長的字符串,如果embstr存儲了超過它極限的字符串它就得重新分配資源轉為SDS。

字符串對象編碼的轉換

  • 對于int和embstr,如果滿足轉換條件就會轉為raw。
  • 對int類型執行一些命令(如append一個字符或者字符串)就從int變為raw。
  • 對embstr執行任何的修改命令時,都會從embstr變為raw。

列表對象

列表對象的編碼可以是ziplist,linkedlist。

  • ziplist:使用壓縮列表作為底層實現。
  • 列表對象保存的所有字符串長度都小于64字節。
  • 列表對象保存的元素數量小于512個。
  • linkedlist:使用雙向鏈表作為底層實現,每一個節點都是一個字符串對象。
  • 不能滿足ziplist的就使用linklist作為對象的編碼。

例如對一個短的字符串擴充為長度字符串,或者增加節點數量都有可能使列表對象的編碼方式發生變化。

ziplist的極限條件是可以變的,可以在配置文件中設置。

哈希對象

哈希對象的編碼可以是ziplist,hashtable。

  • ziplist:
  • 當有新鍵值對進來的時候,先把鍵節點推入壓縮列表表尾,然后再把值節點推入哈希列表表尾。鍵的后邊緊跟著值。先進的在列表的表頭,后進的在表尾。
  • 列表對象保存的所有字符串長度都小于64字節。
  • 列表對象保存的元素數量小于512個。
  • hashlist:使用字典作為底層實現。
  • 不能滿足ziplist就轉為hashlist。

ziplist的條件都一樣,當然也可以和上邊一樣在配置文件中修改。

集合對象

集合對象可以是intset,hashtable。

  • intset:使用整數集合作為底層實現。
  • 集合中保存的必須都是整數。
  • 集合對象保存的元素小于等于512。
  • hashtable:使用字典作為底層實現,每一個鍵都是一個字符串對象,值都為空。也就是將所有的鍵作為一個字符串集合。
  • 不能滿足instset條件的都使用字典作為底層實現。

集合對象保存的元素數量可以更改。

有序集合對象

有序集合的編碼可以是ziplist,skiplist。

  • ziplist:使用壓縮列表作為底層實現。每個集合元素緊挨在一起,第一個是元素成員(member)緊跟其后的是分值(score)。

  • 壓縮列表按分值大小進行排序,分值小的靠近表頭。

  • 有序集合保存的元素數量小于128

  • 有序集合保存的所有元素長度都小于64字節。

  • skiplist:使用zset作為底層實現。zset的結構體中同時包含一個字典和一個跳躍表。主要用于排序。

  • 在zset中,zsl按照分值由大到小保存了所有的元素,每個跳躍表節點都保存了一個元素(跳躍表節點的obj保存的是元素成員,score保存的分值)。

  • 在zset中dict字典為有序集合創建了一個從成員到分值的映射,字典中每一個鍵值對都保存一個元素。主要用于查找。

  • 如果不能滿足ziplist的所有要求,那就用zset作為底層實現的數據結構。

typedef struct zset{//跳躍表zskiplist *zsl;//字典dict *dict;}

設計者很精巧的設計了這一方案,各取所長,用指針來共享相同元素和分值,所以這樣不會產生重復元素,并不會浪費太多的空間。

前面介紹完Redis的五大對象,那如果對不同對象進行不同的命令操作會怎么樣?

類型的檢測

可以籠統的把Redis中的命令分為兩種

  • 通用型命令:如del,rename,type,object等等
  • 對象特有命令
  • 如下操作

    • set,get,append,strlen等只能對字符串鍵執行

    • hdel,hset,hget,hlen等只能對哈希鍵執行,用別的操作也不能操作哈希鍵。如下圖

    • rpush,lpop,linsert,llen,lrange等只能對列表鍵使用

    • sadd,spop,sinsert,scard等命名只能對集合鍵使用

    • zadd,zcard,zrank,zscore等只能對有序集合鍵使用

    類型檢測的實現流程

  • 執行命令前,服務器檢測輸入數據庫的鍵和值是否符合類型,是的話就跳到2,不是的話就跳到3
  • 執行命令
  • 服務器拒絕執行并返回錯誤
  • 多態命令

    什么是多態命令?

    多態命令就是Reids除了根據值的對象類型來判斷指令的能否執行之外,也還會根據值對象的encoding來選擇命令是否可執行。

    多態命令的執行流程

  • 客戶端發送指令
  • 服務器檢查輸入數據庫鍵的值是否相符,相符的話就執行3,不相符的話就返回一個錯誤
  • 根據這個命令所對應的實現方式,調用各自的方法。
  • 打個比方就是這個命令相當于是一個總管,給下面的人要收他們中某一個人這一個月的銷售報告,不歸他管的人就走開了,歸他管的人就各自找他們的銷售報告,相當于總管這個命令對他的下屬都有效。

    內存回收

    前面說到過Redis使用引用計數技術和LRU(最近最久未使用算法)實現的垃圾回收。

    • 當創建一個新的對象時,計數的值會被初始化為1
    • 當對象被一個新的程序使用,計數的值+1
    • 當對象不再被程序使用時,計數的值會-1
    • 當值變為0時,就會被釋放掉

    雖然,原理很簡單,但是實現的過程也是挺麻煩的呢。比如怎么能認為程序不會再使用這個對象了呢?

    算法的實現采用的是HashMap+Double LinkedList。

    詳情還得看大佬寫的文章https://www.cnblogs.com/WJ5888/p/4371647.html

    對象共享

    什么是對象共享?

    就是一段內存存的值,被多個對象使用。

    Redis中對象共享的步驟

  • 讓數據庫的值指針指到已有的值對象
  • 將被共享的值的引用計數器+1
  • 這個也正好和垃圾回收對接上了。
    比如下邊這個例子: refcount是查看引用計數器的值,可以看到在共享后,refcount就+1,刪除后refcount就-1.

    這些共享不止字符串鍵可以使用,那些嵌套了字符串鍵的其他對象也可以使用。但是Redis中不共享字符串的對象,字符串比對需要的時間復雜度高,而且如果這個對象內含多個字符串對象那驗證的事件復雜度將會是O(ne2)

    對象的空轉時長

    還記得在前面在說對象的時候說的lru屬性,它是用來記錄最后一次被命令訪問的事件。這也就可也順理成章的和前面說的LRU垃圾回收相應了。可以通過object idletime 來查看空轉的時間(now-lastTime),然后根據空轉的時長來判斷是否進行回收,前提是服務器打開maxmemory選項,超過上限時,會將空轉時間長的進行回收。

    總結

    以上是生活随笔為你收集整理的Redis的设计与实现之对象的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。