Redis 數(shù)據(jù)結(jié)構(gòu)與內(nèi)存管理策略(下)

字典(dict)

dict?字典是基于?hash算法?來(lái)實(shí)現(xiàn),是?Hash?數(shù)據(jù)類(lèi)型的底層存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)。我們來(lái)看下?redis 3.0.0?版本的?dict.h?頭文件定義。

typedefstructdict{dictType *type;void*privdata;? ? dictht ht[2];longrehashidx;intiterators; } dict;

typedefstructdictht{dictEntry **table;unsignedlongsize;unsignedlongsizemask;unsignedlongused;} dictht;

typedefstructdictEntry{void*key;union{void*val;uint64_tu64;int64_ts64;doubled;? ? } v;structdictEntry*next;} dictEntry;

說(shuō)到?hash table?有兩個(gè)東西是我們經(jīng)常會(huì)碰到的,首先就是?hash 碰撞?問(wèn)題,redis dict?是采用鏈地址法來(lái)解決,dictEntry->next?就是指向下個(gè)沖突?key的節(jié)點(diǎn)。

還有一個(gè)經(jīng)常碰到的就是?rehash?的問(wèn)題,提到?rehash?我們還是有點(diǎn)擔(dān)心性能的。那么redis 實(shí)現(xiàn)是非常巧妙的,采用?惰性漸進(jìn)式 rehash 算法?。

在?dict struct?里有一個(gè)?ht[2]?組數(shù),還有一個(gè)?rehashidx?索引。redis?進(jìn)行?rehash?的大致算法是這樣的,首先會(huì)開(kāi)辟一個(gè)新的?dictht?空間,放在?ht[2]?索引上,此時(shí)將?rehashidx?設(shè)置為0,表示開(kāi)始進(jìn)入?rehash?階段,這個(gè)階段可能會(huì)持續(xù)很長(zhǎng)時(shí)間,rehashidx?表示?dictEntry?個(gè)數(shù)。

每次當(dāng)有對(duì)某個(gè)?ht[1]?索引中的?key?進(jìn)行訪問(wèn)時(shí),獲取、刪除、更新,redis?都會(huì)將當(dāng)前?dictEntry?索引中的所有?key?rehash?到?ht[2]?字典中。一旦?rehashidx=-1?表示?rehash?結(jié)束。

跳表(skip list)

skip list?是?zset?的底層數(shù)據(jù)結(jié)構(gòu),有著高性能的查找排序能力。

我們都知道一般用來(lái)實(shí)現(xiàn)帶有排序的查找都是用?Tree?來(lái)實(shí)現(xiàn),不管是各種變體的?B Tree?還是?B+ Tree,本質(zhì)都是用來(lái)做順序查找。

skip list?實(shí)現(xiàn)起來(lái)簡(jiǎn)單,性能也與?B Tree?相接近。

typedefstructzskiplistNode{robj *obj;doublescore;structzskiplistNode*backward;structzskiplistLevel{structzskiplistNode*forward;unsignedintspan;? ? } level[];} zskiplistNode;typedefstructzskiplist{structzskiplistNode*header, *tail;unsignedlonglength;intlevel;} zskiplist;

zskiplistNode->zskiplistLevel->span?這個(gè)值記錄了當(dāng)前節(jié)點(diǎn)距離下個(gè)節(jié)點(diǎn)的跨度。每一個(gè)節(jié)點(diǎn)會(huì)有最大不超過(guò)?zskiplist->level?節(jié)點(diǎn)個(gè)數(shù),分別用來(lái)表示不同跨度與節(jié)點(diǎn)的距離。

每個(gè)節(jié)點(diǎn)會(huì)有多個(gè)?forward?向前指針,只有一個(gè)?backward?指針。每個(gè)節(jié)點(diǎn)會(huì)有對(duì)象 __*obj__ 和?score?分值,每個(gè)分值都會(huì)按照順序排列。

整數(shù)集合(int set)

int set?整數(shù)集合是?set?數(shù)據(jù)類(lèi)型的底層實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu),它的特點(diǎn)和使用場(chǎng)景很明顯,只要我們使用的集合都是整數(shù)且在一定的范圍之內(nèi)都會(huì)使用整數(shù)集合編碼。

