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

聊一聊redis奇葩數(shù)據(jù)類型與集群知識

 更新時間:2022年01月06日 11:07:41   作者:康師傅  
現(xiàn)在越來越多的項目都會利用到redis,多實例redis服務(wù)比單實例要復雜的多,這里面涉及到定位、容錯、擴容等技術(shù)問題,下面這篇文章主要給大家介紹了關(guān)于redis奇葩數(shù)據(jù)類型與集群知識的相關(guān)資料,需要的朋友可以參考下

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

string 類型簡單方便,支持空間預分配,也就是每次會多分配點空間,這樣 string 如果下次變長的話,就不需要額外的申請空了,當然前提是剩余的空間夠用。

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

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

set 集合類型可以幫你做一些統(tǒng)計,比如你要統(tǒng)計某天活躍的用戶,可以直接把用戶ID扔到集合里,集合支持一些騷操作,比如 sdiff 可以獲取集合之間的差集,sunion 可以獲取集合之間的并集,功能很多,但是一定需要謹慎,因為牛逼的功能是有代價的,這些操作需要耗費一些 CPU 和IO 資源,可能會導致阻塞,因此大集合之間的騷操作要慎用,

zset 可以說是最閃耀的星,可以做排序,因為可以排序,因此應用場景挺多,比如點贊前xx名用戶,延時隊列等等。

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

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

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

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

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

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

bf 這是 redis4.0 之后支持的布隆過濾器 RedisBloom,但是需要單獨加載對應的 module,當然我們也可以基于上述的 bitmap 來實現(xiàn)自己的布隆過濾器,不過既然 redis 已經(jīng)支持了,通過 RedisBloom 可以減少我們的開發(fā)時間,布隆過濾器是干嘛的,我這里就不贅述了,直接來看看 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這個用戶加入
bf.add exists 99//判斷99這個用戶是否存在

因為布隆過濾器是存在誤判的,所有 bf 支持自定義誤判率,0.001就代表誤判率,10000 代表布隆過濾器可以存儲的元素個數(shù),當實際存儲的元素個數(shù)超過這個值的時候,誤判率會提高。

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

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

GEO 是可以應用在地理位置的業(yè)務(wù)上,比如微信附近的人或者附近的車輛等等,先來看一下如果沒有GEO 這種數(shù)據(jù)結(jié)構(gòu),你如何知道你附近的人?首先得上報自己的地理位置信息吧,比如經(jīng)度 116.397128,緯度 39.916527,此時可以用 string、hash 數(shù)據(jù)類型存儲,但是如果要查找你附近的人,string 和 hash 這種就無能為例了,你不可能每次都要遍歷全部的數(shù)據(jù)來判斷,這樣太耗時了,當然你也不可能通過 zset 這種數(shù)據(jù)結(jié)構(gòu)來把經(jīng)緯度信息當成權(quán)重,但是如果我們能把經(jīng)緯度信息通過某種方式轉(zhuǎn)換成一個數(shù)字,然后當成權(quán)重好像也可以,這時我們只需通過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]這個區(qū)間,因此記1,第二次是在[90,180],因此再記1,第三次是在[90,135],因此記0。緯度也是同樣的邏輯,假設(shè)此時對應的緯度編碼后是010,最后把經(jīng)緯度合并在一起,需要注意的是經(jīng)度的每個值在偶數(shù)位,緯度的每個值在奇數(shù)位。

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

原理是這樣,我們再來看看 redis 如何使用 GEO:

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

搞懂集群

生產(chǎn)環(huán)境用單實例 redis 的應該比較少,單實例的風險在于:

  1. 單點故障即服務(wù)故障,沒有backup
  2. 單實例壓力大,又要提供讀,又要提供寫

于是我們首先想到的就是經(jīng)典的主從模式,而且往往是一主多從,這是因為大部分應用都是讀多寫少的情況,我們的主負責更新,從負責提供讀,就算我們的主宕機了,我們也可以選擇一個從來充當主,這樣整個應用依然可以提供服務(wù)。

復制過程的細節(jié)

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

那 fork 的子進程在 bgsave 過程中如果有新的變更請求會怎么辦?

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

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

