欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis數(shù)據(jù)類型超詳細(xì)講解分析

 更新時(shí)間:2024年12月27日 11:09:42   作者:愛(ài)吃牛肉的大老虎  
Redis是一個(gè)開(kāi)源的內(nèi)存數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)系統(tǒng),可以用作數(shù)據(jù)庫(kù)、緩存和消息中間件,本文詳細(xì)介紹了Redis的各個(gè)數(shù)據(jù)類型、內(nèi)部編碼以及一些高級(jí)功能,如Geo、HyperLogLog和Stream,需要的朋友可以參考下

1 Redis

Redis官網(wǎng)英文版:https://redis.io/

Redis官網(wǎng)中文版:http://redis.cn/

1.1 概述

Redis是以key-value存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)服務(wù)器,所有的key(鍵)是字符串,而value可以包含:

  • 字符串類型(string):最基本的數(shù)據(jù)類型,二進(jìn)制安全的字符串,最大512M
  • 列表類型(list):按照添加順序保持順序的字符串列表
  • 集合類型(set):無(wú)序的字符串集合,不存在重復(fù)的元素
  • 有序集合類型(sorted setZset):已排序的字符串集合
  • 散列類型(hash):key-value對(duì)的一種集合
  • 位操作(bitmap):更細(xì)化的一種操作,以bit為單位。
  • 基數(shù)統(tǒng)計(jì)(hyperloglog):基于概率的數(shù)據(jù)結(jié)構(gòu),2.8.9新增
  • 地理位置(Geo):地理位置信息儲(chǔ)存起來(lái), 并對(duì)這些信息進(jìn)行操作 3.2新增
  • 流(Stream) 5.0新增

1.2 查看內(nèi)部編碼

Redis查看內(nèi)部編碼使用OBJECT ENCODING命令

該命令用來(lái)返回?cái)?shù)據(jù)結(jié)構(gòu)的內(nèi)部編碼

對(duì)象所使用的底層數(shù)據(jù)結(jié)構(gòu)編碼常量object encoding 命令輸出
整數(shù)REDIS_ENCODING_INT“int”
embstr編碼簡(jiǎn)單動(dòng)態(tài)字符串(SDS)REDIS_ENCODING_EMBSTR“embstr”
簡(jiǎn)單動(dòng)態(tài)字符串REDIS_ENCODING_RAW“raw”
字典REDIS_ENCODING_HT“hashtable”
雙端鏈表REDIS_ENCODING_LINKEDLIST“linkedlist”
壓縮列表REDIS_ENCODING_ZIPLiST“ziplist”
整數(shù)集合REDIS_ENCODING_INTSET“intset”
跳躍表和字典REDIS_ENCODING_SKIPLIST“skiplist”

1.3 String字符串

1.3.1 簡(jiǎn)介

String是redis中最基本的數(shù)據(jù)類型,一個(gè)key對(duì)應(yīng)一個(gè)value。

redis的key和string類型value限制均為512MB雖然Key的大小上限為512M,但是一般建議key的大小不要超過(guò)1KB,這樣既可以節(jié)約存儲(chǔ)空間,又有利于Redis進(jìn)行檢索

1.3.2 應(yīng)用常景

String類型是二進(jìn)制安全的,意思是 redis 的 string 可以包含任何數(shù)據(jù)。如數(shù)字,字符串,jpg圖片或者序列化的對(duì)象。字符串類型實(shí)際上可以是字符串(簡(jiǎn)單的字符串、復(fù)雜的字符串(xml、json)、數(shù)字(整數(shù)、浮點(diǎn)數(shù))、二進(jìn)制(圖片、音頻、視頻))

緩存: 經(jīng)典使用場(chǎng)景,把常用信息,字符串,圖片或者視頻等信息放到redis中,redis作為緩存層,mysql做持久化層,降低mysql的讀寫壓力。

1.3.3 String內(nèi)部編碼

String內(nèi)部編碼:

  • int:8個(gè)字節(jié)的長(zhǎng)整型(long,2^63-1)
  • embstr小于等于44 個(gè)字節(jié)的字符串,embstr格式的 SDS(簡(jiǎn)單動(dòng)態(tài)字符串:Simple Dynamic String)
  • rawSDS大于 44 個(gè)字節(jié)的字符串

Redis 為什么要自己寫一個(gè)SDS的數(shù)據(jù)類型,主要是為了解決C語(yǔ)言 char[] 的四個(gè)問(wèn)題:

  • 字符數(shù)組必須先給目標(biāo)變量分配足夠的空間,否則可能會(huì)溢出
  • 查詢字符數(shù)組長(zhǎng)度,時(shí)間復(fù)雜度O(n)
  • 長(zhǎng)度變化,需要重新分配內(nèi)存
  • 通過(guò)從字符串開(kāi)始到結(jié)尾碰到的第一個(gè)\0來(lái)標(biāo)記字符串的結(jié)束,因此不能保存圖片、音頻、視頻、壓縮文件等二進(jìn)制(bytes)保存的內(nèi)容,二進(jìn)制不安全

Redis SDS的優(yōu)勢(shì):

  • 不用擔(dān)心內(nèi)存溢出問(wèn)題,如果需要會(huì)對(duì) SDS 進(jìn)行擴(kuò)容
  • 因?yàn)槎x了 len 屬性,查詢數(shù)組長(zhǎng)度時(shí)間復(fù)雜度O(1) 固定長(zhǎng)度
  • 空間預(yù)分配,惰性空間釋放
  • 根據(jù)長(zhǎng)度 len來(lái)判斷是否結(jié)束,而不是 \0

為什么要有embstr編碼呢?比raw的優(yōu)勢(shì)在哪里?

embstr編碼將創(chuàng)建字符串對(duì)象所需的空間分配的次數(shù)從raw編碼的兩次降低為一次。
因?yàn)?code>emstr編碼字符串的素有對(duì)象保持在一塊連續(xù)的內(nèi)存里面,所以那個(gè)編碼的字符串對(duì)象比起raw編碼的字符串對(duì)象能更好的利用緩存。并且釋放embstr編碼的字符串對(duì)象只需要調(diào)用一次內(nèi)存釋放函數(shù),而釋放raw編碼對(duì)象的字符串對(duì)象需要調(diào)用兩次內(nèi)存釋放函數(shù)

1.4 Hash散列

1.4.1 簡(jiǎn)介

常用命令:hget,hsetnx,hset,hvals,hgetall,hmset,hmget 等
Redis 中每個(gè) hash 可以存儲(chǔ) 2^32 - 1 鍵值對(duì)(40多億)

1.4.2 應(yīng)用常景

我們簡(jiǎn)單舉個(gè)實(shí)例來(lái)描述下 Hash 的應(yīng)用場(chǎng)景:

比如我們要存儲(chǔ)一個(gè)用戶信息對(duì)象數(shù)據(jù),包含以下信息:用戶 ID 為查找的 key,存儲(chǔ)的 value 用戶對(duì)象包含姓名,年齡,生日等信息,如果用普通的 key/value 結(jié)構(gòu)來(lái)存儲(chǔ),主要有以下2種存儲(chǔ)方式:

  • 第一種方式將用戶 ID 作為查找 key,把其他信息封裝成一個(gè)對(duì)象以序列化的方式存儲(chǔ),這種方式的缺點(diǎn)是,增加了序列化/反序列化的開(kāi)銷,并且在需要修改其中一項(xiàng)信息時(shí),需要把整個(gè)對(duì)象取回,并且修改操作需要對(duì)并發(fā)進(jìn)行保護(hù),引入CAS等復(fù)雜問(wèn)題。
  • 第二種方法是這個(gè)用戶信息對(duì)象有多少成員就存成多少個(gè) key-value 對(duì)兒,用用戶 ID +對(duì)應(yīng)屬性的名稱作為唯一標(biāo)識(shí)來(lái)取得對(duì)應(yīng)屬性的值,雖然省去了序列化開(kāi)銷和并發(fā)問(wèn)題,但是用戶 ID 為重復(fù)存儲(chǔ),如果存在大量這樣的數(shù)據(jù),內(nèi)存浪費(fèi)還是非??捎^的。