SADDset:userid100200300(integer)3

OBJECTencodingset:userid"intset"

int set?使用一塊連續(xù)的內(nèi)存來(lái)存儲(chǔ)集合數(shù)據(jù),它是數(shù)組結(jié)構(gòu)不是鏈表結(jié)構(gòu)。

typedefstructintset{uint32_tencoding;uint32_tlength;int8_tcontents[];} intset;

intset->encoding?用來(lái)確定?contents[]?是什么類(lèi)型的整數(shù)編碼,以下三種值之一。

#defineINTSET_ENC_INT16(sizeof(int16_t))#defineINTSET_ENC_INT32(sizeof(int32_t))#defineINTSET_ENC_INT64(sizeof(int64_t))

redis?會(huì)根據(jù)我們?cè)O(shè)置的值類(lèi)型動(dòng)態(tài)?sizeof?出一個(gè)對(duì)應(yīng)的空間大小。如果我們集合原來(lái)是?int16?,然后往集合里添加了?int32?整數(shù)將觸發(fā)升級(jí),一旦升級(jí)成功不會(huì)觸發(fā)降級(jí)操作。

壓縮表(zip list)

zip list?壓縮表是?listzset、hash?數(shù)據(jù)類(lèi)型的底層數(shù)據(jù)結(jié)構(gòu)之一。它是為了節(jié)省內(nèi)存通過(guò)壓縮數(shù)據(jù)存儲(chǔ)在一塊連續(xù)的內(nèi)存空間中。

typedefstructzlentry{unsignedintprevrawlensize, prevrawlen;unsignedintlensize, len;unsignedintheadersize;unsignedcharencoding;unsignedchar*p;} zlentry;

它最大的優(yōu)點(diǎn)就是壓縮空間,空間利用率很高。缺點(diǎn)就是一旦出現(xiàn)更新可能就是連鎖更新,因?yàn)閿?shù)據(jù)在內(nèi)容空間中都是連續(xù)的,最極端情況下就是可能出現(xiàn)順序連鎖擴(kuò)張。

壓縮列表會(huì)由多個(gè)?zlentry?節(jié)點(diǎn)組成,每一個(gè)?zlentry?記錄上一個(gè)節(jié)點(diǎn)長(zhǎng)度和大小,當(dāng)前節(jié)點(diǎn)長(zhǎng)度?lensize?和大小?len?包括編碼?encoding?。

這取決于業(yè)務(wù)場(chǎng)景,redis?提供了一組配置,專(zhuān)門(mén)用來(lái)針對(duì)不同的場(chǎng)景進(jìn)行閾值控制。

hash-max-ziplist-entries512hash-max-ziplist-value64

list-max-ziplist-entries512list-max-ziplist-value64

zset-max-ziplist-entries128zset-max-ziplist-value64

上述配置分別用來(lái)配置?ziplist?作為?hash?、list、zset?數(shù)據(jù)類(lèi)型的底層壓縮閾值控制。

Redis Object 類(lèi)型與映射

redis 內(nèi)部每一種數(shù)據(jù)類(lèi)型都是對(duì)象化的,也就是我們所說(shuō)的5種數(shù)據(jù)類(lèi)型其實(shí)內(nèi)部都會(huì)對(duì)應(yīng)到 redisObject 對(duì)象,然后在由 redisObject 來(lái)包裝具體的存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)和編碼。

typedefstructredisObject {unsignedtype:4;unsignedencoding:4;unsignedlru:REDIS_LRU_BITS;intrefcount;void*ptr;} robj;

這是一個(gè)很?OO?的設(shè)計(jì),redisObject->type?是?5?種數(shù)據(jù)類(lèi)型之一,redisObject->encoding?是這個(gè)數(shù)據(jù)類(lèi)型所使用的數(shù)據(jù)結(jié)構(gòu)和編碼。

我們看下?redis?提供的?5?種數(shù)據(jù)類(lèi)型與每一種數(shù)據(jù)類(lèi)型對(duì)應(yīng)的存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)和編碼。

/* Object types */#defineREDIS_STRING 0#defineREDIS_LIST 1#defineREDIS_SET 2#defineREDIS_ZSET 3#defineREDIS_HASH 4

