標(biāo)簽: Redis Redis數(shù)據(jù)結(jié)構(gòu) Redis內(nèi)存管理策略 Redis數(shù)據(jù)類型 Redis類型映射
成都創(chuàng)新互聯(lián)公司是專業(yè)的銅鼓網(wǎng)站建設(shè)公司,銅鼓接單;提供網(wǎng)站設(shè)計(jì)制作、網(wǎng)站制作,網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行銅鼓網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
redis為我們提供了 5種數(shù)據(jù)類型,基本上我們使用頻率最高的就是 string,而對(duì)其他四種數(shù)據(jù)類型使用的頻次稍弱于 string。
一方面是由于 string使用起來比較簡單,可以方便存儲(chǔ)復(fù)雜大對(duì)象,使用場景比較多。還有一個(gè)原因就是由于 redis expire time只能設(shè)置在 key上,像 list、hash、set、zset屬于集合類型,會(huì)管理一組 item,我們無法在這些集合的 item上設(shè)置過期時(shí)間,所以使用 expire time來處理集合的 cache失效會(huì)變得稍微復(fù)雜些。但是 string使用 expire time來管理過期策略會(huì)比較簡單,因?yàn)樗捻?xiàng)少。這里說的集合是寬泛的類似集合。
導(dǎo)致我們習(xí)慣性的使用 string而忽視其他四種數(shù)據(jù)類型的另一個(gè)深層次原因,大多是由于我們對(duì)另外四種數(shù)據(jù)類型的使用和原理不是太了解。這個(gè)時(shí)候往往會(huì)忽視在特定場景下使用某種數(shù)據(jù)類型可能會(huì)比 string性能高出很多,比如使用 hash結(jié)構(gòu)來提高某個(gè)實(shí)體的某個(gè)項(xiàng)的修改等。
這里我們不打算羅列這 5種數(shù)據(jù)類型的使用方法,這些資料網(wǎng)上有很多。我們主要討論這 5種數(shù)據(jù)類型的功能特點(diǎn),這些特點(diǎn)分別適合用于處理哪些現(xiàn)實(shí)的業(yè)務(wù)場景,最重要的是我們?nèi)绾谓M合性的使用這 5種數(shù)據(jù)類型來解決復(fù)雜的 cache問題。
string是 redis提供的字符串類型??梢葬槍?duì) string類型獨(dú)立設(shè)置 expire time。通常用來存儲(chǔ)長字符串?dāng)?shù)據(jù),比如,某個(gè)對(duì)象的 json字符串。
string類型我們?cè)谑褂蒙献钋擅畹氖强梢詣?dòng)態(tài)拼接 key。通常我們可以將一組 id放在 set里,然后動(dòng)態(tài)查找 string還是否存在,如果不存在說明已經(jīng)過期或者由于數(shù)據(jù)修改主動(dòng) delete了,需要再做一次 cache數(shù)據(jù) load。
雖然 set無法設(shè)置 item的過期時(shí)間,但是我們可以將 set item與 string key關(guān)聯(lián)來達(dá)到相同的效果。
上圖中的左邊是一個(gè) key為 set:order:ids的 set集合,它可能是一個(gè)全量集合,也可能是某個(gè)查詢條件獲取出來的一個(gè)集合。
有時(shí)候復(fù)雜點(diǎn)的場景需要多個(gè) set集合來支撐計(jì)算,在 redis 服務(wù)器里可能會(huì)有很多類似這樣的集合。
這些集合我們可以稱為 功能數(shù)據(jù),這些數(shù)據(jù)是用來輔助 cache計(jì)算的,當(dāng)進(jìn)行各種集合運(yùn)算之后會(huì)得出當(dāng)前查詢需要返回的子集,最后我們才會(huì)去獲取某個(gè)訂單真正的數(shù)據(jù)。
這些 string:order:{orderId}字符串 key并不一定是為了服務(wù)一種場景,而是整個(gè)系統(tǒng)最底層的數(shù)據(jù),各種場景最后都需要獲取這些數(shù)據(jù)。那些 set集合可以認(rèn)為是查詢條件數(shù)據(jù),用來輔助查詢條件的計(jì)算。
redis為我們提供了 TYPE命令來查看某個(gè) key的數(shù)據(jù)類型,如:string類型:
SET string:order:100 order-100
TYPE string:order:100
string
list在提高 throughput的場景中非常適用,因?yàn)樗赜械?LPUSH、RPUSH、LPOP、RPOP功能可以無縫的支持生產(chǎn)者、消費(fèi)者架構(gòu)模式。
這非常適合實(shí)現(xiàn)類似 Java ConcurrencyFork/Join框架中的 work-stealing 算法 (工作竊取)。
java fork/join框架使用并行來提高性能,但是會(huì)帶來由于并發(fā) take task帶來的 race condition (競態(tài)條件)問題,所以采用 work-stealing 算法來解決由于競爭問題帶來的性能損耗。
上圖中模擬了一個(gè)典型的支付 callback峰值場景。在峰值出現(xiàn)的地方一般我們都會(huì)使用加 buffer的方式來加快請(qǐng)求處理速度,這樣才能提高并發(fā)處理能力,提高 throughput。
支付gateway收到 callback之后不做任何處理直接交給 分發(fā)器。分發(fā)器是一個(gè)無狀態(tài)的 cluster,每個(gè) node通過向 注冊(cè)中心pull handler queue list,也就是獲取下游處理器注冊(cè)到注冊(cè)中心里的消息通道。
每一個(gè)分發(fā)器 node會(huì)維護(hù)一個(gè)本地 queue list,然后順序推送消息到這些 queue list即可。這里會(huì)有點(diǎn)小問題,就是 支付gateway調(diào)用分發(fā)器的時(shí)候是如何做 load balance,如果不是平均負(fù)載可能會(huì)有某個(gè) queue list高出其他 queue list。
而分發(fā)器不需要做 soft load balance,因?yàn)槟呐履硞€(gè) queue list比其他 queue list多也無所謂,因?yàn)橄掠?message handler會(huì)根據(jù) work-stealing算法來竊取其他消費(fèi)慢的 queue list。
redis list 的 LPUSH、RPUSH、LPOP、RPOP特性確實(shí)可以在很多場景下提高這種橫向擴(kuò)展計(jì)算能力。
hash數(shù)據(jù)類型很明顯是基于 hash算法的,對(duì)于項(xiàng)的查找時(shí)間復(fù)雜度是 O(1)的,在極端情況下可能出現(xiàn)項(xiàng) hash沖突問題,redis內(nèi)部是使用鏈表加 key判斷來解決的。具體 redis內(nèi)部的數(shù)據(jù)結(jié)構(gòu)我們?cè)诤竺嬗薪榻B,這里就不展開了。
hash數(shù)據(jù)類型的特點(diǎn)通??梢杂脕斫鉀Q帶有映射關(guān)系,同時(shí)又需要對(duì)某些項(xiàng)進(jìn)行更新或者刪除等操作。如果不是某個(gè)項(xiàng)需要維護(hù),那么一般可以通過使用 string來解決。
如果有需要對(duì)某個(gè)字段進(jìn)行修改,使用 string很明顯是會(huì)多出很多開銷,需要讀取出來反序列化成對(duì)象然后操作,然后再序列化寫回 redis,這中間可能還有并發(fā)問題。
那我們可以使用 redis hash提供的實(shí)體屬性 hash存儲(chǔ)特性,我們可以認(rèn)為 hash value是一個(gè) hash table,實(shí)體的每一個(gè)屬性都是通過 hash得到屬性的最終數(shù)據(jù)索引。
上圖使用 hash數(shù)據(jù)類型來記錄頁面的 a/b metrics,左邊的是首頁 index的各個(gè)區(qū)域的統(tǒng)計(jì),右邊是營銷 marketing的各個(gè)區(qū)域統(tǒng)計(jì)。
在程序里我們可以很方便的使用 redis的 atomic特性對(duì) hash某個(gè)項(xiàng)進(jìn)行累加操作。
HMSET hash:mall:page:ab:metrics:index topbanner 10 leftbanner 5 rightbanner 8 bottombanner 20 productmore 10 topshopping 8
OK
HGETALL hash:mall:page:ab:metrics:index
1) "topbanner"
2) "10"
3) "leftbanner"
4) "5"
5) "rightbanner"
6) "8"
7) "bottombanner"
8) "20"
9) "productmore"
10) "10"
11) "topshopping"
12) "8"
HINCRBY hash:mall:page:ab:metrics:index topbanner 1
(integer) 11
使用 redis hash increment進(jìn)行原子增加操作。HINCRBY命令可以原子增加任何給定的整數(shù),也可以通過 HINCRBYFLOAT來原子增加浮點(diǎn)類型數(shù)據(jù)。
set集合數(shù)據(jù)類型可以支持集合運(yùn)算,不能存儲(chǔ)重復(fù)數(shù)據(jù)。
set最大的特點(diǎn)就是集合的計(jì)算能力,inter 交集、union 并集、diff 差集,這些特點(diǎn)可以用來做高性能的交叉計(jì)算或者剔除數(shù)據(jù)。
set集合在使用場景上還是比較多和自由的。舉個(gè)簡單的例子,在應(yīng)用系統(tǒng)中比較常見的就是商品、活動(dòng)類場景。用一個(gè) set緩存有效商品集合,再用一個(gè) set緩存活動(dòng)商品集合。如果商品出現(xiàn)上下架操作只需要維護(hù)有效商品 set,每次獲取活動(dòng)商品的時(shí)候需要過濾下是否有下架商品,如果有就需要從活動(dòng)商品中剔除。
當(dāng)然,下架的時(shí)候可以直接刪除緩存的活動(dòng)商品,但是活動(dòng)是從 marketing系統(tǒng)中 load出來的,就算我將 cache里的活動(dòng)商品刪除,當(dāng)下次再從 marketing系統(tǒng)中 load活動(dòng)商品時(shí)候還是會(huì)有下架商品。當(dāng)然這只是舉例,一個(gè)場景有不同的實(shí)現(xiàn)方法。
上圖中左右兩邊是兩個(gè)不同的集合,左邊是營銷域中的可用商品ids集合,右邊是營銷域中活動(dòng)商品ids集合,中間計(jì)算出兩個(gè)集合的交集。
SADD set:marketing:product:available:ids 1000100 1000120 1000130 1000140 1000150 1000160
SMEMBERS set:marketing:product:available:ids
1) "1000100"
2) "1000120"
3) "1000130"
4) "1000140"
5) "1000150"
6) "1000160"
SADD set:marketing:activity:product:ids 1000100 1000120 1000130 1000140 1000200 1000300
SMEMBERS set:marketing:activity:product:ids
1) "1000100"
2) "1000120"
3) "1000130"
4) "1000140"
5) "1000200"
6) "1000300"
SINTER set:marketing:product:available:ids set:marketing:activity:product:ids
1) "1000100"
2) "1000120"
3) "1000130"
4) "1000140"
在一些復(fù)雜的場景中,也可以使用 SINTERSTORE命令將交集計(jì)算后的結(jié)果存儲(chǔ)在一個(gè)目標(biāo)集合中。 這在使用 pipeline命令管道中特別有用,將 SINTERSTORE命令包裹在 pipeline命令串中可以重復(fù)使用計(jì)算出來的結(jié)果集。
由于 redis是 Signle-Thread 單線程模型,基于這個(gè)特性我們就可以使用 redis提供的 pipeline 管道來提交一連串帶有邏輯的命令集合,這些命令在處理期間不會(huì)被其他客戶端的命令干擾。
zset排序集合與 set集合類似,但是 zset提供了排序的功能。在介紹 set集合的時(shí)候我們知道 set集合中的成員是無序的,zset填補(bǔ)了集合可以排序的空隙。
zset最強(qiáng)大的功能就是可以根據(jù)某個(gè) score 比分值進(jìn)行排序,這在很多業(yè)務(wù)場景中非常急需。比如,在促銷活動(dòng)里根據(jù)商品的銷售數(shù)量來排序商品,在旅游景區(qū)里根據(jù)流入人數(shù)來排序熱門景點(diǎn)等。
基本上人們?cè)谧鋈魏问虑槎夹枰鶕?jù)某些條件進(jìn)行排序。
其實(shí) zset在我們應(yīng)用系統(tǒng)中能用到地方到處都是,這里我們舉一個(gè)簡單的例子,在團(tuán)購系統(tǒng)中我們通常需要根據(jù)參團(tuán)人數(shù)來排序成團(tuán)列表,大家都希望參加那些即將成團(tuán)的團(tuán)。
上圖是一個(gè)根據(jù)團(tuán)購code創(chuàng)建的zset,score 分值就是參團(tuán)人數(shù)累加和。
ZADD zset:marketing:groupon:group:codes 5 G_PXYJY9QQFA 8 G_4EXMT6NZJQ 20 G_W7BMF5QC2P 10 G_429DHBTGZX 8 G_KHZGH9U4PP
ZREVRANGEBYSCORE zset:marketing:groupon:group:codes 1000 0
1) "G_W7BMF5QC2P"
2) "G_ZMZ69HJUCB"
3) "G_429DHBTGZX"
4) "G_KHZGH9U4PP"
5) "G_4EXMT6NZJQ"
6) "G_PXYJY9QQFA"
ZREVRANGEBYSCORE zset:marketing:groupon:group:codes 1000 0 withscores
1) "G_W7BMF5QC2P"
2) "20"
3) "G_ZMZ69HJUCB"
4) "10"
5) "G_429DHBTGZX"
6) "10"
7) "G_KHZGH9U4PP"
8) "8"
9) "G_4EXMT6NZJQ"
10) "8"
11) "G_PXYJY9QQFA"
12) "5"
zset本身提供了很多方法用來進(jìn)行集合的排序,如果需要 score分值可以使用 withscore字句帶出每一項(xiàng)的分值。
在一些比較特殊的場合可能需要組合排序,可能有多個(gè) zset分別用來對(duì)同一個(gè)實(shí)體在不同維度的排序,按時(shí)間排序、按人數(shù)排序等。這個(gè)時(shí)候就可以組合使用 zset帶來的便捷性,利用 pipeline再結(jié)合多個(gè) zset最終得出組合排序集合。
我們總結(jié)了 redis提供的 5種數(shù)據(jù)類型的各自特點(diǎn)和一般的使用場景。但是我們不僅僅可以分開使用這些數(shù)據(jù)類型,我們完全可以綜合使用這些數(shù)據(jù)類型來完成復(fù)雜的 cache場景。
下面我們分享一個(gè)使用多個(gè) zset、string來優(yōu)化 團(tuán)購系統(tǒng)前臺(tái)接口的例子。由于篇幅和時(shí)間限制,這里只介紹跟本次案例相關(guān)的信息。
hot-top接口是指熱點(diǎn)、排名接口的意思,表示它的瀏覽量、并發(fā)量比較高,一般大促的時(shí)候都會(huì)有幾個(gè)這種性能要求比較高的接口。
我們先來分析一個(gè)查詢接口所包含的常規(guī)信息。
首先一個(gè)查詢接口肯定是有 query condition 查詢條件,然后是 sort 排序信息 、最后是page 分頁信息。這是一般接口所承擔(dān)的基本職責(zé),當(dāng)然,特殊場景下還需要支持 master/slave replication時(shí)關(guān)于數(shù)據(jù) session 一致性的要求,需要提供跟蹤標(biāo)記來回 master查詢數(shù)據(jù),這里就不展開了。
我們可以抽象出這幾個(gè)維度的信息:
query condition
查詢條件,companyid=100,sellerid=1010101 諸如此類。
sort
排序信息,一般是默認(rèn)一個(gè)列排序,但是在復(fù)雜的場景下會(huì)有可能讓接口使用者定制排序字段,比如一些租戶信息列。
page
分頁信息,簡單理解就是數(shù)據(jù)記錄排完序之后的第幾行到第幾行。
由于這里我們純粹用 redis來提高 cache能力,不涉及到有關(guān)于任何搜索的能力,所以這里忽略其他復(fù)雜查詢的情況。其實(shí)我們?cè)趶?fù)雜的地方使用了 elastcsearch來提高搜索能力。
上述我們分析總結(jié)出了一個(gè)查詢接口的基本信息,這里還有一個(gè)有關(guān)于高并發(fā)接口的設(shè)計(jì)原則就是將 hot-top接口和一般 search接口分離開,因?yàn)橹挥蟹侄沃拍芊謩e根據(jù)特點(diǎn)選用不同的技術(shù)。如果我們不分職責(zé)將所有的查詢場景封裝在一個(gè)接口里,那么在后面優(yōu)化接口性能的時(shí)候基本就很麻煩了,有些場景是無法或者很難用 cache來解決的,因?yàn)榻涌诶锺詈狭烁鞣N場景邏輯,就算勉強(qiáng)能實(shí)現(xiàn)性能也不會(huì)高。
前面做這些鋪墊是為了能在介紹案例的時(shí)候達(dá)成一個(gè)基本的共識(shí)?,F(xiàn)在我們來看下這個(gè)團(tuán)購系統(tǒng)的 hot-top接口的具體邏輯。
在大促的時(shí)候需要展現(xiàn)團(tuán)購列表,這個(gè)接口的訪問量是非常大的,團(tuán)購活動(dòng)需要根據(jù)參團(tuán)人數(shù)倒序排序,并且分頁返回指定數(shù)量的團(tuán)列表。
我們假設(shè)這個(gè)接口名為 getTopGroups(getTopGroupsRequest request)
我們來仔細(xì)分析下,首先不同的查詢條件從 DB里查詢出來的數(shù)據(jù)是不一樣的,也就是說查詢出來的團(tuán)列表是不一樣的,可能有 company 公司、channel 渠道等過濾條件。由于一個(gè)團(tuán)購活動(dòng)下不會(huì)有太多團(tuán),頂多上百個(gè)是極限了,所以一個(gè)查詢條件出來的團(tuán)列表也頂多幾十個(gè),而且根據(jù)場景分析熱點(diǎn)查詢條件不會(huì)超過十個(gè),所以我們選擇將 查詢條件 hash出一個(gè) code來緩存本次查詢條件的全量團(tuán)列表集合,但是這些結(jié)果集是沒有任何排序的。
再看根據(jù)參團(tuán)人數(shù)排序問題,我們立刻就可以想到使用 zset來處理團(tuán)排序問題,因?yàn)橹挥幸粋€(gè)排序維度,所以一個(gè) zset就夠了。我們使用一個(gè) zset來緩存所有團(tuán)的參團(tuán)人數(shù)集合,它是一個(gè)全量的團(tuán)排序集合。
那么我們?nèi)绾螌⒂脩舻牟樵儣l件出來的團(tuán)列表根據(jù)參團(tuán)人數(shù)排序尼,剛好可以使用 zset的交集運(yùn)算直接計(jì)算出當(dāng)前這個(gè)集合的 zset子集。
通過對(duì)已經(jīng)排序之后的團(tuán)列表 zset使用 zrange來獲取出分頁集合。
我們來看下完整的流程,如何處理查詢、排序、分頁的。
上圖從 query condition計(jì)算 hash code,然后通過 DB查詢出當(dāng)前條件全量團(tuán)列表。
zset:marketing:groupon:hottop:available:group key表示全量團(tuán)的參團(tuán)人數(shù),用一個(gè) zset來緩存。接著將這兩個(gè) zset計(jì)算交集,就可以得出當(dāng)前查詢所需要的帶有參團(tuán)人數(shù)的 zset,最后在使用 zrevrange獲取分頁區(qū)間。
ZADD zset:marketing:groupon:hottop:condition:2986080 0 G4ZD5732YZQ 0 G5VW3YF42UC 0 GF773FEJ7CC 0 GFW8DUEND8S 0 GKPKKW8XEY9 0 GL324DGWMZM
(integer) 6
ZADD zset:marketing:groupon:hottop:available:group 5 GN7KQH36ZWK 10 GS7VB22AWD4 15 GF773FEJ7CC 17 G5VW3YF42UC 18 G4ZD5732YZQ 32 GTYJKCEJBRR 40 GKPKKW8XEY9 45 GL324DGWMZM 50 GFW8DUEND8S 60 GYTKY4ACWLT
(integer) 10
ZINTERSTORE zset:marketing:groupon:hottop:condition:interstore 2 zset:marketing:groupon:hottop:condition:2986080 zset:marketing:groupon:hottop:available:group
(integer) 6
ZRANGE zset:marketing:groupon:hottop:condition:interstore 0 -1 withscores
1) "GF773FEJ7CC"
2) "15"
3) "G5VW3YF42UC"
4) "17"
5) "G4ZD5732YZQ"
6) "18"
7) "GKPKKW8XEY9"
8) "40"
9) "GL324DGWMZM"
10) "45"
11) "GFW8DUEND8S"
12) "50"
ZREVRANGE zset:marketing:groupon:hottop:condition:interstore 2 4 withscores
1) "GKPKKW8XEY9"
2) "40"
3) "G4ZD5732YZQ"
4) "18"
5) "G5VW3YF42UC"
6) "17"
有了返回的團(tuán) code集合之后就可以通過 mget來批量獲取 string類型的團(tuán)詳情信息,這里就不貼出代碼了。
由于篇幅和時(shí)間關(guān)系,這里就不展開太多的業(yè)務(wù)場景介紹了。這其中還涉及到計(jì)算 cache過期時(shí)間的問題,這也跟促銷活動(dòng)的運(yùn)營規(guī)則有關(guān)系,還涉及到有可能 query condition hash沖突問題等,但是這些已經(jīng)不與我們本節(jié)主題相關(guān)。
我們已經(jīng)了解了 redis提供的 5種數(shù)據(jù)類型,那么 redis內(nèi)部到底是如何支持這 5種數(shù)據(jù)類型的,也就是說 redis到底是使用什么樣的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)、查找我們?cè)O(shè)置在內(nèi)存中的數(shù)據(jù)。
雖然我們使用 5種數(shù)據(jù)類型來緩存數(shù)據(jù),但是 redis會(huì)根據(jù)我們存儲(chǔ)數(shù)據(jù)的不同而選用不同的數(shù)據(jù)結(jié)構(gòu)和編碼。
我們?nèi)粘J褂玫氖?redis提供的 5種數(shù)據(jù)類型,但是這 5種數(shù)據(jù)類型在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)和編碼有很多種。隨著我們存儲(chǔ)的數(shù)據(jù)類型的不同、數(shù)據(jù)量的大小不同都會(huì)引起內(nèi)存數(shù)據(jù)結(jié)構(gòu)的動(dòng)態(tài)調(diào)整。
本節(jié)只是做數(shù)據(jù)結(jié)構(gòu)和編碼的一般性介紹,不做過多細(xì)節(jié)討論,一方面是關(guān)于 redis源碼分析的資料網(wǎng)上有很多,還有一個(gè)原因就是 redis每一個(gè)版本的實(shí)現(xiàn)有很大差異,一旦展開細(xì)節(jié)討論每一個(gè)點(diǎn)每一個(gè)數(shù)據(jù)結(jié)構(gòu)都會(huì)很復(fù)雜,所以我們這里就不展開討論這些,只是起到拋磚引玉作用。
我們知道使用 type命令可以查看某個(gè) key是否是 5種數(shù)據(jù)類型之一,但是當(dāng)我們想查看某個(gè) key底層是使用哪種數(shù)據(jù)結(jié)構(gòu)和編碼來存儲(chǔ)的時(shí)候可以使用 OBJECT encoding命令。
SET string:orderid:10101010 10101010
OK
OBJECT encoding string:orderid:10101010
"int"
SET string:orderid:10101010 "orderid:10101010"
OK
OBJECT encoding string:orderid:10101010
"embstr"
同樣一個(gè) key,但是由于我們?cè)O(shè)置的值不同而 redis選用了不同的內(nèi)存數(shù)據(jù)結(jié)構(gòu)和編碼。雖然 redis提供的 string數(shù)據(jù)類型,但是 redis會(huì)自動(dòng)識(shí)別我們 cache的數(shù)據(jù)類型是 int還是 string。
如果我們?cè)O(shè)置的是字符串,且這個(gè)字符串長度不大于 39字節(jié)那么將使用 embstr來編碼,如果大于 39字節(jié)將使用 raw來編碼。redis 4.0將這個(gè)閥值擴(kuò)大了 45個(gè)字節(jié)。
除了使用 OBJECT encoding命令外,我們還可以使用 DEBUG OBJECT命令來查看更多詳細(xì)信息。
DEBUG OBJECT string:orderid:10101010
Value at:0x7fd190500210 refcount:1 encoding:int serializedlength:5 lru:6468044 lru_seconds_idle:8
DEBUG OBJECT string:orderid:10101010
Value at:0x7fd19043be60 refcount:1 encoding:embstr serializedlength:17 lru:6465804 lru_seconds_idle:1942
DEBUG OBJECT能看到這個(gè)對(duì)象的 refcount 引用計(jì)數(shù)、serializedlength 長度、_lru_secondsidle 時(shí)間,這些信息決定了這個(gè) key緩存清除策略。
簡單動(dòng)態(tài)字符串簡稱 SDS,在 redis中所有涉及到字符串的地方都是使用 SDS實(shí)現(xiàn),當(dāng)然這里不包括字面量。 SDS與傳統(tǒng) C字符串的區(qū)別就是 SDS是結(jié)構(gòu)化的,它可以高效的處理分配、回收、長度計(jì)算等問題。
struct sdshdr {
unsigned int len;
unsigned int free;
char buf[];
};
這是 redis 3.0版本的 sds.h頭文件定義,3.0.0之后變化比較大。len表示字符串長度,free表示空間長度,buf數(shù)組表示字符串。
SDS有很多優(yōu)點(diǎn),比如,獲取長度的時(shí)間復(fù)雜度 O(1),不需要遍歷所有 char buf[]組數(shù),直接返回 len值。
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->len;
}
當(dāng)然還有空間分配檢查、空間預(yù)分配、空間惰性釋放等,這些都是 SDS結(jié)構(gòu)化字符串帶來的強(qiáng)大的擴(kuò)展能力。
鏈表數(shù)據(jù)結(jié)構(gòu)我們是比較熟悉的,最大的特點(diǎn)就是節(jié)點(diǎn)的增、刪非常靈活。redis List數(shù)據(jù)類型底層就是基于鏈表來實(shí)現(xiàn)。這是 redis 3.0實(shí)現(xiàn)。
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned long len;
} list;
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
在 redis 3.2.0版本的時(shí)候引入了 quicklist鏈表結(jié)構(gòu),結(jié)合了 linkedlist和 ziplist的優(yōu)勢。
typedef struct quicklist {
quicklistNode *head;
quicklistNode *tail;
unsigned long count; /* total count of all entries in all ziplists */
unsigned int len; /* number of quicklistNodes */
int fill : 16; /* fill factor for individual nodes */
unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
typedef struct quicklistNode {
struct quicklistNode *prev;
struct quicklistNode *next;
unsigned char *zl;
unsigned int sz; /* ziplist size in bytes */
unsigned int count : 16; /* count of items in ziplist */
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
quicklist提供了靈活性同時(shí)也兼顧了 ziplist的壓縮能力,quicklist->encoding指定了兩種壓縮算法。 quicklist->compress表示我們可以進(jìn)行 quicklist node的深度壓縮能力。redis 提供了兩個(gè)有關(guān)于壓縮的配置。
list-max-ziplist-size:ziplist長度控制
list-compress-depth:控制鏈表兩端節(jié)點(diǎn)的壓縮個(gè)數(shù),越是靠近兩端的節(jié)點(diǎn)被訪問的機(jī)率越大,所以可以將訪問機(jī)率大的節(jié)點(diǎn)不壓縮,其他節(jié)點(diǎn)進(jìn)行壓縮
對(duì)比 redis 3.2的 quicklist與 redis 3.0,很明顯 quicklist提供了更加豐富的壓縮功能。redis 3.0的版本是每個(gè) listnode直接緩存值,而 quicklistnode還有強(qiáng)大的有關(guān)于壓縮能力。
LPUSH list:products:mall 100 200 300
(integer) 3
OBJECT encoding list:products:mall
"quicklist"
網(wǎng)頁標(biāo)題:Redis數(shù)據(jù)結(jié)構(gòu)與內(nèi)存管理策略(上)
文章鏈接:http://www.jbt999.com/article32/iieppc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)、企業(yè)建站、Google、營銷型網(wǎng)站建設(shè)、小程序開發(fā)、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:[email protected]。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)