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

歡迎訪問 生活随笔!

生活随笔

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

数据库

Redis实现之整数集合

發布時間:2023/12/2 数据库 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis实现之整数集合 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

整數集合

整數集合(insert)是集合鍵的底層實現之一,當一個集合只包含整數值元素,并且這個集合的元素數量不多時,Redis就會使用整數集合作為集合鍵的底層實現。舉個栗子,如果我們創建一個只包含五個元素的集合鍵,并且集合中的所有元素都是整數值,那么這個集合鍵的底層實現就會是整數集合:

127.0.0.1:6379> SADD numbers 1 3 5 7 9 (integer) 5 127.0.0.1:6379> SMEMBERS numbers 1) "1" 2) "3" 3) "5" 4) "7" 5) "9" 127.0.0.1:6379> OBJECT ENCODING numbers "intset"

    

整數集合的實現

整數集合(insert)是Redis用于保存整數值的集合抽象數據結構,它可以保存類型為int16_t、int32_t或者int64_t的整數值,并且保證集合中不會出現重復元素。

intset.h

typedef struct intset {//編碼方式uint32_t encoding;//集合包含的元素數量uint32_t length;//保存元素的數組int8_t contents[]; } intset;

  

contents數組是整數集合的底層實現:整數集合的每個元素都是contents數組中的一個數組項,各個項在數組中按值的大小從小到大有序地排列,并且數組中不包含任何重復項。length屬性記錄了整數集合包含的元素數量,也即是contents數組的長度。雖然intset結構將contents屬性聲明為int8_t類型的數組,但實際上contents數組并不保存任何int8_t類型的值,contents數組的真正類型取決于encoding屬性的值:

  • 如果encoding屬性的值為為INTSET_ENC_INT16,那么contents就是一個int16_t類型的數組了,數組里的每個項都是一個int16_t類型的整數值(最小值為-32 768,最大值為32 767)
  • 如果encoding屬性的值為INTSET_ENC_INT32,那么contents就是一個int32_t類型的數組了,數組里的每個項都是一個int32_t類型的整數值(最小值為-2 147 483 648,最大值為2 147 483 647)
  • 如果encoding屬性的值為INTSET_ENC_INT64,那么contents就是一個int64_t類型的數組了,數組里的每個項都是一個int64_t類型的整數值(最小值為-9 223 372 036 854 775 808,最大值為9 223 372 036 854 775 807)。

圖1-1展示了一個整數集合示例:

圖1-1? ?一個包含五個int6_t類型整數值的整數集合

  • 如果encoding屬性的值為為INTSET_ENC_INT16,表示整數集合的底層實現為int16_t類型的數組,而集合保存的都是int16_t類型的整數值
  • length屬性為5,表示整數集合包含5個元素
  • contents數組按從小到大的順序保存著集合中的五個元素
  • 因為每個集合元素都是int16_t類型的整數值,所以contents數組的大小等于sizeof(int16_t)*5=16*5=80位

圖1-2展示了另一個整數集合示例:

圖1-2? ?一個包含四個int16_t類型整數值的整數集合

  • encoding屬性的值為INTSET_ENC_INT64,表示整數集合的底層實現為int64_t類型的數組,而數組中保存的都是int64_t類型的整數值
  • length屬性的值為4,表示整數集合包含四個元素
  • contents數組按從小到大的順序保存著集合中的四個元素
  • 因為每個集合元素都是int64_t類型的整數值,所以contents數組的大小為sizeof(int64_t)*4=64*4=256位

雖然contents數組保存的四個整數值中,只有-2 675 256 175 807 981 027是真正需要用int64_t類型來保存的,而其他的1、3、5三個值都可以用int16_t類型來保存,不過根據整數集合的升級規則,當向一個底層為int16_t數組的整數集合添加一個int64_t類型的整數值時,整數集合已有的所有元素都會被轉換成int64_t類型,所以contents數組保存的四個整數值都是int64_t類型的,不僅僅是?-2 675 256 175 807 981 027

升級

每當我們要將一個新元素添加到整數集合里面,并且新元素的類型比整數集合現有所有元素的類型要長時,整數集合需要先進行升級(upgrade),然后才能將新元素添加到整數集合里面。升級整數集合并添加新元素共分為三步進行:

  • ?根據新元素的類型,擴展整數集合底層數組對的空間大小,并為新元素分配空間
  • 將底層數組現有的所有元素都轉換成與新元素相同的類型,并將類型轉換后的元素放到正確的位置上,而且在放置元素的過程中,需要繼續維持底層數組的有序性質不變
  • 將新元素添加到底層數組里面
  • 舉個栗子,假設現在有一個INTSET_ENC_INT16編碼的整數集合,集合中包含三個int16_t類型的元素,如圖1-3所示

    圖1-3? ?一個包含三個int16_t類型的元素的整數集合

    因為每個元素都占用16位空間,所以整數集合底層數組的大小為3*16=48位,圖1-4展示了整數集合的三個元素在這48位里的位置

    現在,假設我們要將類型為int32_t的整數值65 535添加到整數集合里面,因為65 535的類型int32_t比整數集合當前所有元素要長,所以在將65 535添加到整數集合之前,程序需要先對整數集合進行升級。升級首先要做的是,根據新類型的長度,以及集合元素的數量(包括要添加的新元素在內),對底層數組進行空間重分配

    整數集合目前有三個元素,再加上新元素65 535,整數集合需要分配四個元素的空間,因為每個int32_t整數值需要占用32位空間,所以在空間重分配之后,底層數組的大小將是32*4=128位,如圖1-5所示。雖然程序對底層數組進行了空間重分配,但數組原有的三個元素1、2、3仍然是int16_t類型,這些元素還保存在數組的前48位里面,所以程序接下來要做的就是將這三個元素轉換成int32_t類型,并將轉換后的元素放置到正確的位上面,而且在放置元素的過程中,需要維持底層數組的有序性質不變

    ?

    圖1-5? ?進行空間重分配之后的數組

    首先,因為元素3在1、2、3、65535四個元素中排名第三,所以它將被移動到contents數組的索引2位置上,也即是數組64位至95位的空間內,如圖1-6所示

    圖1-6? ?對元素3進行類型轉換,并保存在適當的位置上

    接著,因為元素2在1、2、3、65535四個元素中排名第二,所以它將被移動到contents元素的索引1的位置上,也即是數組的32位至63位的空間內,如圖1-7

    ?

    圖1-7? ?對元素2進行類型轉換,并保存在適當的位上

    之后,因為元素1在1、2、3、65536四個元素中排名第一,所以它將被移動到contents數組的索引0位置上,即數組的0位至31位空間內,如圖1-8所示

    圖1-8? ?對元素1進行類型轉換,并保存在適當的位置上

    然后,因為元素65535在1、2、3、65535四個元素中排名第四,所以它將被添加到contents數組的索引3位置上,也即時數組的96位至127位的空間內,如圖1-9所示

    如圖1-9? ?添加65535到數組

    最后,程序將整數集合encoding屬性的值從INTSET_ENC_INT16改為INTSET_ENC_INT32,并將length屬性的值從3改為4,設置完成之后的整數集合如圖1-10所示

    ?

    圖1-10? ?完成添加操作之后的整數集合

    因為每次向整數集合添加新元素都可能會引起升級,而每次升級都需要對底層數組中已有的元素進行類型轉換,所以向整數集合添加新元素的時間復雜度為O(N)。其他類型的升級操作,比如從INTSET_ENC_INT16編碼升級為INTSET_ENC_INT64編碼,或從INTSET_ENC_INT32編碼升級為INTSET_ENC_INT64編碼,升級的過程都和上面展示的升級過程類似

    升級的好處

    整數集合的升級策略有兩個好處,一個是提升整數集合的靈活性,另一個是盡可能地節約內存

    提升靈活性

    因為C語言是靜態類型語言,為了避免類型錯誤,我們通常不會將兩種不同類型的值放在同一數據結構里面。例如,我們一般只用int16_t類型的數組來保存int16_t類型的值,只使用int32_t類型的數組來保存int32_t類型的值,諸如此類。但是,因為整數集合可以通過自動升級底層數組來適應新元素,所以我們可以隨意地將int16_t、int32_t或int64_t類型的整數添加到集合中,而不必擔心出現類型錯誤

    節約內存

    要讓一個數組可以同時保存int16_t、int32_t、int64_t三種類型的值,最簡單的做法就是直接使用int64_t類型的數組作為整數集合的底層實現。不過這樣一來,部分可以用int16_t、int32_t來存儲的值因使用int64_t類型來存儲,會造成內存的浪費。而整數集合現在的做法既可以讓集合能同時保存三種不同類型的值,又可以確保升級操作只會在有需要的時候進行,這可以盡量節省內存

    降級

    整數集合不支持降級操作,一旦對數組進行了升級,編碼就會一直保持升級后的狀態。舉個栗子,對于圖1-11所示的整數集合來說,即使我們將集合里唯一一個真正需要使用int64_t類型來保存的元素4 294 967 295刪除了,整數集合的編碼仍然會維持使用int64_t,底層數組也仍然會使int64_t類型,如圖1-12所示

    圖1-11? ?數組編碼為INTSET_ENC_INT64的整數集合

    圖1-12? ?刪除4 294 967 295的整數集合

    整數集合API

    表1-1列出了整數集合的操作API

    表1-1? ?整數集合API
    函數作用時間復雜度
    intsetNew(void)創建一個新的整數集合O(1)
    intsetAdd(intset *is, int64_t value, uint8_t *success)將給定元素添加到整數集合里面O(N)
    intsetRemove(intset *is, int64_t value, int *success)從整數集合中移除給定元素O(N)
    intsetFind(intset *is, int64_t value)檢查給定值是否存在于集合因為底層數組有序,查找可以通過二分查找法來進行,所以復雜度為O(logN)
    intsetRandom(intset *is)從整數集合中隨機返回一個元素O(1)
    intsetGet(intset *is, uint32_t pos, int64_t *value)取出底層數組在給定索引上的元素O(1)
    intsetLen(intset *is)返回整數集合包含的元素個數O(1)
    intsetBlobLen(intset *is)返回整數集合占用的內存字節數O(1)

    轉載于:https://www.cnblogs.com/beiluowuzheng/p/9734227.html

    總結

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

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