#defineREDIS_ENCODING_RAW 0#defineREDIS_ENCODING_INT 1#defineREDIS_ENCODING_HT 2#defineREDIS_ENCODING_ZIPMAP 3#defineREDIS_ENCODING_LINKEDLIST 4#defineREDIS_ENCODING_ZIPLIST 5#defineREDIS_ENCODING_INTSET 6#defineREDIS_ENCODING_SKIPLIST 7#defineREDIS_ENCODING_EMBSTR 8

REDIS_ENCODING_ZIPMAP 3?這個(gè)編碼可以忽略了,在特定的情況下有性能問(wèn)題,在?redis 2.6?版本之后已經(jīng)廢棄,為了兼容性保留。

上圖是?redis 5?種數(shù)據(jù)類(lèi)型與底層數(shù)據(jù)結(jié)構(gòu)和編碼的對(duì)應(yīng)關(guān)系,但是這種對(duì)應(yīng)關(guān)系在每一個(gè)版本中都會(huì)有可能發(fā)生變化,這也是?redisObject?的靈活性所在,有著?OO?的這種多態(tài)性。

redisObject->refcount?表示當(dāng)前對(duì)象的引用計(jì)數(shù),在?redis?內(nèi)部為了節(jié)省內(nèi)存采用了共享對(duì)象的方法,當(dāng)某個(gè)對(duì)象被引用的時(shí)候這個(gè)?refcount?會(huì)加?1,釋放的時(shí)候會(huì)減?1。

redisObject->lru?表示當(dāng)前對(duì)象的?空轉(zhuǎn)時(shí)長(zhǎng),也就是?idle time?,這個(gè)時(shí)間會(huì)是?redis lru?算法用來(lái)釋放對(duì)象的時(shí)間依據(jù)??梢酝ㄟ^(guò)?OBJECT idletime?命令查看某個(gè)?key?的空轉(zhuǎn)時(shí)長(zhǎng)?lru?時(shí)間。

Redis 內(nèi)存管理策略

redis?在服務(wù)端分別為不同的?db index?維護(hù)一個(gè)?dict?這個(gè)?dict?稱(chēng)為?key space?鍵空間 。每一個(gè)?redis client?只能屬于一個(gè)?db index?,在?redis?服務(wù)端會(huì)維護(hù)每一個(gè)鏈接的?redisClient?。

typedefstructredisClient{uint64_tid;intfd;? ? redisDb *db;} redisClient;

在服務(wù)端每一個(gè)?redis?客戶端都會(huì)有一個(gè)指向?redisDb?的指針。

typedefstructredisDb{dict *dict;? ? dict *expires;? ? dict *blocking_keys;? ? dict *ready_keys;? ? dict *watched_keys;structevictionPoolEntry*eviction_pool;intid;longlongavg_ttl;} redisDb;

key space?鍵空間就是這里的?redisDb->dict?。redisDb->expires?是維護(hù)所有鍵空間的每一個(gè)?key?的過(guò)期時(shí)間。

鍵 過(guò)期時(shí)間、生存時(shí)間

對(duì)于一個(gè)?key?我們可以設(shè)置它多少秒、毫秒之后過(guò)期,也可以設(shè)置它在某個(gè)具體的時(shí)間點(diǎn)過(guò)期,后者是一個(gè)時(shí)間戳。

EXPIRE?命令可以設(shè)置某個(gè)?key?多少秒之后過(guò)期

PEXPIRE?命令可以設(shè)置某個(gè)?key?多少毫秒之后過(guò)期

EXPIREAT?命令可以設(shè)置某個(gè)?key?在多少秒時(shí)間戳之后過(guò)期

PEXPIREAT?命令可以設(shè)置某個(gè)?key?在多少毫秒時(shí)間戳之后過(guò)期

PERSIST?命令可以移除鍵的過(guò)期時(shí)間

其實(shí)上述命令最終都會(huì)被轉(zhuǎn)換成對(duì)?PEXPIREAT?命令。在?redisDb->expires?指向的?key?字典中維護(hù)著一個(gè)到期的毫秒時(shí)間戳。

