Redis:09---Hash對象


一、哈希對象簡介

  • 幾乎所有的編程語言都提供了哈希(hash)類型,它們的叫法可能是哈希、字典、關聯數組

  • 哈希又稱散列

  • 在Redis中,哈希類型是指鍵值本身又是一個鍵值對結構,形如value={{field1,value1},...{fieldN,valueN}},Redis鍵值對和哈希類型二者的關系可以下圖表示

  • 一些特點:

    • 存儲多個鍵值對之間的映射,并且鍵值對不允許重復

    • 在某一個固定的key中,其對應value中的field也不允許重復

    • 散列存儲的值既可以是字符串也可以是數字值

    • 用戶同樣可以對散列存儲的數字值執行自增操作或自減操作

    • 散列在很多方面是一個微縮版的Redis,不少字符串命令都有相應的散列版本

    • 熟悉文檔數據庫的讀者可以將散列看作是文檔數據庫里面的文檔,而熟悉關系數據庫的讀者可以將散列看作是關系數據庫里面的行。因為“文檔、行、散列”這三者都允許用戶同時訪問或修改一個或多個域

  • 注意:哈希類型中的映射關系叫作field-value,注意這里的value是指field對應的值,不是鍵對應的值,請注意value在不同上下文的作用。

二、命令

常用命令

  • hset:設置值。如果設置成功會返回1,反之會返回0

hset key field value

? ? ??hsetnx:它們的關系就像set和setnx命令一樣,只不過作用域由鍵變為field

? ? ? ?

  • hget:獲取值。如果鍵或field不存在,返回nil

hget key field

好,到這里會有人好奇,這里到底是怎么樣的結構,能不能直觀的看到這些記錄,還是我在前幾篇文章說到的Redis DeskTop Manager可以查看

  • hdel:刪除field

    • hdel會刪除一個或多個field,返回結果為成功刪除field的個數

    • 直到某一個key對應的field全部刪除完全之后,該哈希對象才會被刪除

hdel key field [field ...]

  • hlen:計算fileld個數

hlen key

  • hmget、hmset:批量獲取/設置field-value

hmget key field [field ...]
hmset key field value [field value ...]

  • hstrlen:計算value的字符串長度(需要Redis3.2以上)

hstrlen key field

其他命令

  • hincrby、hincrbyfloat:hincrby和hincrbyfloat,就像incrby和incrbyfloat命令一樣,但是它們的作用域是filed

  • hexists:判斷field是否存在。field存在返回1,不包含返回0

  • hkeys:獲取所有field

hkeys key

  • hvals:獲取所有值

hvals key
  • hgetall:獲取所有的field-value

hgetall key

  • 提示:在使用hgetall時,如果哈希元素個數比較多,會存在阻塞Redis的可能。 如果開發人員只需要獲取部分field,可以使用hmget,如果一定要獲取全部 field-value,可以使用hscan命令,該命令會漸進式遍歷哈希類型.

  • 下圖給出了哈希類型命令的時間復雜度:

三、內部編碼

  • 哈希類型的內部編碼有兩種:

    • ziplist(壓縮列表):當哈希類型元素個數小于hash-max-ziplist-entries 配置(默認512個)、同時所有值都小于hash-max-ziplist-value配置(默認64 字節)時,Redis會使用ziplist作為哈希的內部實現,ziplist使用更加緊湊的 結構實現多個元素的連續存儲,所以在節省內存方面比hashtable更加優秀

    • hashtable(哈希表):當哈希類型無法滿足ziplist的條件時,Redis會使 用hashtable作為哈希的內部實現,因為此時ziplist的讀寫效率會下降,而 hashtable的讀寫時間復雜度為O(1)

演示說明

  • 當field個數比較少且沒有大的value時,內部編碼為ziplist:

  • 當有value大于64字節,內部編碼會由ziplist變為hashtable:

  • 當field個數超過512,內部編碼也會由ziplist變為hashtable

四、字符串和散列的比較與選擇

散列的優點

  • 散列的最大優勢,只需要在數據庫里面創建一個鍵,就可以把任意多的字段和值存儲到散列里面

