溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

redis中多樣的數(shù)據(jù)類型及集群相關(guān)的知識(shí)有哪些

發(fā)布時(shí)間:2022-01-13 09:55:17 來源:億速云 閱讀:166 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫

這篇文章主要介紹了redis中多樣的數(shù)據(jù)類型及集群相關(guān)的知識(shí)有哪些的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇redis中多樣的數(shù)據(jù)類型及集群相關(guān)的知識(shí)有哪些文章都會(huì)有所收獲,下面我們一起來看看吧。

redis中多樣的數(shù)據(jù)類型及集群相關(guān)的知識(shí)有哪些

多樣的數(shù)據(jù)類型

string 類型簡單方便,支持空間預(yù)分配,也就是每次會(huì)多分配點(diǎn)空間,這樣 string 如果下次變長的話,就不需要額外的申請(qǐng)空了,當(dāng)然前提是剩余的空間夠用?!鞠嚓P(guān)推薦:Redis視頻教程】

List 類型可以實(shí)現(xiàn)簡單的消息隊(duì)列,但是注意可能存在消息丟失哦,它并不持 ACK 模式。

Hash 表有點(diǎn)像關(guān)系型數(shù)據(jù)庫,但是當(dāng) hash 表越來越大的時(shí)候,請(qǐng)注意,避免使用 hgetall 之類的語句,因?yàn)檎?qǐng)求大量的數(shù)據(jù)會(huì)導(dǎo)致redis阻塞,這樣后面的兄弟們就得等待了。

set 集合類型可以幫你做一些統(tǒng)計(jì),比如你要統(tǒng)計(jì)某天活躍的用戶,可以直接把用戶ID扔到集合里,集合支持一些騷操作,比如 sdiff 可以獲取集合之間的差集,sunion 可以獲取集合之間的并集,功能很多,但是一定需要謹(jǐn)慎,因?yàn)榕1频墓δ苁怯写鷥r(jià)的,這些操作需要耗費(fèi)一些 CPU 和IO 資源,可能會(huì)導(dǎo)致阻塞,因此大集合之間的騷操作要慎用,

zset 可以說是最閃耀的星,可以做排序,因?yàn)榭梢耘判?,因此?yīng)用場(chǎng)景挺多,比如點(diǎn)贊前xx名用戶,延時(shí)隊(duì)列等等。

bitmap 位圖的好處就是在于節(jié)省空間,特別在做一些統(tǒng)計(jì)類的方面,比如要統(tǒng)計(jì)某一天有多少個(gè)用戶簽到了并且某個(gè)用戶是否簽到了,如果不用bitmap的話,你可能會(huì)想到用set。

SADD day 1234//簽到就添加到集合
SISMEMBER day 1234//判斷1234是否簽到
SCARD day   //有多少個(gè)簽到的

set 在功能上可以滿足,但是相比bitmap的話,set要更耗費(fèi)存儲(chǔ)空間,set的底層主要是由整數(shù)集合或者 hashtable 組成,整數(shù)集合只有在數(shù)據(jù)量非常小的情況下才會(huì)使用,一般是小于512個(gè)元素,同時(shí)元素必須都是整數(shù),對(duì)于set來說,整數(shù)集合的數(shù)據(jù)更加緊湊,他們?cè)趦?nèi)存是上連續(xù)的,查詢的話只能是二分查找了,時(shí)間復(fù)雜度是O(logN),而 hashtable 就不同了,這里的 hashtable 和 redis 的5大數(shù)據(jù)類型中的hash是一樣的,只不過沒有 value 而已,value 指向個(gè) null,同時(shí)也不存在沖突,因?yàn)檫@里是集合,但是需要考慮 rehash 相關(guān)問題。ok,扯的有點(diǎn)遠(yuǎn),我們說的用戶簽到問題,在用戶非常多的情況下,set 的話肯定會(huì)用到 hashtable,hashtable 的話,其實(shí)每個(gè)元素都是個(gè) dictEntry 結(jié)構(gòu)體

typedef struct dictEntry {
    // 鍵
    void *key;
    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;
    // 指向下個(gè)哈希表節(jié)點(diǎn),形成鏈表
    struct dictEntry *next;

} dictEntry;

從這個(gè)結(jié)構(gòu)體可以看到什么呢?首先雖然值 union(沒有 value)和 next(沒有沖突)是空的,但是結(jié)構(gòu)體本身需要空間,還需要加上個(gè) key,這個(gè)占用空間是實(shí)打?qū)嵉?,而如果?bitmap 的話,一個(gè)bit位就可以代表一個(gè)數(shù)字,很省空間,我們來看看 bitmap 的方式如何設(shè)置和統(tǒng)計(jì)。