TTL、PTTL?可以通過(guò)這兩個(gè)命令查看某個(gè)?key?的過(guò)期秒、毫秒數(shù)。

redis?內(nèi)部有一個(gè)?事件循環(huán),這個(gè)事件循環(huán)會(huì)檢查鍵的過(guò)期時(shí)間是否小于當(dāng)前時(shí)間,如果小于則會(huì)刪除這個(gè)鍵。

過(guò)期鍵刪除策略

在使用?redis?的時(shí)候我們最關(guān)心的就是鍵是如何被刪除的,如何高效的準(zhǔn)時(shí)的刪除某個(gè)鍵。其實(shí)?redis?提供了兩個(gè)方案來(lái)完成這件事情。

redis?采用?惰性刪除?、?定期刪除?雙重刪除策略。

當(dāng)我們?cè)L問(wèn)某個(gè)?key?的時(shí)候?redis?會(huì)檢查它是否過(guò)期,這是惰性刪除。

robj *lookupKeyRead(redisDb *db, robj *key) {? ? robj *val;? ? expireIfNeeded(db,key);val= lookupKey(db,key);if(val==NULL)? ? ? ? server.stat_keyspace_misses++;elseserver.stat_keyspace_hits++;returnval;}

intexpireIfNeeded(redisDb *db, robj *key){mstime_twhen = getExpire(db,key);mstime_tnow;if(when <0)return0;/* No expire for this key */if(server.loading)return0;? ? ? ? now = server.lua_caller ? server.lua_time_start : mstime();if(server.masterhost !=NULL)returnnow > when;/* Return when this key has not expired */if(now <= when)return0;/* Delete the key */server.stat_expiredkeys++;? ? propagateExpire(db,key);? ? notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,"expired",key,db->id);returndbDelete(db,key);}

redis?也會(huì)通過(guò)?事件循環(huán)?周期性的執(zhí)行?key?的過(guò)期刪除動(dòng)作,這是定期刪除。

intserverCron(structaeEventLoop *eventLoop,longlongid,void*clientData) {/* Handle background operations on Redis databases. */databasesCron();}

voiddatabasesCron(void){/* Expire keys by random sampling. Not required for slaves

? ? * as master will synthesize DELs for us. */if(server.active_expire_enabled && server.masterhost ==NULL)? ? ? ? activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);}

惰性刪除?是每次只要有讀取、寫(xiě)入都會(huì)觸發(fā)惰性刪除代碼。周期刪除?是由?redis EventLoop?來(lái)觸發(fā)的。redis?內(nèi)部很多維護(hù)性工作都是基于?EventLoop?。

AOF?、RDB?處理過(guò)期鍵策略

既然鍵會(huì)隨時(shí)存在過(guò)期問(wèn)題,那么涉及到持久化?redis?是如何幫我們處理的。

當(dāng)?redis?使用?RDB?方式持久化時(shí),每次持久化的時(shí)候就會(huì)檢查這些即將被持久化的?key?是否已經(jīng)過(guò)期,如果過(guò)期將直接忽略,持久化那些沒(méi)有過(guò)期的鍵。當(dāng)?redis作為?master 主服務(wù)器?啟動(dòng)的時(shí)候,在載入?rdb?持久化鍵時(shí)也會(huì)檢查這些鍵是否過(guò)期,將忽略過(guò)期的鍵,只載入沒(méi)過(guò)期的鍵。

當(dāng)?redis?使用?AOF?方式持久化時(shí),每次遇到過(guò)期的?key redis?會(huì)追加一條?DEL?命令 到?AOF?文件,也就是說(shuō)只要我們順序載入執(zhí)行?AOF?命令文件就會(huì)刪除過(guò)期的鍵。

如果?redis?作為從服務(wù)器啟動(dòng)的化,它一旦與?master 主服務(wù)器?建立鏈接就會(huì)清空所有數(shù)據(jù)進(jìn)行完整同步,當(dāng)然新版本的?redis?支持?SYCN2?的半同步。如果是已經(jīng)建立了?master/slave?主從同步之后,主服務(wù)器會(huì)發(fā)送?DEL?命令給所有從服務(wù)器執(zhí)行刪除操作。

Redis?LRU?算法