replication buffer不是萬能的補給劑

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

  1. 我們知道主從同步的時候,主庫會執(zhí)行 fork 來讓子進程完成相應地工作,因此子進程從開始執(zhí)行 bgsave 到執(zhí)行完畢這期間,變更是要寫入 replication buffer 的。
  2. rdb 生成好之后,需要把它發(fā)送給從庫,這個網(wǎng)絡(luò)傳輸是不是也需要耗點時間,這期間也是要寫入 replication buffer 的。
  3. 從庫在收到 rdb 之后需要把 rdb 應用到內(nèi)存里,這期間從庫是阻塞的,無法提供服務(wù),因此這期間也是要寫入 replication buffer 的。

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

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

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

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

需要一個管理者

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

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

  1. 權(quán)重,每個從庫其實都可以設(shè)置一個權(quán)重,權(quán)重越高的從庫會被優(yōu)先選擇
  2. 復制的進度,每個從庫復制的進度可能是不一樣的,優(yōu)先選擇當前和主庫數(shù)據(jù)差距最小的那個
  3. 服務(wù)的 ID,其實每個 redis 實例都有自己的 ID,如果以上條件都一樣,那么會選擇 ID 最小的那個庫來充當主庫

更強的橫向伸縮性

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

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

crc16(key)%16384

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

總結(jié)

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

相關(guān)文章

  • 一文搞懂Redis中String數(shù)據(jù)類型

    一文搞懂Redis中String數(shù)據(jù)類型

    string 是 redis 最基本的類型,你可以理解成與 Memcached 一模一樣的類型,一個 key 對應一個 value。今天通過本文給大家介紹下Redis中String數(shù)據(jù)類型,感興趣的朋友一起看看吧
    2022-04-04
  • Redis分布式鎖的7種實現(xiàn)

    Redis分布式鎖的7種實現(xiàn)

    這篇文章主要介紹了Redis分布式鎖的實現(xiàn)
    2022-04-04
  • 面試分析分布式架構(gòu)Redis熱點key大Value解決方案

    面試分析分布式架構(gòu)Redis熱點key大Value解決方案

    這篇文章主要為大家介紹了分布式架構(gòu)Redis熱點key大Value解決方案,以及在面試中如果遇到這類問題的分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • 微服務(wù)Spring Boot 整合 Redis 實現(xiàn)好友關(guān)注功能

    微服務(wù)Spring Boot 整合 Redis 實現(xiàn)好友關(guān)注功能

    這篇文章主要介紹了微服務(wù)Spring Boot 整合 Redis 實現(xiàn) 好友關(guān)注,本文結(jié)合示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-12-12
  • Redis的Sentinel解決方案介紹與運行機制

    Redis的Sentinel解決方案介紹與運行機制

    這篇文章主要介紹了Redis的Sentinel解決方案介紹與運行機制, Sentinel 是一款面向分布式服務(wù)架構(gòu)的輕量級流量控制組件,主要以流量為切入點,從流量控制、熔斷降級、系統(tǒng)自適應保護等多個維度來保障服務(wù)的穩(wěn)定性,需要的朋友可以參考下
    2023-07-07
  • Redis位圖bitmap操作

    Redis位圖bitmap操作

    本文主要介紹了Redis位圖bitmap操作,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • linux 常見的標識與Redis數(shù)據(jù)庫詳解

    linux 常見的標識與Redis數(shù)據(jù)庫詳解

    這篇文章主要介紹了linux 常見的標識與Redis數(shù)據(jù)庫,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • redis緩存的簡單操作(get、put)

    redis緩存的簡單操作(get、put)

    這篇文章主要介紹了redis緩存的簡單操作,包括引入jedisjar包、配置redis、RedisDao需要的一些工具等,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)Dict的實現(xiàn)方法

    Redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)Dict的實現(xiàn)方法

    這篇文章主要介紹了Redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)Dict的實現(xiàn)方法,本篇文章所述的dict在Redis中最主要的作用就是用于維護Redis數(shù)據(jù)庫中所有Key、value映射的數(shù)據(jù)結(jié)構(gòu),需要的朋友可以參考下
    2022-05-05
  • 詳解redis big key 排查思路

    詳解redis big key 排查思路

    本文主要介紹了詳解redis big key 排查思路,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06

最新評論