SETBIT day 1234 1//簽到
GETBIT day 1234//判斷1234是否簽到
BITCOUNT day//有多少個(gè)簽到的

bf 這是 redis4.0 之后支持的布隆過濾器 RedisBloom,但是需要單獨(dú)加載對(duì)應(yīng)的 module,當(dāng)然我們也可以基于上述的 bitmap 來實(shí)現(xiàn)自己的布隆過濾器,不過既然 redis 已經(jīng)支持了,通過 RedisBloom 可以減少我們的開發(fā)時(shí)間,布隆過濾器是干嘛的,我這里就不贅述了,直接來看看 RedisBloom 相關(guān)的用法吧。

# 可以通過docker的方式快速拉取鏡像來玩耍
docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
docker exec -it redis-redisbloom bash
redis-cli
# 相關(guān)操作
bf.reserve sign 0.001 10000
bf.add sign 99 //99這個(gè)用戶加入
bf.add exists 99//判斷99這個(gè)用戶是否存在

因?yàn)椴悸∵^濾器是存在誤判的,所有 bf 支持自定義誤判率,0.001就代表誤判率,10000 代表布隆過濾器可以存儲(chǔ)的元素個(gè)數(shù),當(dāng)實(shí)際存儲(chǔ)的元素個(gè)數(shù)超過這個(gè)值的時(shí)候,誤判率會(huì)提高。

HyperLogLog 可以用于統(tǒng)計(jì),它的優(yōu)點(diǎn)就是占用的存儲(chǔ)空間極小,只需要 12KB 的內(nèi)存就可以統(tǒng)計(jì) 2^64 個(gè)元素,那它主要統(tǒng)計(jì)什么呢?其實(shí)主要就是基數(shù)統(tǒng)計(jì),比如像 UV 這種,從功能上來說 UV 可以用 set 或者 hash 來存儲(chǔ),但是缺點(diǎn)就是耗費(fèi)存儲(chǔ),容易使之變成大 key,如果想要節(jié)省空間,bitmap 也可以,12KB 空間的 bitmap 只能統(tǒng)計(jì) 12*1024*8=98304個(gè)元素,而 HyperLogLog 卻可以統(tǒng)計(jì) 2^64 個(gè)元素,但是這么牛逼的技術(shù)其實(shí)是有誤差的,HyperLogLog 是基于概率來統(tǒng)計(jì)的,標(biāo)準(zhǔn)誤算率是 0.81%,在統(tǒng)計(jì)海量數(shù)據(jù)并且對(duì)精度要求不那么高的場(chǎng)景下,HyperLogLog 在節(jié)省空間這塊還是很優(yōu)秀的。

PFADD uv 1 2 3 //1 2 3是活躍用戶
PFCOUNT uv //統(tǒng)計(jì)

GEO 是可以應(yīng)用在地理位置的業(yè)務(wù)上,比如微信附近的人或者附近的車輛等等,先來看一下如果沒有GEO 這種數(shù)據(jù)結(jié)構(gòu),你如何知道你附近的人?首先得上報(bào)自己的地理位置信息吧,比如經(jīng)度 116.397128,緯度 39.916527,此時(shí)可以用 string、hash 數(shù)據(jù)類型存儲(chǔ),但是如果要查找你附近的人,string 和 hash 這種就無能為例了,你不可能每次都要遍歷全部的數(shù)據(jù)來判斷,這樣太耗時(shí)了,當(dāng)然你也不可能通過 zset 這種數(shù)據(jù)結(jié)構(gòu)來把經(jīng)緯度信息當(dāng)成權(quán)重,但是如果我們能把經(jīng)緯度信息通過某種方式轉(zhuǎn)換成一個(gè)數(shù)字,然后當(dāng)成權(quán)重好像也可以,這時(shí)我們只需通過zrangebyscore key v1 v2也可以找到附近的人。真的需要這么麻煩嗎?于是 GEO 出現(xiàn)了,GEO 轉(zhuǎn)換經(jīng)緯度為數(shù)字的方法是“二分區(qū)間,區(qū)間編碼”,這是什么意思呢?以經(jīng)度為例,它的范圍是[-180,180],如果要采用3位編碼值,那么就是需要二分3次,二分后落在左邊的用0表示,右邊的用1表示,以經(jīng)度是121.48941 來說,第一次是在[0,180]這個(gè)區(qū)間,因此記1,第二次是在[90,180],因此再記1,第三次是在[90,135],因此記0。緯度也是同樣的邏輯,假設(shè)此時(shí)對(duì)應(yīng)的緯度編碼后是010,最后把經(jīng)緯度合并在一起,需要注意的是經(jīng)度的每個(gè)值在偶數(shù)位,緯度的每個(gè)值在奇數(shù)位。