在使用?redis?的時(shí)候我們會(huì)設(shè)置?maxmemory?選項(xiàng),64?位的默認(rèn)是?0?不限制。線上的服務(wù)器必須要設(shè)置的,要不然很有可能導(dǎo)致?redis?宿主服務(wù)器直接內(nèi)存耗盡最后鏈接都上不去。

所以基本要設(shè)置兩個(gè)配置:

maxmemory 最大內(nèi)存閾值

maxmemory-policy 到達(dá)閾值的執(zhí)行策略

可以通過(guò)?CONFIG GET maxmemory/maxmemory-policy?分別查看這兩個(gè)配置值,也可以通過(guò)?CONFIG SET?去分別配置。

maxmemory-policy?有一組配置,可以用在很多場(chǎng)景下:

noeviction:客戶端嘗試執(zhí)行會(huì)讓更多內(nèi)存被使用的命令直接報(bào)錯(cuò)

allkeys-lru: 在所有key里執(zhí)行l(wèi)ru算法

volatile-lru:在所有已經(jīng)過(guò)期的key里執(zhí)行l(wèi)ru算法

allkeys-random:在所有key里隨機(jī)回收

volatile-random:在已經(jīng)過(guò)期的key里隨機(jī)回收

volatile-ttl:回收已經(jīng)過(guò)期的key,并且優(yōu)先回收存活時(shí)間(TTL)較短的鍵

關(guān)于?cache?的命中率可以通過(guò)?info?命令查看 鍵空間的命中率和未命中率。

# Statskeyspace_hits:33keyspace_misses:5

maxmemory?在到達(dá)閾值的時(shí)候會(huì)采用一定的策略去釋放內(nèi)存,這些策略我們可以根據(jù)自己的業(yè)務(wù)場(chǎng)景來(lái)選擇,默認(rèn)是?noeviction?。

redis LRU?算法有一個(gè)取樣的優(yōu)化機(jī)制,可以通過(guò)一定的取樣因子來(lái)加強(qiáng)回收的?key?的準(zhǔn)確度。CONFIG GET maxmemory-samples?查看取樣配置,具體可以參考更加詳細(xì)的文章。

Redis 持久化方式

redis?本身提供持久化功能,有兩種持久化機(jī)制,一種是數(shù)據(jù)持久化?RDB?,一種是命令持久化?AOF,這兩種持久化方式各有優(yōu)缺點(diǎn),也可以組合使用,一旦組合使用?redis?在載入數(shù)據(jù)的時(shí)候會(huì)優(yōu)先載入?aof?文件,只有當(dāng)?AOF?持久化關(guān)閉的時(shí)候才會(huì)載入?rdb?文件。

RDB?(Redis DataBase)

RDB?是?redis?數(shù)據(jù)庫(kù),redis?會(huì)根據(jù)一個(gè)配置來(lái)觸發(fā)持久化。

#save<seconds><changes>save9001save30010save6010000

CONFIGGET save1)"save"2)"3600 1 300 100 60 10000"

表示在多少秒之類(lèi)的變化次數(shù),一旦達(dá)到這個(gè)觸發(fā)條件 redis 將觸發(fā)持久化動(dòng)作。redis 在執(zhí)行持久化的時(shí)候有兩種模式?BGSAVE、SAVE?。BGSAVE?是后臺(tái)保存,redis?會(huì)?fork?出一個(gè)子進(jìn)程來(lái)處理持久化,不會(huì)?block?用戶的執(zhí)行請(qǐng)求。而?SAVE?則會(huì)?block?用戶執(zhí)行請(qǐng)求。

structredisServer{longlongdirty;/* Changes to DB from the last save */time_tlastsave;/* Unix time of last successful save */longlongdirty_before_bgsave;pid_trdb_child_pid;/* PID of RDB saving child */structsaveparam*saveparams;/* Save points array for RDB */}

structsaveparam{time_tseconds;intchanges;};

redisServer?包含的信息很多,其中就包含了有關(guān)于?RDB?持久化的信息。redisServer->dirty?至上次?save?到目前為止的?change?數(shù)。redisServer->lastsave?上次?save?時(shí)間。