那么 Redis 提供的 Hash 很好的解決了這個(gè)問(wèn)題,Redis 的 Hash 實(shí)際是內(nèi)部存儲(chǔ)的 Value 為一個(gè) HashMap,并提供了直接存取這個(gè) Map 成員的接口

也就是說(shuō),Key 仍然是用戶 ID,value 是一個(gè) Map,這個(gè) Map 的 key 是成員的屬性名,value 是屬性值,這樣對(duì)數(shù)據(jù)的修改和存取都可以直接通過(guò)其內(nèi)部 Map 的 Key(Redis 里稱內(nèi)部 Map 的 key 為 field),也就是通過(guò) key(用戶 ID) + field(屬性標(biāo)簽)就可以操作對(duì)應(yīng)屬性數(shù)據(jù)了,既不需要重復(fù)存儲(chǔ)數(shù)據(jù),也不會(huì)帶來(lái)序列化和并發(fā)修改控制的問(wèn)題。

很好的解決了問(wèn)題。這里同時(shí)需要注意,Redis 提供了接口(hgetall)可以直接取到全部的屬性數(shù)據(jù),但是如果內(nèi)部 Map 的成員很多,那么涉及到遍歷整個(gè)內(nèi)部 Map 的操作,由于 Redis 單線程模型的緣故,這個(gè)遍歷操作可能會(huì)比較耗時(shí),而另其它客戶端的請(qǐng)求完全不響應(yīng),這點(diǎn)需要格外注意。

1.4.3 Hash內(nèi)部編碼

內(nèi)部編碼:

  • ziplist(壓縮列表):當(dāng)哈希類型中元素個(gè)數(shù)小于 hash-max-ziplist-entries配置(默認(rèn) 512 個(gè)),同時(shí)所有值都小于 hash-max-ziplist-value 配置(默認(rèn) 64 字節(jié))時(shí),Redis 會(huì)使用 ziplist 作為哈希的內(nèi)部實(shí)現(xiàn)。
  • hashtable(哈希表):當(dāng)上述條件不滿足時(shí),Redis 則會(huì)采用 hashtable 作為哈希的內(nèi)部實(shí)現(xiàn)。

在 Redis 7.0 中,壓縮列表數(shù)據(jù)結(jié)構(gòu)已經(jīng)廢棄了,交由 listpack 數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)了

1.4.4 rehash和漸進(jìn)式rehash操作

擴(kuò)容和縮容都會(huì)通過(guò)rehash來(lái)實(shí)現(xiàn),所謂漸進(jìn)式rehash是指我們的大字典的擴(kuò)容是比較消耗時(shí)間的,需要重新申請(qǐng)新的數(shù)組,然后將舊字典所有鏈表的元素重新掛接到新的數(shù)組下面,是一個(gè)O(n)的操作。但是因?yàn)槲覀兊?code>redis是單線程的,無(wú)法承受這樣的耗時(shí)過(guò)程,所以采用了漸進(jìn)式rehash小步搬遷,雖然慢一點(diǎn),但是可以搬遷完畢

redis會(huì)在內(nèi)部擴(kuò)容時(shí)新建一個(gè)長(zhǎng)度為原始長(zhǎng)度2倍的空哈希表,然后原哈希表上的元素重新rehash到新的哈希表中去,然后我們?cè)偈褂眯碌墓1砑纯伞?br />那么,這樣還是有個(gè)問(wèn)題要解決呀

要知道redis中存儲(chǔ)的數(shù)據(jù)可能是成百萬(wàn)上千萬(wàn)的,我們重新rehash一次未免太耗時(shí)了吧,因?yàn)閞edis中操作大部分是單線程的。
這個(gè)過(guò)程可能會(huì)阻斷其他操作很長(zhǎng)時(shí)間,這是不能忍受的,那要怎么處理呢

1.4.4.1 過(guò)程

首先redis是采用了漸進(jìn)式rehash的操作,就是會(huì)有一個(gè)變量,指向第一個(gè)哈希桶,然后redis每執(zhí)行一個(gè)添加key,刪除key的類似命令,就順便copy一個(gè)哈希桶中的數(shù)據(jù)到新的哈希表中去,這樣細(xì)水長(zhǎng)流的操作,是不會(huì)影響什么性能,就會(huì)所有的數(shù)據(jù)都被重新hash到新的哈希表中。

那么在這個(gè)過(guò)程中,當(dāng)然再有寫的操作,會(huì)直接把數(shù)據(jù)放到新的哈希表中,保證舊的肯定有copy完的時(shí)候,如果這段時(shí)間對(duì)數(shù)據(jù)庫(kù)的操作比較少,也沒(méi)有關(guān)系,redis內(nèi)部也有定時(shí)任務(wù),每隔一段時(shí)間也會(huì)copy一次

redis通過(guò)鏈?zhǔn)焦=鉀Q沖突,也就是同一個(gè)桶里面的元素使用鏈表保存。但是當(dāng)鏈表過(guò)長(zhǎng)就會(huì)導(dǎo)致查找性能變差可能。所以redis為了追求塊,使用了兩個(gè)全局哈希表。用于rehash操作,增加現(xiàn)有的哈希桶數(shù)量,減少哈希沖突
開(kāi)始默認(rèn)使用hash表1保存鍵值對(duì)數(shù)據(jù),hash表2此刻沒(méi)有分配空間。當(dāng)數(shù)據(jù)越來(lái)越多的觸發(fā)rehash操作,則執(zhí)行以下操作:

  • hash表2分配更大的空間
  • hash表1的數(shù)據(jù)重新映射拷貝到hash表2
    將hash表1的數(shù)據(jù)重新映射到hash表2的過(guò)程并不是一次性的,這樣會(huì)造成redis阻塞,無(wú)法提供服務(wù)
  • 釋放hash表1的空間

詳細(xì)步驟:

  • 為ht[1]分配空間,讓字典同時(shí)持有ht[0]和ht[1]兩個(gè)hash表
  • 在字典中維持一個(gè)索引計(jì)數(shù)器變量rehashidx,并將它的值設(shè)置為0,表示rehash工作正式開(kāi)始
  • 在rehash進(jìn)行期間,每次對(duì)字典執(zhí)行添加,刪除,查找或者更新操作時(shí),程序除了執(zhí)行特定的操作以外,還會(huì)順帶將ht[0]哈希表在rehashidx索引上的所有鍵值對(duì)rehash到ht[1],當(dāng)rehash工作完成之后,程序?qū)ehashidx屬性的值增1
  • 隨著字典操作的不斷執(zhí)行,最終在某個(gè)時(shí)間點(diǎn)上,ht[0]的所有鍵值對(duì)都會(huì)被rehash至ht[1],這時(shí)程序?qū)ehashidx屬性的值設(shè)為-1,表示rehash操作已完成
  • 將ht[0]釋放,然后將ht[1]設(shè)置成ht[0],最后為ht[1]分配一個(gè)空白哈希表

1.4.4.2 rehash觸發(fā)條件

rehash觸發(fā)條件:

  • 擴(kuò)容
    我們的擴(kuò)容一般會(huì)在Hash表中的元素個(gè)數(shù)等于第一維數(shù)組的長(zhǎng)度的時(shí)候,就會(huì)開(kāi)始擴(kuò)容。擴(kuò)容的大小是原數(shù)組的兩倍。不過(guò)在redis在做bgsave(RDB持久化操作的過(guò)程)時(shí),為了減少內(nèi)存頁(yè)的過(guò)多分離(Copy On Write),redis不會(huì)去擴(kuò)容。
    但是如果hash表的元素個(gè)數(shù)已經(jīng)到達(dá)了第一維數(shù)組長(zhǎng)度的5倍的時(shí)候,就會(huì)強(qiáng)制擴(kuò)容,不管你是否在持久化。
  • 縮容
    當(dāng)我們的hash表元素逐漸刪除的越來(lái)越少的時(shí)候。redis就會(huì)對(duì)hash表進(jìn)行縮容來(lái)減少第一維數(shù)組長(zhǎng)度的空間占用。縮容的條件是元素個(gè)數(shù)低于數(shù)組長(zhǎng)度的10%,并且縮容不考慮是否在做redis持久化。
    不用考慮bgsave主要原因是因?yàn)槲覀兊目s容的內(nèi)存都是已經(jīng)使用過(guò)的,縮容的時(shí)候可以直接置空,而且由于申請(qǐng)的內(nèi)存比較小,同時(shí)會(huì)釋放掉一些已經(jīng)使用的內(nèi)存,不會(huì)增大系統(tǒng)的壓力。