1 1 0   //經(jīng)度
 0 1 0  //緯度
------------
101100 //經(jīng)緯度對(duì)應(yīng)的數(shù)值

原理是這樣,我們?cè)賮砜纯?redis 如何使用 GEO:

GEOADD location 112.123456 41.112345 99 //上報(bào)用戶99的地理位置信息
GEORADIUS location  112.123456 41.112345 1 km ASC COUNT 10 //獲取附近1KM的人

搞懂集群

生產(chǎn)環(huán)境用單實(shí)例 redis 的應(yīng)該比較少,單實(shí)例的風(fēng)險(xiǎn)在于:

  • 單點(diǎn)故障即服務(wù)故障,沒有backup

  • 單實(shí)例壓力大,又要提供讀,又要提供寫

于是我們首先想到的就是經(jīng)典的主從模式,而且往往是一主多從,這是因?yàn)榇蟛糠謶?yīng)用都是讀多寫少的情況,我們的主負(fù)責(zé)更新,從負(fù)責(zé)提供讀,就算我們的主宕機(jī)了,我們也可以選擇一個(gè)從來充當(dāng)主,這樣整個(gè)應(yīng)用依然可以提供服務(wù)。

復(fù)制過程的細(xì)節(jié)

當(dāng)一個(gè) redis 實(shí)例首次成為某個(gè)主的從的時(shí)候,這時(shí)主得把數(shù)據(jù)發(fā)給它,也就是 rdb 文件,這個(gè)過程 master 是要 fork 一個(gè)子進(jìn)程來處理的,這個(gè)子進(jìn)程會(huì)執(zhí)行 bgsave 把當(dāng)前的數(shù)據(jù)重新保存一下,然后準(zhǔn)備發(fā)給新來的從,bgsave 的本質(zhì)是讀取當(dāng)前內(nèi)存中的數(shù)據(jù)然后保存到 rdb 文件中,這個(gè)過程涉及大量的 IO,如果直接在主進(jìn)程中來處理的話,大概率會(huì)阻塞正常的請(qǐng)求,因此使用個(gè)子進(jìn)程是個(gè)明智的選擇。

那 fork 的子進(jìn)程在 bgsave 過程中如果有新的變更請(qǐng)求會(huì)怎么辦?

嚴(yán)格來說子進(jìn)程出來的一瞬間,要保存的數(shù)據(jù)應(yīng)該就是當(dāng)時(shí)那個(gè)點(diǎn)的快照數(shù)據(jù),所以是直接把當(dāng)時(shí)的內(nèi)存再復(fù)制一份嗎?不復(fù)制的話,如果這期間又有變更改怎么辦?其實(shí)這要說到寫實(shí)復(fù)制(COW)機(jī)制,首先從表象上來看內(nèi)存是一整塊空間,其實(shí)這不太好維護(hù),因此操作系統(tǒng)會(huì)把內(nèi)存分成一小塊一小塊的,也就是內(nèi)存分頁管理,一頁的大小一般是4K、8K或者16K等等,redis 的數(shù)據(jù)都是分布在這些頁面上的,出于效率問題,fork 出來的子進(jìn)程是和主進(jìn)程是共享同一塊的內(nèi)存的,并不會(huì)復(fù)制內(nèi)存,如果這期間主進(jìn)程有數(shù)據(jù)變更,那么為了區(qū)分,這時(shí)最快捷的做法就是把對(duì)應(yīng)的數(shù)據(jù)頁重新復(fù)制一下,然后主的變更就在這個(gè)新的數(shù)據(jù)頁上修改,并不會(huì)修改來的數(shù)據(jù)頁,這樣就保證了子進(jìn)程處理的還是當(dāng)時(shí)的快照。

以上說的變更是從快照的角度來考慮的,如果從數(shù)據(jù)的一致性來說,當(dāng)快照的 rdb 被從庫應(yīng)用之后,這期間的變更該如何同步給從庫?答案是緩沖區(qū),這個(gè)緩沖區(qū)叫做 replication buffer,主庫在收到需要同步的命令之后,會(huì)把期間的變更都先保存在這個(gè)緩沖區(qū)中,這樣在把 rdb 發(fā)給從庫之后,緊接著會(huì)再把 replication buffer 的數(shù)據(jù)也發(fā)給從庫,最終主從就保持了一致。