saveparam struct?保存了我們通過(guò)?save?命令設(shè)置的參數(shù),time_t?是個(gè)?long?時(shí)間戳。

typedef__darwin_time_ttime_t;

typedeflong__darwin_time_t;/* time() */

intserverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {for(j =0; j = sp->changes &&server.unixtime-server.lastsave > sp->seconds &&? ? ? ? ? ? ? ? (server.unixtime-server.lastbgsave_try >? ? ? ? ? ? ? ? REDIS_BGSAVE_RETRY_DELAY ||server.lastbgsave_status == REDIS_OK))? ? ? ? ? ? {? ? ? ? ? ? ? ? redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",? ? ? ? ? ? ? ? ? ? sp->changes, (int)sp->seconds);? ? ? ? ? ? ? ? rdbSaveBackground(server.rdb_filename);? ? ? ? ? ? ? ? break;? ? ? ? ? ? }? ? ? ? }}

redis?事件循環(huán)會(huì)周期性的執(zhí)行?serverCron?方法,這段代碼會(huì)循環(huán)遍歷?server.saveparams?參數(shù)鏈表。

如果?server.dirty?大于等于 我們參數(shù)里配置的變化并且?server.unixtime-server.lastsave?大于參數(shù)里配置的時(shí)間并且?server.unixtime-server.lastbgsave_try?減去?bgsave?重試延遲時(shí)間或者當(dāng)前?server.lastbgsave_status==REDIS_OK?則執(zhí)行?rdbSaveBackground?方法。

AOF?(Append-only file)

AOF?持久化是采用對(duì)文件進(jìn)行追加對(duì)方式進(jìn)行,每次追加都是?redis?處理的 命令。有點(diǎn)類(lèi)似?command sourcing 命令溯源?的模式。

只要我們可以將所有的命令按照?qǐng)?zhí)行順序在重放一遍就可以還原最終的?redis?內(nèi)存狀態(tài)。

AOF?持久化最大的優(yōu)勢(shì)是可以縮短數(shù)據(jù)丟失的間隔,可以做到秒級(jí)的丟失率。RDB?會(huì)丟失上一個(gè)保存周期到目前的所有數(shù)據(jù),只要沒(méi)有觸發(fā)?save?命令設(shè)置的?save seconds changes?閾值數(shù)據(jù)就會(huì)一直不被持久化。

structredisServer{/* AOF buffer, written before entering the event loop */sds aof_buf; }

structsdshdr{unsignedintlen;unsignedintfree;charbuf[];};

aof_buf?是命令緩存區(qū),采用?sds?結(jié)構(gòu)緩存,每次當(dāng)有命令被執(zhí)行當(dāng)時(shí)候都會(huì)寫(xiě)一次到?aof_buf?中。有幾個(gè)配置用來(lái)控制?AOF?持久化的機(jī)制。

appendonlynoappendfilename"appendonly.aof"

appendonly?用來(lái)控制是否開(kāi)啟?AOF?持久化,appendfilename?用來(lái)設(shè)置?aof?文件名。

appendfsyncalwaysappendfsync everysecappendfsyncno

appendfsync?用來(lái)控制命令刷盤(pán)機(jī)制。現(xiàn)在操作系統(tǒng)都有文件?cache/buffer?的概念,所有的寫(xiě)入和讀取都會(huì)走?cache/buffer,并不會(huì)每次都同步刷盤(pán),因?yàn)檫@樣性能一定會(huì)受影響。所以?redis?也提供了這個(gè)選項(xiàng)讓我們來(lái)自己根據(jù)業(yè)務(wù)場(chǎng)景控制。

always?:每次將?aof_buf?命令寫(xiě)入?aof?文件并且執(zhí)行實(shí)時(shí)刷盤(pán)。

everysec?:每次將?aof_buf?命令寫(xiě)入?aof?文件,但是每隔一秒執(zhí)行一次刷盤(pán)。

no?:每次將?aof_buf?命令寫(xiě)入?aof?文件不執(zhí)行刷盤(pán),由操作系統(tǒng)來(lái)自行控制。