1.4.5 跟JDK的HashMap的區(qū)別

數(shù)據(jù)結(jié)構(gòu)上,采用了兩個(gè)數(shù)組保存數(shù)據(jù),發(fā)生hash沖突時(shí),只采用了鏈地址法解決hash沖突,并沒(méi)有跟jdk1.8一樣當(dāng)鏈表超過(guò)8時(shí)優(yōu)化成紅黑樹(shù),因此插入元素時(shí)跟jdk1.7hashmap一樣采用的是頭插法。

在發(fā)生擴(kuò)容時(shí),跟jdk的hashmap一次性、集中式進(jìn)行擴(kuò)容不一樣,采取的是漸進(jìn)式的rehash,每次操作只會(huì)操作當(dāng)前的元素,在當(dāng)前數(shù)組中移除或者存放到新的數(shù)組中,直到老數(shù)組的元素徹底變成空表。

當(dāng)負(fù)載因子小于0.1時(shí),會(huì)自動(dòng)進(jìn)行縮容。jdk的hashmap出于性能考慮,不提供縮容的操作。
redis使用MurmurHash來(lái)計(jì)算哈希表的鍵的hash值,而jdkhashmap使用key.hashcode()的高十六位跟低十六位做與運(yùn)算獲得鍵的hash值。

1.5 List列表

1.5.1 簡(jiǎn)介

Redis中的List其實(shí)就是鏈表Redis雙端鏈表實(shí)現(xiàn)List

使用List結(jié)構(gòu),我們可以輕松地實(shí)現(xiàn)最新消息排隊(duì)功能(比如新浪微博的TimeLine)。List的另一個(gè)應(yīng)用就是消息隊(duì)列,可以利用List的 PUSH 操作,將任務(wù)存放在List中,然后工作線程再用 POP 操作將任務(wù)取出進(jìn)行執(zhí)行。

列表(List)用來(lái)存儲(chǔ)多個(gè)有序的字符串,每個(gè)字符串稱為元素;一個(gè)列表可以存儲(chǔ)2^32-1個(gè)元素。Redis中的列表支持兩端插入和彈出,并可以獲得指定位置(或范圍)的元素,可以充當(dāng)數(shù)組、隊(duì)列、棧等

1.5.2 命令和應(yīng)用

常用命令:lpush,rpush,lpop,rpop,lrange等。

應(yīng)用場(chǎng)景

比如 twitter 的關(guān)注列表,粉絲列表等都可以用 Redis 的 list 結(jié)構(gòu)來(lái)實(shí)現(xiàn),可以利用lrange命令,做基于Redis的分頁(yè)功能,性能極佳,用戶體驗(yàn)好
消息隊(duì)列:Redis 的 list 是有序的列表結(jié)構(gòu),可以實(shí)現(xiàn)阻塞隊(duì)列,使用左進(jìn)右出的方式。Lpush 用來(lái)生產(chǎn) 從左側(cè)插入數(shù)據(jù),Brpop 用來(lái)消費(fèi),用來(lái)從右側(cè) 阻塞的消費(fèi)數(shù)據(jù)。

數(shù)據(jù)的分頁(yè)展示: lrange 命令需要兩個(gè)索引來(lái)獲取數(shù)據(jù),這個(gè)就可以用來(lái)實(shí)現(xiàn)分頁(yè),可以在代碼中計(jì)算兩個(gè)索引值,然后來(lái) redis 中取數(shù)據(jù)。
可以用來(lái)實(shí)現(xiàn)粉絲列表以及最新消息排行等功能

使用列表的技巧:

  • lpush+lpop=Stack(棧)
  • lpush+rpop=Queue(隊(duì)列)
  • lpush+ltrim=Capped Collection(有限集合)
  • lpush+brpop=Message Queue(消息隊(duì)列)

1.5.3 List內(nèi)部編碼

內(nèi)部編碼:

  • ziplist(壓縮列表):當(dāng)列表中元素個(gè)數(shù)小于 512(默認(rèn))個(gè),并且列表中每個(gè)元素的值都小于 64(默認(rèn))個(gè)字節(jié)時(shí),Redis 會(huì)選擇用 ziplist 來(lái)作為列表的內(nèi)部實(shí)現(xiàn)以減少內(nèi)存的使用。當(dāng)然上述默認(rèn)值也可以通過(guò)相關(guān)參數(shù)修改:list-max-ziplist-entried(元素個(gè)數(shù))、list-max-ziplist-value(元素值)。
  • linkedlist(鏈表):當(dāng)列表類型無(wú)法滿足 ziplist 條件時(shí),Redis 會(huì)選擇用 linkedlist 作為列表的內(nèi)部實(shí)現(xiàn)。
    因?yàn)?code>雙向鏈表占用的內(nèi)存比壓縮列表要多, 所以當(dāng)創(chuàng)建新的列表鍵時(shí), 列表會(huì)優(yōu)先考慮使用壓縮列表, 并且在有需要的時(shí)候, 才從壓縮列表實(shí)現(xiàn)轉(zhuǎn)換到雙向鏈表實(shí)現(xiàn)
  • quicklist(快速列表)就是linkedlistziplist的結(jié)合。quicklist中的每個(gè)節(jié)點(diǎn)ziplist都能夠存儲(chǔ)多個(gè)數(shù)據(jù)元素。Redis3.2開(kāi)始,列表采quicklist進(jìn)編碼

1.6 Set集合

1.6.1 簡(jiǎn)介

Redis 的 Set 是 String 類型的無(wú)序集合。集合成員是唯一的,這就意味著集合中不能出現(xiàn)重復(fù)的數(shù)據(jù)。
Redis 中集合是通過(guò)哈希表實(shí)現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是 O(1)集合中最大的成員數(shù)為 2^32 - 1 (每個(gè)集合可存儲(chǔ)40多億個(gè)成員)

1.6.2 命令和應(yīng)用

常用命令:sadd,spop,smembers,sunion,scard,sscan,sismember等。

應(yīng)用場(chǎng)景:

Redis set 對(duì)外提供的功能與 list 類似是一個(gè)列表的功能,特殊之處在于 set 是可以自動(dòng)去重的,當(dāng)你需要存儲(chǔ)一個(gè)列表數(shù)據(jù),又不希望出現(xiàn)重復(fù)數(shù)據(jù)時(shí),set 是一個(gè)很好的選擇,并且 set 提供了判斷某個(gè)成員是否在一個(gè) set 集合內(nèi)的重要接口,這個(gè)也是 list 所不能提供的。

標(biāo)簽(tag):集合類型比較典型的使用場(chǎng)景,如一個(gè)用戶對(duì)娛樂(lè)、體育比較感興趣,另一個(gè)可能對(duì)新聞感興趣,這些興趣就是標(biāo)簽,有了這些數(shù)據(jù)就可以得到同一標(biāo)簽的人,以及用戶的共同愛(ài)好的標(biāo)簽,這些數(shù)據(jù)對(duì)于用戶體驗(yàn)以及曾強(qiáng)用戶粘度比較重要。

點(diǎn)贊,或點(diǎn)踩,收藏等,可以放到set中實(shí)現(xiàn)

1.6.3 Set內(nèi)部編碼

Set內(nèi)部編碼:

  • intset(整數(shù)集合):當(dāng)集合中的元素都是整數(shù),并且集合中的元素個(gè)數(shù)小于set-max-intset-entries 參數(shù)時(shí),默認(rèn)512,Redis 會(huì)選用 intset 作為底層內(nèi)部實(shí)現(xiàn)。
  • hashtable(哈希表):當(dāng)上述條件不滿足時(shí),Redis 會(huì)采用 hashtable 作為底層實(shí)現(xiàn)。

1.7 ZSet有序集合

1.7.1 簡(jiǎn)介

