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

歡迎訪問 生活随笔!

生活随笔

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

数据库

将一个键值对添加入一个对象_细品Redis高性能数据结构之hash对象

發(fā)布時間:2024/9/19 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 将一个键值对添加入一个对象_细品Redis高性能数据结构之hash对象 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

背景

上一節(jié)講Redis的高性能字符串結構SDS,今天我們來看一下redis的hash對象。

Hash對象

簡介

  • redis的hash對象有兩種編碼(底層實現(xiàn))方式,字典編碼和壓縮列表編碼。在使用字典編碼的時候程序就是將hash表的key存為字典的鍵,hash的value作為字典的值,字典的鍵值都是用的是字符串類型。
  • 在哈希對象保存的所有鍵值對的鍵和值的字符串長度都小于 64 字節(jié)和哈希對象保存的鍵值對數(shù)量小于 512 個使用的是ziplist,不能滿足這個的使用的是hashtable(字典編碼)

深度理解

ZipList(壓縮列表)

  • redis 的壓縮列表是一塊連續(xù)的內(nèi)存空間,元素之間緊挨著存儲,沒有任何冗余空間
  • 源碼
  • struct ziplist<T> { int32 zlbytes; // 整個壓縮列表占用字節(jié)數(shù) int32 zltail_offset; // 最后一個元素距離壓縮列表起始位置的偏移量,用于快速定位到最后一個 節(jié)點 int16 zllength; // 元素個數(shù) T[] entries; // 元素內(nèi)容列表,挨個挨個緊湊存儲 int8 zlend; // 標志壓縮列表的結束,值恒為 0xFF }/** *entry對象源碼 */ struct entry { int<var> prevlen; // 前一個 entry 的字節(jié)長度 int<var> encoding; // 元素類型編碼 optional byte[] content; // 元素內(nèi)容 }
  • 壓縮列表是支持雙向遍歷,所以才會有zltail_offset這個字段的,可以進行快速定位到最后一個元素。然后倒序查找(O(1))
  • prevlen 表示的是前一個字段的長度,有人就有疑問了,為什么是前一個entry的長度,為什么不是自己的呢,其實他還有一個作用是在壓縮列表倒敘遍歷的時候,需要通過這個字段來快速定位到下一個元素的位置,由于他是一個連續(xù)的存儲空間,已經(jīng)知道當前元素的位置+這個空間地址就可以確定寫一個entry的位置。為什么會這樣呢?因為entry的大小是不一樣的。如果是一樣的話就可以根據(jù)下表進行行為(個人理解,有錯誤還請指出),且prevlen 是一個變長的整數(shù),redis的常規(guī)操作,將不同長度使用不同的數(shù)據(jù)類型。節(jié)省內(nèi)存
  • encoding的意思是元素的編碼類型,有了這個字段就可以決定元素內(nèi)容的設定,內(nèi)存大小的分配。防止內(nèi)存分配浪費的一種方式。具體內(nèi)容查看下面
  • 1、00xxxxxx 最大長度位 63 的短字符串,后面的 6 個位存儲字符串的位數(shù),剩余的字 節(jié)就是字符串的內(nèi)容。 2、01xxxxxx xxxxxxxx 中等長度的字符串,后面 14 個位來表示字符串的長度,剩余的 字節(jié)就是字符串的內(nèi)容。 3、10000000 aaaaaaaa bbbbbbbb cccccccc dddddddd 特大字符串,需要使用額外 4 個字節(jié) 來表示長度。第一個字節(jié)前綴是 10,剩余 6 位沒有使用,統(tǒng)一置為零。后面跟著字符串內(nèi) 容。不過這樣的大字符串是沒有機會使用的,壓縮列表通常只是用來存儲小數(shù)據(jù)的。 4、11000000 表示 int16,后跟兩個字節(jié)表示整數(shù)。 5、11010000 表示 int32,后跟四個字節(jié)表示整數(shù)。 6、11100000 表示 int64,后跟八個字節(jié)表示整數(shù)。 7、11110000 表示 int24,后跟三個字節(jié)表示整數(shù)。 8、11111110 表示 int8,后跟一個字節(jié)表示整數(shù)。 9、11111111 表示 ziplist 的結束,也就是 zlend 的值 0xFF。 10、1111xxxx 表示極小整數(shù),xxxx 的范圍只能是 (0001~1101), 也就是 1~13,因為 0000、1110、1111 都被占用了。讀取到的 value 需要將 xxxx 減 1,也就是整數(shù) 0~12 就是 最終的 value。
  • 之前有講到hash對像選用壓縮列表的兩個前提條件,其中之一是鍵值的大小都小于64,具體為什么小于64和簡=鍵值對小于512就不具體說了,可以結合一下SDS中的擴容方式思考一下,壓縮列表沒有冗余空間,在進行擴容的時候會出現(xiàn)頻繁擴容,再加上占用空間大了后進行copy數(shù)據(jù)就很浪費性能了。所以當數(shù)據(jù)量大了后,就選擇了另一種數(shù)據(jù)結構那就是hashtable(字典)
  • HashTable(字典)

    簡介

    • redis 的hashtable和java中的hashMap實現(xiàn)方式是類似的,都是通過數(shù)組和鏈表實現(xiàn)的。也就是key-value形式。當然它解決hash沖突的方式也是使用鏈地址法(解決hash沖突的幾種方法可以想一下),當不同的key創(chuàng)建出了相同的hash值時將vlue就放入鏈表上,如下圖。
    • 在細節(jié)方面和java中的hashMap差別還是很大的。列如擴容的過程,key值得hash算法等等。接下來我們根據(jù)源碼細細的品一品。

    • 官方給的解釋:字典(dictionary), 又名映射(map)或關聯(lián)數(shù)組(associative array), 是一種抽象數(shù)據(jù)結構, 由一集鍵值對(key-value pairs)組成, 各個鍵值對的鍵各不相同, 程序可以添加新的鍵值對到字典中, 或者基于鍵進行查找、更新或刪除等操作

    其字典的底層結構是使用的是redis 中dict。不僅是hash對象底層使用了dict,而且在redis全局也是使用的是key-vlue結構,也就是字典的形式,還有Zset的數(shù)據(jù)結構底層也是基于redis 中的dict結構。我們來看一下其源碼:

    // resdis 全局使用的字典結構 struct RedisDb { dict* dict; // all keys key=>value dict* expires; // all expired keys key=>long(timestamp) ...} // 有序集合的底層數(shù)據(jù)結構 struct zset { dict *dict; // all values value=>score zskiplist *zsl; }

    2. dict結構深度解析

    • 源碼:
    /** 字典** 每個字典使用兩個哈希表,用于實現(xiàn)漸進式 rehash*/ typedef struct dict {// 特定于類型的處理函數(shù)dictType *type;// 類型處理函數(shù)的私有數(shù)據(jù)void *privdata;// 哈希表(2 個)dictht ht[2];// 記錄 rehash 進度的標志,值為 -1 表示 rehash 未進行int rehashidx;// 當前正在運作的安全迭代器數(shù)量int iterators;} dict;
    • 可以類的成員變量中看到有兩個hashtable,通常情況下是一個有值一個沒有值。在壓縮列表中我們遇到的問題是在擴容方面存在性能問題,這兩個hashtable就是來解決擴容問題的。在擴容和縮容時進行漸進式搬遷,當搬遷結束的時候將舊的hashtable進行刪除,新的hashtable 取而代之。
    • 那我們來細細的研究一下hashtable,(Java中的hashtable是Java中hashMap的線程安全版本)。在這里的hashtable和java中的hashmap是類似的,解決hash沖突的方式通過分桶的方式。一維數(shù)組,二維鏈表。但是在擴容還是有一些區(qū)別的。
    struct dictEntry { void* key; void* val; dictEntry* next; // 鏈接下一個 entry } struct dictht { dictEntry** table; // 二維 long size; // 第一維數(shù)組的長度 long used; // hash 表中的元素個數(shù) ... }
    • 來看一下redis中hash是如何進行的
      1.大字典的擴容是非常耗時間的,需要重新申請新的數(shù)組,然后將舊的字典所有的鏈表中的元素重新掛接到新的數(shù)組下面,這個過程時間復雜度為O(n),作為單線程的redis怎么會把時間浪費在這里呢,。于是他就采用了漸進式處理的方式(說到漸進式是否能想到他漸進式批量根據(jù)key查詢呢scan 和 keys), rehash的過程點擊這里。其思想也就是我們上面所說的小步執(zhí)行。
    • 聯(lián)系一下Set結構也是通過字典實現(xiàn)的,只不是所有的value都是NULL,有沒有想到什么?Java中的hashSet是不是也和這個類似呢?。

    總結

  • hash對象有兩種底層實現(xiàn)方式,hashtable(字典) 和 ziplist(壓縮鏈表)
  • 壓縮鏈表由于是連續(xù)空間在剛開始數(shù)據(jù)量小的時候性能是顯著的,但是在數(shù)據(jù)量大的時候就會出現(xiàn)擴容慢的問題
  • 字典通過雙hahstable的方式,再加上漸進式hash的方式解決了壓縮列表的擴容的問題
  • redis 高性能數(shù)據(jù)結構我們可以看到他在很對細節(jié)的把握很多,如不同的數(shù)字大小選用不同的字段類型,不同的存儲方式采用不同的
  • 總結

    以上是生活随笔為你收集整理的将一个键值对添加入一个对象_细品Redis高性能数据结构之hash对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。