AOF?也是采用后臺(tái)子進(jìn)程的方式進(jìn)行,與主進(jìn)程共享數(shù)據(jù)空間也就是?aof_buf,但是只要開(kāi)始了?AOF?子進(jìn)程之后?redis 事件循環(huán)文件事件處理器?會(huì)將之后的命令寫(xiě)入另外一個(gè)?aof_buf?,這樣就可以做到平滑的切換。

AOF?會(huì)不斷的追加命令進(jìn)?aof?文件,隨著時(shí)間和并發(fā)量的加大?aof?文件會(huì)極速膨脹,所以有必要對(duì)這個(gè)文件大小進(jìn)行優(yōu)化。redis?基于?rewrite?重寫(xiě)對(duì)文件進(jìn)行壓縮。

no-appendfsync-on-rewriteno/yesauto-aof-rewrite-percentage100auto-aof-rewrite-min-size64mb

no-appendfsync-on-rewrite?控制是否在?bgrewriteaof?的時(shí)候還需要進(jìn)行命令追加,如果追加可能會(huì)出現(xiàn)磁盤(pán)?IO?跑高現(xiàn)象。

上面說(shuō)過(guò),當(dāng)?AOF?進(jìn)程在執(zhí)行的時(shí)候原來(lái)的事件循環(huán)還會(huì)正常的追加命令進(jìn)?aof?文件,同時(shí)還會(huì)追加命令進(jìn)另外一個(gè)?aof_buf?,用來(lái)做新?aof?文件的重寫(xiě)。這是兩條并行的動(dòng)作,如果我們?cè)O(shè)置成?yes?就不追加原來(lái)的?aof_buf?因?yàn)樾碌?aof?文件已經(jīng)包含了之后進(jìn)來(lái)的命令。

auto-aof-rewrite-percentage、auto-aof-rewrite-min-size 64mb?這兩個(gè)配置前者是文件增長(zhǎng)百分比來(lái)進(jìn)行?rewrite?,后者是按照文件大小增長(zhǎng)進(jìn)行?rewrite?。

歡迎工作一到五年的Java工程師朋友們加入Java架構(gòu)開(kāi)發(fā): 854393687

群內(nèi)提供免費(fèi)的Java架構(gòu)學(xué)習(xí)資料(里面有高可用、高并發(fā)、高性能及分布式、Jvm性能調(diào)優(yōu)、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個(gè)知識(shí)點(diǎn)的架構(gòu)資料)合理利用自己每一分每一秒的時(shí)間來(lái)學(xué)習(xí)提升自己,不要再用"沒(méi)有時(shí)間“來(lái)掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來(lái)的自己一個(gè)交代!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 分布式緩存技術(shù)PK:選擇Redis還是Memcached? 經(jīng)平臺(tái)同意授權(quán)轉(zhuǎn)載 作者:田京昆(騰訊后臺(tái)研發(fā)工程師)...
    meng_philip123閱讀 69,062評(píng)論 7 60
  • 五種數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)介 Redis是使用C編寫(xiě)的,內(nèi)部實(shí)現(xiàn)了一個(gè)struct結(jié)構(gòu)體redisObject對(duì)象,通過(guò)結(jié)構(gòu)體...
    彥幀閱讀 7,180評(píng)論 0 14
  • 和天津木炎老師聊儒道釋以及心理學(xué)感悟 她說(shuō)她理解我,她說(shuō)她理解他,被允許被理解被接納的感覺(jué)真好。再次捫心自問(wèn),反觀...
    韓林汐_a2d5閱讀 153評(píng)論 0 0
  • 為自己的夢(mèng)想努力有錯(cuò)嗎?努力追求夢(mèng)想不對(duì)嗎?現(xiàn)在的這個(gè)社會(huì)怎么啦?有必要這樣針對(duì)三個(gè)孩子嗎? 當(dāng)在微博看到...
    大哆閱讀 342評(píng)論 2 3
  • 自從進(jìn)入生物這個(gè)實(shí)驗(yàn)學(xué)科,我曾經(jīng)想過(guò)要成為一名實(shí)驗(yàn)高手。但現(xiàn)實(shí)是,經(jīng)過(guò)這五年多歷練,我突然發(fā)現(xiàn),我好像并不適合做實(shí)...
    xiaosine閱讀 1,708評(píng)論 1 2

友情鏈接更多精彩內(nèi)容