Redis 有序集合和集合一樣也是 string 類型元素的集合,且不允許重復(fù)的成員。不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè) double 類型的分?jǐn)?shù)。redis 正是通過(guò)分?jǐn)?shù)來(lái)為集合中的成員進(jìn)行從小到大的排序。

有序集合的成員是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)。集合是通過(guò)哈希表實(shí)現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是 O(1)。
集合中最大的成員數(shù)為 2^32 - 1 (每個(gè)集合可存儲(chǔ)40多億個(gè)成員)

1.7.2 命令和應(yīng)用

常用命令:zadd,zrange,zrem,zcard,zscore,zcount,zlexcount等

應(yīng)用常景:

  • 排行榜:有序集合經(jīng)典使用場(chǎng)景
    例如小說(shuō)視頻等網(wǎng)站需要對(duì)用戶上傳的小說(shuō)視頻做排行榜,榜單可以按照用戶關(guān)注數(shù),更新時(shí)間,字?jǐn)?shù)等打分,做排行,
    如新聞網(wǎng)站對(duì)熱點(diǎn)新聞排序,比如根據(jù)點(diǎn)擊量、點(diǎn)贊量等。
  • 帶權(quán)重的消息隊(duì)列:重要的消息 score 大一些,普通消息 score 小一些,可以實(shí)現(xiàn)優(yōu)先級(jí)高的任務(wù)先執(zhí)行

1.7.3 ZSet內(nèi)部編碼

內(nèi)部編碼:

  • ziplist(壓縮列表):當(dāng)有序集合的元素個(gè)數(shù)小于 128 個(gè)(默認(rèn)設(shè)置),同時(shí)每個(gè)元素的值都小于 64 字節(jié)(默認(rèn)設(shè)置),Redis 會(huì)采用 ziplist 作為有序集合的內(nèi)部實(shí)現(xiàn)。也可以通過(guò)以下參數(shù)設(shè)置:zset-max-ziplist-entries 和 zset-max-ziplist-value
  • skiplist(跳躍表):當(dāng)上述條件不滿足時(shí),Redis 會(huì)采用 skiplist 作為內(nèi)部編碼。

在 Redis 7.0 中,壓縮列表數(shù)據(jù)結(jié)構(gòu)已經(jīng)廢棄了,交由 listpack 數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)了

1.7.4 跳表數(shù)據(jù)結(jié)構(gòu)

鏈表在查找元素的時(shí)候,因?yàn)樾枰鹨徊檎遥圆樵冃史浅5?,時(shí)間復(fù)雜度是O(N),于是就出現(xiàn)了跳表。跳表是在鏈表基礎(chǔ)上改進(jìn)過(guò)來(lái)的,實(shí)現(xiàn)了一種 多層 的有序鏈表,這樣的好處是能快讀定位數(shù)據(jù)。

那跳表長(zhǎng)什么樣呢?這里舉個(gè)例子,下圖展示了一個(gè)層級(jí)為 3 的跳表。

圖中頭節(jié)點(diǎn)有 L0~L2 三個(gè)頭指針,分別指向了不同層級(jí)的節(jié)點(diǎn),然后每個(gè)層級(jí)的節(jié)點(diǎn)都通過(guò)指針連接起來(lái):

  • L0 層級(jí)共有 5 個(gè)節(jié)點(diǎn),分別是節(jié)點(diǎn)1、2、3、4、5;
  • L1 層級(jí)共有 3 個(gè)節(jié)點(diǎn),分別是節(jié)點(diǎn) 2、3、5;
  • L2 層級(jí)只有 1 個(gè)節(jié)點(diǎn),也就是節(jié)點(diǎn) 3 。

如果我們要在鏈表中查找節(jié)點(diǎn) 4 這個(gè)元素,只能從頭開(kāi)始遍歷鏈表,需要查找 4 次,而使用了跳表后,只需要查找 2 次就能定位到節(jié)點(diǎn) 4,因?yàn)榭梢栽陬^節(jié)點(diǎn)直接從 L2 層級(jí)跳到節(jié)點(diǎn) 3,然后再往前遍歷找到節(jié)點(diǎn) 4。
可以看到,這個(gè)查找過(guò)程就是在多個(gè)層級(jí)上跳來(lái)跳去,最后定位到元素。當(dāng)數(shù)據(jù)量很大時(shí),跳表的查找復(fù)雜度就是 O(logN)

跳表是怎么設(shè)置層高的?

跳表在創(chuàng)建節(jié)點(diǎn)時(shí)候,會(huì)生成范圍為 [0-1] 的一個(gè)隨機(jī)數(shù),如果這個(gè)隨機(jī)數(shù)小于 0.25(相當(dāng)于概率 25%),那么層數(shù)就增加 1 層,然后繼續(xù)生成下一個(gè)隨機(jī)數(shù),直到隨機(jī)數(shù)的結(jié)果大于 0.25 結(jié)束,最終確定該節(jié)點(diǎn)的層數(shù)。

1.8 Bitmap位圖和布隆過(guò)濾器

點(diǎn)擊了解java中Bitmap和布隆過(guò)濾器

1.8.1 Bitmap位圖

1.8.1.1 簡(jiǎn)介

Bitmap(也稱為位數(shù)組或者位向量等)是一種實(shí)現(xiàn)對(duì)位的操作的’數(shù)據(jù)結(jié)構(gòu)’,在數(shù)據(jù)結(jié)構(gòu)加引號(hào)主要因?yàn)椋?code>Bitmap本身不是一種數(shù)據(jù)結(jié)構(gòu),底層實(shí)際上是字符串,可以借助字符串進(jìn)行位操作。

Bitmap 單獨(dú)提供了一套命令,所以與使用字符串的方法不太相同??梢园?Bitmaps 想象成一個(gè)以位為單位的數(shù)組,數(shù)組的每個(gè)單元只能存儲(chǔ) 0 和 1,數(shù)組的下標(biāo)在 Bitmap 中叫做偏移量 offset。
bitmap的出現(xiàn)是為了大數(shù)據(jù)量而來(lái)的,但是前提是統(tǒng)計(jì)的這個(gè)大數(shù)據(jù)量每個(gè)的狀態(tài)只能有兩種,因?yàn)槊恳粋€(gè)bit位只能表示兩種狀態(tài)。

優(yōu)點(diǎn):

  • 極高空間效率:bitmap 是真的節(jié)省數(shù)據(jù)存儲(chǔ)空間。粗略的算一下,一億位的 Bitmap 大概才占 12MB 的內(nèi)存,相比其他數(shù)據(jù)結(jié)構(gòu),能極大地節(jié)省存儲(chǔ)空間;
  • 快速查詢:位操作通常比其他數(shù)據(jù)結(jié)構(gòu)查詢速度更快。無(wú)論是設(shè)置位值還是獲取位值,時(shí)間復(fù)雜度都為 O (1),能夠快速響應(yīng)查詢請(qǐng)求;
  • 易于操作:支持單個(gè)位操作、位統(tǒng)計(jì)、位邏輯運(yùn)算等,運(yùn)算效率高,不需要進(jìn)行比較和移位;

缺點(diǎn):

  • 由于數(shù)據(jù)結(jié)構(gòu)特點(diǎn),導(dǎo)致它僅適用于表示兩種狀態(tài),即 0 和 1。對(duì)于需要表示更多狀態(tài)的情況,Bitmap 就不適用了;
  • 只有當(dāng)數(shù)據(jù)比較密集時(shí)才有優(yōu)勢(shì),如果只設(shè)置(20,30,888888888)三個(gè)偏移量的位值,則需要?jiǎng)?chuàng)建一個(gè) 99999999 長(zhǎng)度的 BitMap ,但是實(shí)際上只存了3個(gè)數(shù)據(jù),這時(shí)候就有很大的空間浪費(fèi),碰到這種問(wèn)題的話,可以通過(guò)引入另一個(gè) Roaring BitMap 來(lái)解決;

1.8.1.2 應(yīng)用常景