replication buffer不是萬能的補(bǔ)給劑

我們來看看 replication buffer 持續(xù)寫入的時(shí)間有多長。

  • 我們知道主從同步的時(shí)候,主庫會(huì)執(zhí)行 fork 來讓子進(jìn)程完成相應(yīng)地工作,因此子進(jìn)程從開始執(zhí)行 bgsave 到執(zhí)行完畢這期間,變更是要寫入 replication buffer 的。

  • rdb 生成好之后,需要把它發(fā)送給從庫,這個(gè)網(wǎng)絡(luò)傳輸是不是也需要耗點(diǎn)時(shí)間,這期間也是要寫入 replication buffer 的。

  • 從庫在收到 rdb 之后需要把 rdb 應(yīng)用到內(nèi)存里,這期間從庫是阻塞的,無法提供服務(wù),因此這期間也是要寫入 replication buffer 的。

replication buffer 既然是個(gè) buffer,那么它的大小就是有限的,如果說上面3個(gè)步驟中,只要有一個(gè)耗時(shí)長,就會(huì)導(dǎo)致 replication buffer 快速增長(前提是有正常的寫入),當(dāng) replication buffer 超過了限制之后就會(huì)導(dǎo)致主庫和從庫之間的連接斷開,斷開之后如果從庫再次連接上來就會(huì)導(dǎo)致重新開始復(fù)制,然后重復(fù)同樣的漫長的復(fù)制步驟,因此這個(gè) replication buffer 的大小還是很關(guān)鍵的,一般需要根據(jù)寫入的速度、每秒寫入的量和網(wǎng)絡(luò)傳輸?shù)乃俣鹊纫蛩貋砭C合判斷。

從庫網(wǎng)絡(luò)不好和主庫斷了該怎么辦?

正常來說,只要主從之間的連接建立好了,后面主庫的變更可以直接發(fā)給從庫,讓從庫直接回放,但是我們并不能保證網(wǎng)絡(luò)環(huán)境是百分百的通暢的,因此也要考慮從庫和主庫之間的斷聯(lián)問題。

應(yīng)該是在 redis2.8 以前,只要從庫斷聯(lián),哪怕只有很短的時(shí)間,后面從庫再次連接上來的時(shí)候,主庫也會(huì)直接無腦的進(jìn)行全量同步。在 2.8 版本及以后,開始支持增量復(fù)制了,增量復(fù)制的原理就是得有個(gè)緩沖區(qū)來保存變更的記錄,這里這個(gè)緩沖區(qū)叫做repl_backlog_buffer,這個(gè)緩沖區(qū)從邏輯上來說是個(gè)環(huán)形緩沖區(qū),寫滿了就會(huì)從頭開始覆蓋,所以也有大小限制。在從庫重新連接上來的時(shí)候,從庫會(huì)告訴主庫:“我當(dāng)前已經(jīng)復(fù)制到了xx位置”,主庫收到從庫的消息之后開始查看xx位置的數(shù)據(jù)是否還在 repl_backlog_buffer 中,如果在的話,直接把xx后面的數(shù)據(jù)發(fā)給從庫即可,如果不在的話,那無能為力了,只能再次進(jìn)行全量同步。

需要一個(gè)管理者

在主從模式下,如果主庫掛了,我們可以把一個(gè)從庫升級(jí)成主庫,但是這個(gè)過程是手動(dòng)的,靠人力來操作,不能使損失降到最低,還是需要一套自動(dòng)管理和選舉的機(jī)制,這就是哨兵,哨兵它本身也是個(gè)服務(wù),只不過它不處理數(shù)據(jù)的讀寫而已,它只負(fù)責(zé)管理所有的 redis 實(shí)例,哨兵每隔一段時(shí)間會(huì)和各個(gè) redis 通信(ping 操作),每個(gè) redis 實(shí)例只要在規(guī)定的時(shí)間內(nèi)及時(shí)回復(fù),就可以表明自己的立場(chǎng)。當(dāng)然哨兵本身也可能存在宕機(jī)或者網(wǎng)絡(luò)不通的情況,因此一般哨兵也會(huì)搭建個(gè)哨兵集群,這個(gè)集群的個(gè)數(shù)最好是奇數(shù),比如3個(gè)或者5這個(gè)這種,奇數(shù)的目的主要就是為了選舉(少數(shù)服從多數(shù))。