字符串的優點

  • 雖然散列鍵命令和字符串鍵命令在部分功能上有重合的地方,但是字符串鍵命令提供的操作比散列鍵命令更為豐富。比如,字符串能夠使用 SETRANGE 命令和 GETRANGE 命令設置或者讀取字符 串值的其中一部分,或者使用 APPEND 命令將新內容追加到字符串值的末尾,而散列鍵并不支持 這些操作

  • 再比如我們要設置鍵過期時間,鍵過期時間是針對整個鍵的,用戶無法為散列中的不同字段設置不 同的過期時間,所以當一個散列鍵過期的時候,他包含的所有字段和值都會被刪除。與此相反,如 果用戶使用字符串鍵存儲信息項,就不會遇到這樣的問題——用戶可以為每個字符串鍵分別設置不 同的過期時間,讓它們根據實際的需要自動被刪除

字符串和散列的選擇

  • 使用場景對比:

    • 如果程序需要為單個數據項單獨設置過期的時間,那么使用字符串鍵。

    • 如果程序需要對數據項執行諸如 SETRANGE、GETRANGE 或者 APPEND 等操作,那么優 先考慮使用字符串鍵。當然,用戶也可以選擇把數據存儲在散列中,然后將類似 SETRANG E、GETRANGE 這樣的操作交給客戶端執行

    • 如果程序需要存儲的數據項比較多,并且你希望盡可能地減少存儲數據所需的內存,就應該優 先考慮使用散列鍵

    • 如果多個數據項在邏輯上屬于同一組或者同一類,那么應該優先考慮使用散列鍵

五、使用場景

短網址生成程序

  • 此時我們可以根據該短鏈接查詢到具體的源網址,并記錄點擊次數

存儲信息

  • 下圖為關系型數據表記錄的兩條用戶信息,用戶的屬性作為表的列, 每條用戶信息作為行

  • 如果將其用哈希類型存儲,如下圖所示:

  • 相比于使用字符串序列化緩存用戶信息,哈希類型變得更加直觀,并且在更新操作上會更加便捷。可以將每個用戶的id定義為鍵后綴,多對fieldvalue對應每個用戶的屬性,類似如下偽代碼:

UserInfo?getUserInfo(long?id){//?用戶id作為key后綴userRedisKey?=?"user:info:"?+?id;//?使用hgetall獲取所有用戶信息映射關系userInfoMap?=?redis.hgetAll(userRedisKey);UserInfo?userInfo;if?(userInfoMap?!=?null)?{//?將映射關系轉換為UserInfouserInfo?=?transferMapToUserInfo(userInfoMap);}?else?{//?從MySQL中獲取用戶信息userInfo?=?mysql.get(id);//?將userInfo變為映射關系使用hmset保存到Redis中redis.hmset(userRedisKey,?transferUserInfoToMap(userInfo));//?添加過期時間redis.expire(userRedisKey,?3600);}return?userInfo;}
  • 但是需要注意的是哈希類型和關系型數據庫有兩點不同之處:

    • 哈希類型是稀疏的,而關系型數據庫是完全結構化的,例如哈希類型 每個鍵可以有不同的field,而關系型數據庫一旦添加新的列,所有行都要為 其設置值(即使為NULL),如下圖所示

    • 關系型數據庫可以做復雜的關系查詢,而Redis去模擬關系型復雜查詢 開發困難,維護成本高

三種方案

  • 開發人員需要將兩者的特點搞清楚,才能在適合的場景使用適合的技術。到目前為止,我們已經能夠用三種方法緩存用戶信息,下面給出三種方案的實現方法和優缺點分析

  • ①原生字符串類型:每個屬性一個鍵

    • 優點:簡單直觀,每個屬性都支持更新操作

    • 缺點:占用過多的鍵,內存占用量較大,同時用戶信息內聚性比較差, 所以此種方案一般不會在生產環境使用

set user:1:name tom
set user:1:age 23
set user:1:city beijin
  • ②序列化字符串類型:將用戶信息序列化后用一個鍵保存。

    • 優點:簡化編程,如果合理的使用序列化可以提高內存的使用效率

    • 缺點:序列化和反序列化有一定的開銷,同時每次更新屬性都需要把全 部數據取出進行反序列化,更新后再序列化到Redis中

set user:1 serialize(userInfo)
  • ③哈希類型:每個用戶屬性使用一對field-value,但是只用一個鍵保存

    • 優點:簡單直觀,如果使用合理可以減少內存空間的使用

    • 缺點:要控制哈希在ziplist和hashtable兩種內部編碼的轉換,hashtable會消耗更多內存

hmset user:1 name tomage 23 city beijing

不過對于我而言,我經常用到的是string,方便,快捷,一般是存儲一些玩家的登錄緩存信息,或者一些全服跨服戰斗的相關數據

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444459.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444459.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444459.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode329. 矩陣中的最長遞增路徑

給定一個整數矩陣,找出最長遞增路徑的長度。 對于每個單元格,你可以往上,下,左,右四個方向移動。 你不能在對角線方向上移動或移動到邊界外(即不允許環繞)。 示例 1: 輸入: nums [ [9,9,…

Query Ajax 實例 ($.ajax、$.post、$.get)

Jquery在異步提交方面封裝的很好,直接用AJAX非常麻煩,Jquery大大簡化了我們的操作,不用考慮瀏覽器的詫異了。 推薦一篇不錯的jQuery Ajax 實例文章,忘記了可以去看看,地址為:http://www.cnblogs.com/yeer/a…

C++:18---const關鍵字(附常量指針、指針常量、常量指針常量)

一、const變量的一些基本特點 ①const修飾的變量不能被修改const int a=10; a=20;//錯誤②因為const修飾的變量不能被修改,所以必須被初始化int a=10; const int b=a; //正確 const int c=10; //正確③const修飾的變量可以賦值給其他值const int a=10; int b=a;//正確④可以有…

C:01---數據類型與ASCII

一、整型 int 取值范圍:-32768~32767unsigned int 取值范圍:0~65535short /short int 取值范圍:比int小unsigned short 無符號短整型long 長整型定義時,后面加上l或L有符號與無符號數: unsigned:無符號數,一般用來表示數據signed:有符號數,一般用來表示數字整型占…

leetcode330. 按要求補齊數組 頂級難度玄學貪心

給定一個已排序的正整數數組 nums,和一個正整數 n 。從 [1, n] 區間內選取任意個數字補充到 nums 中,使得 [1, n] 區間內的任何數字都可以用 nums 中某幾個數字的和來表示。請輸出滿足上述要求的最少需要補充的數字個數。 示例 1: 輸入: nums [1,3], …

C:02---scanf、printf

一、printf 控制符 ①精度控制:輸入小數點后m位(%.mf)。右對齊5位,保留小數點后m位(%d.mf)%f、%lf默認輸出6位小數②寬度:%md(打印m為,右對齊,多出m位照常打印)。%-md(打印m位,左對齊,多出m位照常打印)③長度:h表示短(打印短整型short:%hd),l表示長(打印長…

C++:20---成員變量初始化方式

成員變量初始化有三種方式: 在構造函數體內賦值初始化在自定義的公有函數體中賦值初始化(一般用于成員變量的初始化)在構造函數的成員初始化列表初始化一、構造函數體內初始化 說明:在構造函數體內的初始化方式,本質是是為成員變量賦值,而不是真正意義上的初始化,這點要…

leetcode339. 嵌套列表權重和

給定一個嵌套的整數列表,請返回該列表按深度加權后所有整數的總和。 每個元素要么是整數,要么是列表。同時,列表中元素同樣也可以是整數或者是另一個列表。 示例 1: 輸入: [[1,1],2,[1,1]] 輸出: 10 解釋: 因為列表中有四個深度為 2 的 1…

C++:19---this指針

一、this指針介紹 概念:this指針是成員函數的一個隱式參數,在類中本質上就是對象的指針(常量指針)特點:在成員函數中可通過this指針區別成員變量與形參變量this可以顯式調用示例代碼:class Cperson{private:int age;float height;public:void InitPerson(int age,float hei…

leetcode346. 數據流中的移動平均值

給定一個整數數據流和一個窗口大小,根據該滑動窗口的大小,計算其所有整數的移動平均值。 示例: MovingAverage m new MovingAverage(3); m.next(1) 1 m.next(10) (1 10) / 2 m.next(3) (1 10 3) / 3 m.next(5) (10 3 5) / 3 思路&#xff1…

(二十)TCPIP面試寶典-進入大廠必備總結(中)

TCP 作為傳輸層的協議,是一個IT工程師素養的體現,也是面試中經常被問到的知識點。在此,我將 TCP 核心的一些問題梳理了一下,希望能幫到各位。 實際上這篇文章相當于是復習之前的網絡基礎部分。只不過這篇文章的提問方式更靈活,也是讓讀者們懂得變通,更熟悉TCP。 前兩篇文…

leetcode263. 丑數

編寫一個程序判斷給定的數是否為丑數。 丑數就是只包含質因數 2, 3, 5 的正整數。 示例 1: 輸入: 6 輸出: true 解釋: 6 2 3 示例 2: 輸入: 8 輸出: true 解釋: 8 2 2 2 示例 3: 輸入: 14 輸出: false 解釋: 14 不是丑數,因為它包含了另外一個質因數 7。…

(二十一)TCPIP面試寶典-進入大廠必備總結(下)

TCP 作為傳輸層的協議,是一個IT工程師素養的體現,也是面試中經常被問到的知識點。在此,我將 TCP 核心的一些問題梳理了一下,希望能幫到各位。 實際上這篇文章相當于是復習之前的網絡基礎部分。只不過這篇文章的提問方式更靈活,也是讓讀者們懂得變通,更熟悉TCP。 上一篇文…

C++:23 再議const的用法(下)

上一篇文章折騰了一波粉絲,那么這一篇文章稍微溫柔一些。 我主要開始說如何正確使用const 1.不能將const 修飾的任何對象、引用和指針作為賦值表達式的左值。 const int cx=100; const int & rcx=cx; const int * pcx=&cx; cx=200; //error rcx=200; //error *pcx=200…

C++:22 再議const的作用(上)

我在C++:18篇里說過const的用法,這里我有必要再提升進階下const的理解。 因為你可能只知道他是怎么用的,但是他為什么這樣用,其他用法呢? 首先回顧下const有什么主要的作用? (1)可以定義const常量,具有不可變性。 (2)便于進行類型檢查,使編譯器對處理內容有更多了解…

leetcode57. 插入區間

給出一個無重疊的 ,按照區間起始端點排序的區間列表。 在列表中插入一個新的區間,你需要確保列表中的區間仍然有序且不重疊(如果有必要的話,可以合并區間)。 示例 1: 輸入: intervals [[1,3],[6,9]], newInterval …

C:03---運算符優先級

二話不說先看運算符的優先級表: 一、逗號運算符 格式:整個逗號表達式的值返回的結果是最后一個表達式的值使用起來,最好加上括號來返回最后一個表達式的值。否則逗號表達式的意義將失效(見下面演示案例)(表達式1, 表達式2, 表達式3....); #include <stdio.h> int ma…

C++: 21---引用和指針

一般說到誰和誰怎么樣,要么說兩者的相似點,要么兩者的區別,這里我們也要說二者的區別和聯系,同時,也不僅僅是區別和聯系這么簡單,因為你可能會發現在變量賦值,函數傳參這兩點還是有很多值得品一品的。 最直觀的賦值方面的區別 首先我們先說二者的區別和聯系。 (1)指針…

Oracle數據庫Date類型查詢問題(

淺談Oracle數據庫Date類型查詢問題用過Oracle數據庫的朋友應該知道&#xff0c;Oracle數據庫在以Date類型為查詢條件時存在一個小小的BUG&#xff0c;如&#xff1a;select * from tableName where createDate > to_date(2007-01-01,yyyy-mm-dd) and createDate < to_dat…

(二十二)深入淺出TCPIP之實戰篇—用c++開發一個http服務器

在當前的網絡編程專欄前十幾篇文章里&#xff0c;我已經說明了TCPIP常用的一些原理&#xff0c;那么接下來我將逐步進入到實戰編程階段&#xff1a;本篇文章我將帶大家用C做一個http服務器。既然想實現一個http服務器&#xff0c;首先必須要熟悉的就是http協議知識&#xff0c;…