假如我們現(xiàn)在有幾億個(gè)數(shù)據(jù),數(shù)據(jù)狀態(tài)都是1或者0兩個(gè)狀態(tài),比如用戶簽到次數(shù)、或者登錄次數(shù)等。

  • 場(chǎng)景一:用戶簽到
    很多網(wǎng)站都提供了簽到功能(這里不考慮數(shù)據(jù)落地事宜),并且需要展示最近一個(gè)月的簽到情況
    Redis 為我們提供了bitmap(位圖)這一數(shù)據(jù)結(jié)構(gòu),每個(gè)用戶每天的登錄記錄只占據(jù)一位,365天就是365位,僅僅需要46字節(jié)就可存儲(chǔ),極大地節(jié)約了存儲(chǔ)空間。
  • 場(chǎng)景二:統(tǒng)計(jì)活躍用戶
    使用時(shí)間作為cacheKey,然后用戶ID為offset,如果當(dāng)日活躍過(guò)就設(shè)置為1
    那么我該如果計(jì)算某幾天/月/年的活躍用戶呢(暫且約定,統(tǒng)計(jì)時(shí)間內(nèi)只有有一天在線就稱為活躍),有請(qǐng)下一個(gè)redis的命令
  • 場(chǎng)景三:用戶在線狀態(tài)
    前段時(shí)間開(kāi)發(fā)一個(gè)項(xiàng)目,對(duì)方給我提供了一個(gè)查詢當(dāng)前用戶是否在線的接口。不了解對(duì)方是怎么做的,自己考慮了一下,使用bitmap是一個(gè)節(jié)約空間效率又高的一種方法,只需要一個(gè)key,然后用戶ID為offset,如果在線就設(shè)置為1,不在線就設(shè)置為0,和上面的場(chǎng)景一樣,5000W用戶只需要6MB的空間

1.8.1.3 底層原理

我們知道 Bitmap 本身不是一種數(shù)據(jù)結(jié)構(gòu),底層實(shí)際上使用字符串來(lái)存儲(chǔ)。只不過(guò)操作的粒度變成了位,即bit。
由于 Redis 中字符串的最大長(zhǎng)度是 512 MB字節(jié),所以 BitMap 的偏移量 offset 值也是有上限的,其最大值是:8 * 1024 * 1024 * 512 = 2^32。由于 C 語(yǔ)言中字符串的末尾都要存儲(chǔ)一位分隔符,所以實(shí)際上 BitMap 的偏移量 offset 值上限是:2^32-1

Bitmap 本身是用 String 類型作為底層數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的一種統(tǒng)計(jì)二值狀態(tài)的數(shù)據(jù)類型。String 類型是會(huì)保存為二進(jìn)制的字節(jié)數(shù)組,所以,Redis 就把字節(jié)數(shù)組的每個(gè) bit 位利用起來(lái),用來(lái)表示一個(gè)元素的二值狀態(tài)??梢园?nbsp;Bitmap 看作是一個(gè) bit 數(shù)組。

1.8.1.4 命令

  • SETBIT
    SETBIE用來(lái)設(shè)置或清除存儲(chǔ)在鍵處的字符串值的偏移位,其返回值是原來(lái)位上存儲(chǔ)的值。key 在初始狀態(tài)下所有的位都為 0
    基本格式:SETBIT key offset value
  • GETBIT
    GETBIT 用來(lái)獲取存儲(chǔ)在鍵處的字符串值中偏移位置的位值。
    基本格式:GETBIT key offset
  • BITCOUNT
    BITCOUNT 用來(lái)統(tǒng)計(jì)指定區(qū)間內(nèi),值為1的個(gè)數(shù)。選擇特定的 byte 范圍計(jì)數(shù),具體如下
    基本格式:BITCOUNT key [start end](注意start和end指的是字節(jié),不是位)
    • start:設(shè)置位索引起始位置(包含該位置計(jì)數(shù)),第一個(gè)位置以 0 開(kāi)始,start 參數(shù)需和 end 參數(shù)同時(shí)設(shè)置才合法
    • end:設(shè)置位索引結(jié)束位置(包含該位置計(jì)數(shù)),end 參數(shù)需和 start參數(shù)同時(shí)設(shè)置才合法
  • BITOP
    對(duì)一個(gè)或多個(gè)保存二進(jìn)制位的字符串 key 進(jìn)行位元操作,并將結(jié)果保存到 destkey 上
    語(yǔ)法格式:BITOP operation destkey key [key ...]語(yǔ)法:operation 可以是 AND(與) 、 OR (或)、 NOT(非) 、 XOR(異或)除了 NOT 操作之外,其他操作都可以接受一個(gè)或多個(gè) key 作為輸入
  • BITPOS
    用來(lái)計(jì)算指定 key 對(duì)應(yīng)字符串中,第一位為 1 或者 0 的 offset 位置。除此之外,BITPOS 也有兩個(gè)選項(xiàng) start 和 end,跟 BITCOUNT 一樣。
    語(yǔ)法格式:BITPOS key bit [ start [ end [ BYTE | BIT]]]BYTE、BIT 這兩個(gè)選項(xiàng)從 7.0.0 版本開(kāi)始才能使用。

1.8.2 布隆過(guò)濾器

1.8.2.1 簡(jiǎn)介

上邊提到 bitmap 記錄字符元素的狀態(tài)時(shí),需要先借助哈希運(yùn)算得出偏移量。但引入哈希運(yùn)算后可能會(huì)出現(xiàn)哈希碰撞的情況,導(dǎo)致?tīng)顟B(tài)誤判。

布隆過(guò)濾器對(duì)這個(gè)問(wèn)題做了進(jìn)一步的優(yōu)化,做到了可控誤判率,當(dāng)我們將一個(gè)郵箱地址添加到集合中,多個(gè)不同的哈希函數(shù)會(huì)將這個(gè)郵箱地址映射到 bitmap 中的不同偏移量位置上,且將這些位值置為 1。

要判斷郵箱地址是否在集合中,通過(guò)相同的哈希函數(shù)映射到 bitmap 上的多個(gè)位置,如果這些位上的值都為 1,則郵箱可能存在集合中;如果有任何一個(gè)位置的值為 0,則元素一定不在集合中。這是布隆過(guò)濾器的特點(diǎn)。

雖然但是布隆過(guò)濾器還是會(huì)發(fā)生誤判的情況,但好在我們可以通過(guò)調(diào)整布隆過(guò)濾器的大小和哈希函數(shù)的數(shù)量來(lái)控制誤判率。

注意:需要Redis服務(wù)器版本是4.0或以上,因?yàn)?code>Redis 4.0引入了插件機(jī)制,支持布隆過(guò)濾器等模塊的加載
配置的話是,打開(kāi)Redis的配置文件redis.conf,這個(gè)文件通常位于Redis的安裝目錄下。在配置文件中,添加以下行來(lái)加載布隆過(guò)濾器模塊:loadmodule /path/to/redisbloom.so

1.8.2.2 操作命令

布隆過(guò)濾器的命令也不多,主要用到的如下幾個(gè):

BF.RESERVE:創(chuàng)建一個(gè)新的布隆過(guò)濾器,并指定容量 capacity 和誤判率 error_rate

BF.RESERVE <key> <error_rate> <capacity>
BF.RESERVE myfilter 0.000001 999999

BF.INFO:獲取布隆過(guò)濾器的信息,包括容量、誤判率等。BF.INFO <key>BF.ADD 和 BF.MADD:分別是向布隆過(guò)濾器中添加元素和批量添加

# 向布隆過(guò)濾器中添加元素
BF.ADD myfilter hello
BF.MADD <key> <item> [item ...]

BF.EXISTS 和 BF.MEXISTS:分別是檢查布隆過(guò)濾器中某個(gè)元素和批量檢查元素是否存在

# 元素是否存在于布隆過(guò)濾器中
BF.EXISTS myfilter hello
# 元素是否存在于布隆過(guò)濾器中
BF.MEXISTS <key> <item> [item ...]

1.8.2.3 優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):
    • 布隆過(guò)濾器的空間占用也是極小,它本身不存儲(chǔ)完整的數(shù)據(jù),和 bitmap 一樣底層也是通過(guò) bit 位來(lái)表示數(shù)據(jù)是否存在。
    • 性能比較穩(wěn)定,無(wú)論集合中元素的數(shù)量有多少,插入和查詢操作的時(shí)間復(fù)雜度都非常低,通常為 O (k),其中 k 是哈希函數(shù)的個(gè)數(shù)。也就是說(shuō)在處理大規(guī)模數(shù)據(jù)時(shí),布隆過(guò)濾器的性能不會(huì)隨著數(shù)據(jù)量的增加而急劇下降。
  • 缺點(diǎn):
    • 存在一定的誤識(shí)別率:布隆過(guò)濾器存在誤判的情況,即當(dāng)一個(gè)元素實(shí)際上不在集合中時(shí),有可能被判斷為在集合中。這是因?yàn)槎鄠€(gè)元素可能通過(guò)哈希函數(shù)映射到相同的位置,導(dǎo)致誤判。但是,當(dāng)布隆過(guò)濾器判斷一個(gè)元素不在集合中時(shí),則是 100% 正確的。
    • 刪除元素比較困難:一般情況下,不能直接從布隆過(guò)濾器中刪除元素。這是因?yàn)橐粋€(gè)位置可能被多個(gè)元素映射到,如果直接將該位置的值置為 0,可能會(huì)影響其他元素的判斷。

1.8.2.4 應(yīng)用場(chǎng)景

布隆過(guò)濾器存在一定的誤判,所以使用它的場(chǎng)景就一定要允許不準(zhǔn)確的情況發(fā)生:

  • 解決 Redis 緩存穿透問(wèn)題:秒殺商品詳情通常會(huì)被緩存到 Redis 中。如果有大量惡意請(qǐng)求查詢不存在的商品,通過(guò)布隆過(guò)濾器可以快速判斷這些商品不存在,從而避免了對(duì)數(shù)據(jù)庫(kù)的查詢,減輕了數(shù)據(jù)庫(kù)的壓力。
  • 郵箱黑名單過(guò)濾:在郵件系統(tǒng)中,可以使用布隆過(guò)濾器來(lái)過(guò)濾垃圾郵件和惡意郵件。將已知的垃圾郵件發(fā)送者的地址或特征存儲(chǔ)在布隆過(guò)濾器中,新郵件來(lái)時(shí)判斷發(fā)送者是否在黑名單中。
  • 對(duì)爬蟲網(wǎng)址進(jìn)行過(guò)濾:在爬蟲程序中,為了避免重復(fù)抓取相同的網(wǎng)址,可以使用布隆過(guò)濾器來(lái)記錄已經(jīng)抓取過(guò)的網(wǎng)址。新網(wǎng)址出現(xiàn)時(shí),先判斷是否已抓取過(guò)。

1.9 HyperLogLog基數(shù)統(tǒng)計(jì)

1.9.1 簡(jiǎn)介

Redis 2.8.9 版本更新了 Hyperloglog 數(shù)據(jù)結(jié)構(gòu),Redis HyperLogLog 是用來(lái)做基數(shù)統(tǒng)計(jì)的算法,所謂基數(shù),也就是不重復(fù)的元素

  • 優(yōu)點(diǎn):
    在輸入元素的數(shù)量或者體積非常大時(shí),計(jì)算基數(shù)所需的空間總是固定的、并且是很小的。在 Redis 里面,每個(gè) HyperLogLog 鍵只需要花費(fèi) 12 KB 內(nèi)存,就可以計(jì)算接近 2^64 個(gè)不同元素的基數(shù)。
  • 缺點(diǎn):
    因?yàn)?nbsp;HyperLogLog 只會(huì)根據(jù)輸入元素來(lái)計(jì)算基數(shù),而不會(huì)儲(chǔ)存輸入元素本身,所以 HyperLogLog 不能像集合那樣,返回輸入的各個(gè)元素。
    估算的值,可能存在誤差,帶有 0.81% 標(biāo)準(zhǔn)錯(cuò)誤的近似值

1.9.2 命令和場(chǎng)景

這個(gè)數(shù)據(jù)結(jié)構(gòu)的命令有三個(gè):

  • PFADD:添加指定元素到 HyperLogLog
  • PFCOUNT:返回給定 HyperLogLog 的基數(shù)估算值
  • PFMERGE:將多個(gè) HyperLogLog 合并為一個(gè) HyperLogLog

應(yīng)用場(chǎng)景:

  • 網(wǎng)頁(yè)統(tǒng)計(jì)UV (瀏覽用戶數(shù)量,同一天同一個(gè)ip多次訪問(wèn)算一次訪問(wèn),目的是計(jì)數(shù),而不是保存用戶)
    傳統(tǒng)的方式是使用set保存用戶的id,可以統(tǒng)計(jì)set中元素?cái)?shù)量作為標(biāo)準(zhǔn)判斷。
    但如果這種方式保存大量用戶id,會(huì)占用大量?jī)?nèi)存,我們的目的是為了計(jì)數(shù),而不是去保存id。
  • 注冊(cè) IP 數(shù)、每日訪問(wèn) IP 數(shù)、頁(yè)面實(shí)時(shí)UV)、在線用戶數(shù)等

1.9.3 內(nèi)部編碼和原理

HyperLogLog算法時(shí)一種非常巧妙的近似統(tǒng)計(jì)大量去重元素?cái)?shù)量的算法,它內(nèi)部維護(hù)了16384個(gè)桶來(lái)記錄各自桶的元素?cái)?shù)量,當(dāng)一個(gè)元素過(guò)來(lái),它會(huì)散列到其中一個(gè)桶。當(dāng)元素到來(lái)時(shí),通過(guò) hash 算法將這個(gè)元素分派到其中的一個(gè)小集合存儲(chǔ),同樣的元素總是會(huì)散列到同樣的小集合。這樣總的計(jì)數(shù)就是所有小集合大小的總和。使用這種方式精確計(jì)數(shù)除了可以增加元素外,還可以減少元素

一個(gè)HyperLogLog實(shí)際占用的空間大約是 12k 字節(jié)。但是在計(jì)數(shù)比較小的時(shí)候,大多數(shù)桶的計(jì)數(shù)值都是零。如果 12k 字節(jié)里面太多的字節(jié)都是零,那么這個(gè)空間是可以適當(dāng)節(jié)約一下的。
Redis 在計(jì)數(shù)值比較小的情況下采用了稀疏存儲(chǔ)稀疏存儲(chǔ)的空間占用遠(yuǎn)遠(yuǎn)小于 12k 字節(jié)。相對(duì)于稀疏存儲(chǔ)的就是密集存儲(chǔ),密集存儲(chǔ)會(huì)恒定占用 12k 字節(jié)。

內(nèi)部編碼
HyperLogLog 整體的內(nèi)部結(jié)構(gòu)就是 HLL 對(duì)象頭 加上 16384 個(gè)桶的計(jì)數(shù)值位圖。它在 Redis 的內(nèi)部結(jié)構(gòu)表現(xiàn)就是一個(gè)字符串位圖。你可以把 HyperLogLog 對(duì)象當(dāng)成普通的字符串來(lái)進(jìn)行處理。

1.10 GEO地理位置

1.10.1 簡(jiǎn)介

Redis 的 Geo 在 Redis 3.2 版本就推出了,這個(gè)功能可以推算地理位置的信息: 兩地之間的距離, 方圓幾里的人,GEO使用的是國(guó)際通用坐標(biāo)系WGS-84

1.10.2 命令和場(chǎng)景

命令:

help @geo 查看geo分組下所有的命令
help geoadd 用于查看單個(gè)具體命令