當(dāng)某個(gè)哨兵在發(fā)起 ping 后沒有及時(shí)收到 pong,那么就會(huì)把這個(gè) redis 實(shí)例標(biāo)記下線,此時(shí)它還是不是真正的下線,這時(shí)其他的哨兵也會(huì)判定當(dāng)前這個(gè)哨兵是不是真正的下線,當(dāng)大多數(shù)哨兵都認(rèn)定這個(gè) redis 是下線狀態(tài),那么就會(huì)把它從集群中踢出去,如果下線的是從庫,那么還好,直接踢出去就ok,如果是主庫還要觸發(fā)選舉,選舉也不是盲目選舉,肯定是要選出最合適的那個(gè)從來充當(dāng)新的主庫。這個(gè)最合適充當(dāng)主庫的庫,一般會(huì)按照以下優(yōu)先級(jí)來確定:

  • 權(quán)重,每個(gè)從庫其實(shí)都可以設(shè)置一個(gè)權(quán)重,權(quán)重越高的從庫會(huì)被優(yōu)先選擇

  • 復(fù)制的進(jìn)度,每個(gè)從庫復(fù)制的進(jìn)度可能是不一樣的,優(yōu)先選擇當(dāng)前和主庫數(shù)據(jù)差距最小的那個(gè)

  • 服務(wù)的 ID,其實(shí)每個(gè) redis 實(shí)例都有自己的 ID,如果以上條件都一樣,那么會(huì)選擇 ID 最小的那個(gè)庫來充當(dāng)主庫

更強(qiáng)的橫向伸縮性

主從模式解決了單點(diǎn)故障問題,同時(shí)讀寫分離技術(shù)使得應(yīng)用支撐能力更強(qiáng),哨兵模式可以自動(dòng)監(jiān)管集群,實(shí)現(xiàn)自動(dòng)選主,自動(dòng)剔除故障節(jié)點(diǎn)的能力。

正常來說只要讀的壓力越來越大,我們可以添加從庫來緩解,那如果主庫壓力很大怎么辦?這就得提到接下來要說的分片技術(shù)了,我們只需要把主庫切成幾片,部署到不同的機(jī)器上即可。這個(gè)分片就是 redis 中的概念了,當(dāng)分片的時(shí)候,redis 會(huì)默認(rèn)分成 0~16383 也就是一共 16384 個(gè)槽,然后把這些槽平均分到每個(gè)分片節(jié)點(diǎn)上就可以起到負(fù)載均衡的作用了。每個(gè) key 具體該分到哪個(gè)槽中,主要是先 CRC16 得到一個(gè) 16bit 的數(shù)字,然后這個(gè)數(shù)字再對(duì) 16384 取模即可:

crc16(key)%16384

然后客戶端會(huì)緩存槽信息,這樣每當(dāng)一個(gè) key 到來時(shí),只要通過計(jì)算就知道該發(fā)給哪個(gè)實(shí)例來處理來了。但是客戶端緩存的槽信息并不是一成不變的,比如在增加實(shí)例的時(shí)候,這時(shí)候會(huì)導(dǎo)致重新分片,那么原來客戶端緩存的信息就會(huì)不準(zhǔn)確,一般這時(shí)候會(huì)發(fā)生兩個(gè)常見的錯(cuò)誤,嚴(yán)格來說也不是錯(cuò)誤,更像一種信息,一個(gè)叫做MOVED,一個(gè)叫做ASK。moved的意思就說,原來是實(shí)例A負(fù)責(zé)的數(shù)據(jù),現(xiàn)在被遷移到了實(shí)例B,MOVED 代表的是遷移完成的,但是 ASK 代表的是正在遷移過程中,比如原來是實(shí)例A負(fù)責(zé)的部分?jǐn)?shù)據(jù),現(xiàn)在被遷移到了實(shí)例B,剩下的還在等待遷移中,當(dāng)數(shù)據(jù)遷移完畢之后 ASK 就會(huì)變成 MOVED,然后客戶端收到 MOVED 信息之后就會(huì)再次更新下本地緩存,這樣下次就不會(huì)出現(xiàn)這兩個(gè)錯(cuò)誤了。

關(guān)于“redis中多樣的數(shù)據(jù)類型及集群相關(guān)的知識(shí)有哪些”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“redis中多樣的數(shù)據(jù)類型及集群相關(guān)的知識(shí)有哪些”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI