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

Redis的鍵String全面詳解

 更新時間:2023年06月06日 10:59:34   作者:言西早  
這篇文章主要為大家介紹了Redis的鍵String全面詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

String開篇

在介紹之前,筆者想介紹一下Redis的設(shè)計精髓,也就是其單線程設(shè)計,對于一個宣稱能抗住十萬qps的數(shù)據(jù)庫,其單線程的設(shè)計讓人不可思議,但redis開創(chuàng)的單線程設(shè)計哲學是至今其仍是KV型數(shù)據(jù)庫一方霸主的重要原因,而其驕傲的資本是其立足于內(nèi)存,而又不止步于內(nèi)存(持久化、壓縮、淘汰算法)的諸多設(shè)計。

之所以想介紹它的單線程設(shè)計,正是因為Redis的很多數(shù)據(jù)結(jié)構(gòu),都是為了阻擊單線程架構(gòu)的宿敵——阻塞而產(chǎn)生的,這也會是系列文章的一個主要脈絡(luò)

Redis是KV型的數(shù)據(jù)庫,其數(shù)據(jù)結(jié)構(gòu)代表著對應的鍵,Redis的鍵有五大類型,分別是String、List、Hash、Set、Zset,另外還有三種不常用的類型 HyperLogLog、BitMap、Geo,本文著重介紹前五種

1 字符串鍵

1.1 C語言的字符串實現(xiàn)

字符串鍵的使用和實現(xiàn)都比較簡單

Redis的底層實現(xiàn)是C語言,但是C語言的字符串具有種種弊端,對于Redis來說,最不可接受的有如下幾點:

  • 二進制不安全

C語言中用于識別字符串的函數(shù)會自動對”\0”(也就是空字符字符作出截斷,這對于二進制文本來說是極不安全的,如果有如“redis\0is\0good”的文本,那么C語言就只能識別到”redis”,這從根本上斷絕了用它來傳輸二進制文本的可能

  • 字符串長度獲取復雜度太高

C語言中并未維護獲取字符串長度的變量,每次獲取它的長度,都必須從頭到尾遍歷字符串一次,才能夠獲取它的長度,當這個字符串長度太長時,Redis的單線程會不可避免地陷入阻塞,這是Redis地設(shè)計者不希望看到的

  •  緩沖區(qū)溢出問題

C語言中的字符串拼接默認地假定在該字符串的后面有足夠的內(nèi)存以放下后來的字符串,一旦這個假定成立,就會發(fā)生緩沖區(qū)溢出,C語言處理此類問題的方式也是相當粗暴,一旦溢出發(fā)生,后面”無辜”的數(shù)據(jù)內(nèi)存就會被覆蓋掉,這對于數(shù)據(jù)庫來說是無法容忍的。

1.2 Redis的利器,SDS

SDS是Redis自己的字符串實現(xiàn),其對于以上三個問題都給出了很好的解決

我們可以通過如下代碼發(fā)現(xiàn),SDS實現(xiàn)的字符串具有更好的封裝性,顯得更面向?qū)ο罅?/p>

struct sdshdr{
        int len;//記錄字符串長度
        int free;//記錄可用空間的長度
        char buf[];//保存每一個字符
//APIs
}
  • 通過len,我們得以實現(xiàn)常數(shù)復雜度獲取長度
  • char數(shù)組保存空間,以實現(xiàn)二進制安全

而free有什么用呢?

  • 和C語言中字符串的緩沖區(qū)大小完全聽天由命不同,每一個SDS被創(chuàng)建、修改時,都會有一個等同于自身大小的緩沖區(qū)(這個空間最大為1M)
  • 有了這個緩沖區(qū),Redis得以避免頻繁的空間重分配,因為遷移字符串是一個極其消耗性能的操作,它必須找到一塊新空間,并把原來的字符串一個個搬運過去,這也會增加Redis主線程阻塞的幾率
  • 這種做法體現(xiàn)了一種在空間和時間上的權(quán)衡,拿空間換時間,其帶來的好處就是Redis空間重分配的時間大大減少

1.3 String In Action

接下來筆者想就String在技術(shù)層面和業(yè)務(wù)層面的作用來講講String的妙用

String在點贊系統(tǒng)中的應用

點贊系統(tǒng)社區(qū)功能里最常見核心的功能之一,其承載的巨大數(shù)據(jù)量又是一般的業(yè)務(wù)所不具有的,而String在此系統(tǒng)的設(shè)計中扮演了較為重要的作用,這里借用Bilibili的點贊架構(gòu)來講述String在計數(shù)功能中發(fā)揮何種作用(為了方便理解有做改動,思想不變)

  • 核心需求:點贊數(shù)目、最近點贊列表
  • 技術(shù)選型:Redis+MySQL,更新方式采用CacheAside

數(shù)據(jù)模型:

  • Redis中:

以likeCount:userId存儲某個稿件點贊的數(shù)目

key-value = likeCount:{userId}- {likes},{disLikes}
//用業(yè)務(wù)ID和該業(yè)務(wù)下的實體ID作為緩存的Key,并將點贊數(shù)與點踩數(shù)拼接起來存儲以及更新

以Zset存儲最近的點贊,但是此集合不能無限膨脹,需要剪裁,當需要更多信息時,返回DB以查詢更多

key-value = user:likes:{entityId} - member(messageID)-score(likeTimestamp)

* 用userId作為key,value則是一個ZSet,member為被點贊的實體ID,score為點贊的時間。

當改業(yè)務(wù)下某用戶有新的點贊操作的時候,被點贊的實體則會通過 zadd的方式把最新的點贊\

記錄加入到該ZSet里面來

* 為了維持用戶點贊列表的長度(不至于無限擴張),需要在每一次加入新的點贊記錄的時候,

按照固定長度裁剪用戶的點贊記錄緩存。該設(shè)計也就代表用戶的點贊記錄在緩存中是有限度

長度的,超過該長度的數(shù)據(jù)請求需要回源DB查詢MySQL中:

有人會問了,Redis的效率極高,還支持持久化,為何我不采用set或者Zset以存在Redis里?這對于熱點的like數(shù)據(jù)不是更好嗎?

  • 點贊記錄表 - likes : 每一次的點贊記錄(用戶userId、被點贊的實體ID(entityId)、點贊來源、時間)等信息,并且在userId、entityId兩個維度上建立了滿足業(yè)務(wù)求的聯(lián)合索引。
  • 點贊數(shù)表 - counts :以實體ID(entityID)為主鍵,聚合了該實體的點贊數(shù)、點踩數(shù)等信息。并且按照entityID維度建立滿足業(yè)務(wù)查詢的索引。

Why Not Set or Zset?

  • 首先說說Set實現(xiàn)like有什么問題,首當其沖的是用無序的set存幾乎沒有任何拓展性,比較經(jīng)典的實現(xiàn)是,在以like:{entityId}的set鍵里存儲userId,在調(diào)用它isMember以查看是否點贊過,以及其大小時,可以獲得風馳電掣的速度,但在面對諸如點贊列表、分析用戶在時間尺度上的點贊行為等業(yè)務(wù)需求上,set顯得無能為力

而對于Zset,似乎是實現(xiàn)此結(jié)構(gòu)的天然首選

Member-存entityId
Score-存時間戳
點贊列表-求zset的最后n項即可

其排序和查找特性似乎是為了上述的需求量身定制,然而Zset的問題比set還要糟糕,set僅僅是業(yè)務(wù)拓展能力不足,Zset作為點贊的容器極有可能引起redis主線程的阻塞:

  • Zset對于isMember的查詢是O(N)的,也就是說無異于去遍歷整個鏈表,這對于業(yè)務(wù)量很可能在十幾萬甚至上百萬的點贊上來說,是不可接受的開銷
  • 還是數(shù)據(jù)量的問題,對于每個實體來說都要存上十萬個實體id與它們的時間戳,這樣的實體可能還有上萬個,這對Redis是無法承受之重,畢竟Redis也不是專門服務(wù)你點贊業(yè)務(wù)的(這個問題在Set中同樣存在)

String在分布式鎖中的應用

SET 命令有個 NX 參數(shù)可以實現(xiàn)「key不存在才插入」,可以用它來實現(xiàn)分布式鎖:

這個命令是:

加鎖

SET {加鎖的鍵} {客戶端標識} NX PX {持有鎖的最大時間}’

  • 如果 key 不存在,則顯示插入成功,可以用來表示加鎖成功
  • 如果 key 存在,則會顯示插入失敗,可以用來表示加鎖失敗
  • 客戶端標識用于表示此鎖的擁有權(quán)
  • PX 10000表示此鎖在10000秒內(nèi)失效,防止發(fā)生異常產(chǎn)生無法釋放鎖的情況

釋放鎖的過程就是刪除此鍵,讓我們回想一下CAS思路下的替換操作

這顯然是兩步操作,需要用LUA腳本來進行原子化,具體的邏輯如下

// 釋放鎖時,先比較 unique_value 是否相等,避免鎖的誤釋放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
  • 首先是確認此鎖屬于自己(客戶端id、是否是原值,等等形式)
  • 若是屬于自己,再對其進行改動,如釋放、釋放后通知在等待鎖的線程(此例子中是刪除此鍵)
  • 其他的應用
  • 共享Session、JSON對象、單點過濾,此處不再一一贅述。

總結(jié)

作為KV型數(shù)據(jù)庫中最簡單的數(shù)據(jù)結(jié)構(gòu),SDS的功用僅僅是為Redis的強大開了個頭,但即便是如此簡單的結(jié)構(gòu),我們?nèi)阅茉谄渲锌匆娫S多其為了避免Redis陷入阻塞噩夢的巧思,在接下來的介紹中我們能看見更多的Redis的精妙設(shè)計與實現(xiàn)

參考資料

《Redis設(shè)計與實現(xiàn)》

《Redis開發(fā)與運維》

以上就是Redis的鍵String全面詳解的詳細內(nèi)容,更多關(guān)于Redis鍵String的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論