主要操作方法有:

  • geoadd:添加地理位置的坐標(biāo)
    geoadd 語(yǔ)法格式:GEOADD key longitude latitude member [longitude latitude member ...]
  • geopos:用于從給定的 key 里返回所有指定名稱(member)的位置(經(jīng)度和緯度),不存在的返回 nilgeopos 語(yǔ)法格式:GEOPOS key member [member ...]
  • geodist:用于返回兩個(gè)給定位置之間的距離
    geodist 語(yǔ)法格式:GEODIST key member1 member2 [m|km|ft|mi]member1 member2 為兩個(gè)地理位置
    最后一個(gè)距離單位參數(shù)說(shuō)明:m :米,默認(rèn)單位;km :千米;mi :英里;ft :英尺
  • georadius:以給定的經(jīng)緯度為中心, 返回鍵包含的位置元素當(dāng)中, 與中心的距離不超過(guò)給定最大距離的所有位置元素
    語(yǔ)法格式:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]參數(shù)說(shuō)明:
    • m :米,默認(rèn)單位。
    • km :千米。
    • mi :英里。
    • ft :英尺。
    • WITHDIST: 在返回位置元素的同時(shí), 將位置元素與中心之間的距離也一并返回。
    • WITHCOORD: 將位置元素的經(jīng)度和緯度也一并返回。
    • WITHHASH: 以 52 位有符號(hào)整數(shù)的形式, 返回位置元素經(jīng)過(guò)原始 geohash 編碼的有序集合分值。 這個(gè)選項(xiàng)主要用于底層應(yīng)用或者調(diào)試, 實(shí)際中的作用并不大。
    • COUNT: 限定返回的記錄數(shù)
    • ASC: 查找結(jié)果根據(jù)距離從近到遠(yuǎn)排序。
    • DESC: 查找結(jié)果根據(jù)從遠(yuǎn)到近排序。
  • georadiusbymember: 和 GEORADIUS 命令一樣, 都可以找出位于指定范圍內(nèi)的元素, 但是 georadiusbymember 的中心點(diǎn)是由給定的位置元素決定的, 而不是使用經(jīng)度和緯度來(lái)決定中心點(diǎn)
    語(yǔ)法格式:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]參數(shù)說(shuō)明同GEORADIUS
  • geohash:返回一個(gè)或多個(gè)位置對(duì)象的 geohash 值。
    Redis GEO 使用 geohash 來(lái)保存地理位置的坐標(biāo)。geohash 用于獲取一個(gè)或多個(gè)位置元素的 geohash 值。
    geohash 語(yǔ)法格式如下:GEOHASH key member [member ...]

應(yīng)用場(chǎng)景:
用于存儲(chǔ)地理信息以及對(duì)地理信息作操作的場(chǎng)景

  • 查看附近的人
  • 微信位置共享
  • 地圖上直線距離的展示
  • 比如檢索附近的主播

1.10.3 內(nèi)部編碼

需要說(shuō)明的是,Geo本身不是一種數(shù)據(jù)結(jié)構(gòu),它本質(zhì)上還是借助于Sorted Set(ZSET),并且使用GeoHash技術(shù)進(jìn)行填充。Redis中將經(jīng)緯度使用52位的整數(shù)進(jìn)行編碼,放進(jìn)zset中,score就是GeoHash的52位整數(shù)值。在使用Redis進(jìn)行Geo查詢時(shí),其內(nèi)部對(duì)應(yīng)的操作其實(shí)就是zset(skiplist)的操作。
通過(guò)zsetscore進(jìn)行排序就可以得到坐標(biāo)附近的其它元素,通過(guò)將score還原成坐標(biāo)值就可以得到元素的原始坐標(biāo)。

總之,Redis中處理這些地理位置坐標(biāo)點(diǎn)的思想是:二維平面坐標(biāo)點(diǎn) --> 一維整數(shù)編碼值 --> zset(score為編碼值) --> zrangebyrank(獲取score相近的元素)、zrangebyscore --> 通過(guò)score(整數(shù)編碼值)反解坐標(biāo)點(diǎn) --> 附近點(diǎn)的地理位置坐標(biāo)

1.11 Stream流

1.11.1 簡(jiǎn)介

Redis Stream 是 Redis 5.0 版本新增加的數(shù)據(jù)結(jié)構(gòu)。
Redis Stream 主要用于消息隊(duì)列(MQ,Message Queue),Redis 本身是有一個(gè) Redis 發(fā)布訂閱 (pub/sub) 來(lái)實(shí)現(xiàn)消息隊(duì)列的功能,但它有個(gè)缺點(diǎn)就是消息無(wú)法持久化,如果出現(xiàn)網(wǎng)絡(luò)斷開(kāi)、Redis 宕機(jī)等,消息就會(huì)被丟棄。

點(diǎn)擊此處了解為什么Redis不適合用作MQ簡(jiǎn)單來(lái)說(shuō)發(fā)布訂閱 (pub/sub) 可以分發(fā)消息,但無(wú)法記錄歷史消息。

Redis Stream 提供了消息的持久化主備復(fù)制功能,可以讓任何客戶端訪問(wèn)任何時(shí)刻的數(shù)據(jù),并且能記住每一個(gè)客戶端的訪問(wèn)位置,還能保證消息不丟失。
用一句話概括Stream就是Redis實(shí)現(xiàn)的內(nèi)存版kafka,支持多播的可持久化的消息隊(duì)列,用于實(shí)現(xiàn)發(fā)布訂閱功能,借鑒了 kafka 的設(shè)計(jì)。Redis Stream的結(jié)構(gòu)有一個(gè)消息鏈表,將所有加入的消息都串起來(lái),每個(gè)消息都有一個(gè)唯一的ID和對(duì)應(yīng)的內(nèi)容。消息是持久化的,Redis重啟后,內(nèi)容還在。

1.11.2 命令

Redis Stream 的結(jié)構(gòu)如下所示,它有一個(gè)消息鏈表,將所有加入的消息都串起來(lái),每個(gè)消息都有一個(gè)唯一的 ID 和對(duì)應(yīng)的內(nèi)容

每個(gè) Stream 都有唯一的名稱,它就是 Redis 的 key,在我們首次使用 xadd 指令追加消息時(shí)自動(dòng)創(chuàng)建。

上圖解析:

  • Consumer Group :消費(fèi)組,使用 XGROUP CREATE 命令創(chuàng)建,一個(gè)消費(fèi)組有多個(gè)消費(fèi)者(Consumer)。
  • last_delivered_id :游標(biāo),每個(gè)消費(fèi)組會(huì)有個(gè)游標(biāo) last_delivered_id,任意一個(gè)消費(fèi)者讀取了消息都會(huì)使游標(biāo) last_delivered_id 往前移動(dòng)。
  • pending_ids :消費(fèi)者(Consumer)的狀態(tài)變量,作用是維護(hù)消費(fèi)者的未確認(rèn)的 id。 pending_ids 記錄了當(dāng)前已經(jīng)被客戶端讀取的消息,但是還沒(méi)有 ack (Acknowledge character:確認(rèn)字符)。

消息隊(duì)列相關(guān)命令:

  • XADD :添加消息到末尾
    使用 XADD 向隊(duì)列添加消息,如果指定的隊(duì)列不存在,則創(chuàng)建一個(gè)隊(duì)列
    語(yǔ)法格式:XADD key ID field value [field value ...]
    • key :隊(duì)列名稱,如果不存在就創(chuàng)建
    • ID :消息 id,我們使用 * 表示由 redis 生成,可以自定義,但是要自己保證遞增性。
    • field value : 記錄
  • XTRIM :對(duì)流進(jìn)行修剪,限制長(zhǎng)度
    語(yǔ)法格式:XTRIM key MAXLEN [~] count
    • key :隊(duì)列名稱
    • MAXLEN :長(zhǎng)度
    • count :數(shù)量
  • XDEL :刪除消息
    語(yǔ)法格式:XDEL key ID [ID ...]
    • key:隊(duì)列名稱
    • ID :消息 ID
  • XLEN :獲取流包含的元素?cái)?shù)量,即消息長(zhǎng)度
    語(yǔ)法格式:XLEN keykey:隊(duì)列名稱
  • XRANGE :獲取消息列表,會(huì)自動(dòng)過(guò)濾已經(jīng)刪除的消息
    語(yǔ)法格式:XRANGE key start end [COUNT count]
    • key :隊(duì)列名
    • start :開(kāi)始值,-表示最小值
    • end :結(jié)束值, + 表示最大值
    • count :數(shù)量
  • XREVRANGE :反向獲取消息列表,ID 從大到小
    語(yǔ)法格式:XREVRANGE key end start [COUNT count]
    • key :隊(duì)列名
    • end :結(jié)束值, + 表示最大值
    • start :開(kāi)始值, - 表示最小值
    • count :數(shù)量
  • XREAD :以阻塞或非阻塞方式獲取消息列表
    語(yǔ)法格式:XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
    • count :數(shù)量
    • milliseconds :可選,阻塞毫秒數(shù),沒(méi)有設(shè)置就是非阻塞模式
    • key :隊(duì)列名
    • id :消息 ID

消費(fèi)者組相關(guān)命令:

  • XGROUP CREATE :創(chuàng)建消費(fèi)者組
    語(yǔ)法格式:XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]
    • key :隊(duì)列名稱,如果不存在就創(chuàng)建
    • groupname :組名。
    • $ : 表示從尾部開(kāi)始消費(fèi),只接受新消息,當(dāng)前 Stream 消息會(huì)全部忽略。
    • 從頭開(kāi)始消費(fèi):
      XGROUP CREATE mystream consumer-group-name 0-0
    • 從尾部開(kāi)始消費(fèi):
      XGROUP CREATE mystream consumer-group-name $
  • XREADGROUP GROUP :讀取消費(fèi)者組中的消息
    語(yǔ)法格式:XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] ID [ID ...]
    • group :消費(fèi)組名
    • consumer :消費(fèi)者名
    • count : 讀取數(shù)量
    • milliseconds : 阻塞毫秒數(shù)
    • key : 隊(duì)列名
    • ID : 消息 ID
  • XACK :將消息標(biāo)記為"已處理"
  • XGROUP SETID :為消費(fèi)者組設(shè)置新的最后遞送消息ID
  • XGROUP DELCONSUMER :刪除消費(fèi)者
  • XGROUP DESTROY :刪除消費(fèi)者組
  • XPENDING :顯示待處理消息的相關(guān)信息
  • XCLAIM :轉(zhuǎn)移消息的歸屬權(quán)
  • XINFO :查看流和消費(fèi)者組的相關(guān)信息;
  • XINFO GROUPS :打印消費(fèi)者組的信息;
  • XINFO STREAM :打印流信息

1.11.3 內(nèi)部編碼

stream底層的數(shù)據(jù)結(jié)構(gòu)是radix treeRadix Tree(基數(shù)樹(shù)) 事實(shí)上就是幾乎相同是傳統(tǒng)的二叉樹(shù)。僅僅是在尋找方式上,以一個(gè)unsigned int類型數(shù)為例,利用這個(gè)數(shù)的每個(gè)比特位作為樹(shù)節(jié)點(diǎn)的推斷。能夠這樣說(shuō),比方一個(gè)數(shù)10001010101010110101010,那么依照Radix 樹(shù)的插入就是在根節(jié)點(diǎn),假設(shè)遇到0,就指向左節(jié)點(diǎn),假設(shè)遇到1就指向右節(jié)點(diǎn),在插入過(guò)程中構(gòu)造樹(shù)節(jié)點(diǎn),在刪除過(guò)程中刪除樹(shù)節(jié)點(diǎn)。

如下是一個(gè)保存了7個(gè)單詞的Radix Tree:

127.0.0.1:6379> xadd mystream * key1 128
"1576480551233-0"
127.0.0.1:6379> object encoding mystream
"unknown"

mystream 總共由 3 部分構(gòu)成:

  • 第一部分是 robj, 每個(gè) redis 對(duì)象實(shí)例都會(huì)有一個(gè)最基本的結(jié)構(gòu)來(lái)存儲(chǔ)它實(shí)際的類型, 編碼和對(duì)應(yīng)的結(jié)構(gòu)的位置
  • 第二部分是一個(gè) rax, 用作存儲(chǔ) stream ID
  • 第三部分是 listpack,rax 下的每一個(gè) key 節(jié)點(diǎn)都會(huì)把對(duì)應(yīng)的 keys 和 values 的值存在這個(gè) listpack 結(jié)構(gòu)中

總結(jié) 

到此這篇關(guān)于Redis數(shù)據(jù)類型的文章就介紹到這了,更多相關(guān)Redis數(shù)據(jù)類型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • redis實(shí)現(xiàn)共同好友的思路詳解

    redis實(shí)現(xiàn)共同好友的思路詳解

    微信朋友圈大家都玩過(guò)吧,那么朋友圈的點(diǎn)贊、評(píng)論只能看到自己好友的信息是怎么操作的呢?下面通過(guò)本文給大家分享下此功能的實(shí)現(xiàn)流程,對(duì)redis實(shí)現(xiàn)共同好友的方法感興趣的朋友一起看看吧
    2021-05-05
  • 面試常問(wèn):如何保證Redis緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性

    面試常問(wèn):如何保證Redis緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性

    在實(shí)際開(kāi)發(fā)過(guò)程中,緩存的使用頻率是非常高的,只要使用緩存和數(shù)據(jù)庫(kù)存儲(chǔ),就難免會(huì)出現(xiàn)雙寫時(shí)數(shù)據(jù)一致性的問(wèn)題,那我們又該如何解決呢
    2021-09-09
  • redis數(shù)據(jù)一致性之延時(shí)雙刪策略詳解

    redis數(shù)據(jù)一致性之延時(shí)雙刪策略詳解

    在使用redis時(shí),需要保持redis和數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性,最流行的解決方案之一就是延時(shí)雙刪策略,今天我們就來(lái)詳細(xì)刨析一下,需要的朋友可以參考下
    2023-09-09
  • 在redis中存儲(chǔ)ndarray的示例代碼

    在redis中存儲(chǔ)ndarray的示例代碼

    在Redis中存儲(chǔ)NumPy數(shù)組(ndarray)通常需要將數(shù)組轉(zhuǎn)換為二進(jìn)制格式,然后將其存儲(chǔ)為字符串,這篇文章給大家介紹了在redis中存儲(chǔ)ndarray的示例代碼,感興趣的朋友一起看看吧
    2024-02-02
  • 詳解Redis用鏈表實(shí)現(xiàn)消息隊(duì)列

    詳解Redis用鏈表實(shí)現(xiàn)消息隊(duì)列

    Redis有兩種方式實(shí)現(xiàn)消息隊(duì)列,一種是用Redis自帶的鏈表數(shù)據(jù)結(jié)構(gòu),另一種是用Redis發(fā)布/訂閱模式實(shí)現(xiàn),這篇文章先介紹鏈表實(shí)現(xiàn)消息隊(duì)列,有需要的朋友們可以參考借鑒。
    2016-09-09
  • 在Mac OS上安裝Vagrant和Docker的教程

    在Mac OS上安裝Vagrant和Docker的教程

    這篇文章主要介紹了在Mac OS上安裝Vagrant和Docker的教程,并安裝和設(shè)置Postgres和Elasticsearch和Redis,需要的朋友可以參考下
    2015-04-04
  • Redis Stat的安裝指南

    Redis Stat的安裝指南

    這篇文章主要介紹了Redis Stat的安裝指南的相關(guān)資料,需要的朋友可以參考下
    2016-04-04
  • Redis主從復(fù)制的原理分析

    Redis主從復(fù)制的原理分析

    Redis主從復(fù)制通過(guò)將數(shù)據(jù)鏡像到多個(gè)從節(jié)點(diǎn),實(shí)現(xiàn)高可用性和擴(kuò)展性,主從復(fù)制包括初次全量同步和增量同步兩個(gè)階段,為優(yōu)化復(fù)制性能,可以采用AOF持久化、調(diào)整復(fù)制超時(shí)時(shí)間、優(yōu)化網(wǎng)絡(luò)帶寬等措施,故障轉(zhuǎn)移機(jī)制依賴于Sentinel或Cluster組件
    2025-01-01
  • Redis字典實(shí)現(xiàn)、Hash鍵沖突及漸進(jìn)式rehash詳解

    Redis字典實(shí)現(xiàn)、Hash鍵沖突及漸進(jìn)式rehash詳解

    這篇文章主要介紹了Redis字典實(shí)現(xiàn)、Hash鍵沖突以及漸進(jìn)式rehash的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • 詳解Redis數(shù)據(jù)類型實(shí)現(xiàn)原理

    詳解Redis數(shù)據(jù)類型實(shí)現(xiàn)原理

    這篇文章主要介紹了Redis數(shù)據(jù)類型實(shí)現(xiàn)原理,在工作中或?qū)W習(xí)中有需要的小伙伴可以參考一下這篇文章
    2021-08-08

最新評(píng)論