真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

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

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

標(biāo)簽: Redis Redis數(shù)據(jù)結(jié)構(gòu) Redis內(nèi)存管理策略 Redis數(shù)據(jù)類(lèi)型 Redis類(lèi)型映射

甘泉ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)的ssl證書(shū)銷(xiāo)售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話(huà)聯(lián)系或者加微信:18980820575(備注:SSL證書(shū)合作)期待與您的合作!


  • Redis 數(shù)據(jù)類(lèi)型特點(diǎn)與使用場(chǎng)景
    • StringListHash、SetZset
    • 案例:滬江團(tuán)購(gòu)系統(tǒng)大促 hot-top接口 cache設(shè)計(jì)
  • Redis 內(nèi)存數(shù)據(jù)結(jié)構(gòu)與編碼
    • OBJECTencoding key、DEBUG OBJECTkey
    • 簡(jiǎn)單動(dòng)態(tài)字符串(simple dynamic string)
    • 鏈表(linked list)
    • 字典(dict)
    • 跳表(skip list)
    • 整數(shù)集合(int set)
    • 壓縮表(zip list)
    • Redis Object 類(lèi)型與映射
  • Redis 內(nèi)存管理策略
    • 鍵 過(guò)期時(shí)間、生存時(shí)間
    • 過(guò)期鍵刪除策略
    • AOF、RDB處理過(guò)期鍵策略
    • Redis LRU算法
  • Redis 持久化方式
    • RDB(Redis DataBase)
    • AOF(Append-only file)

字典(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頭文件定義。

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx;
    int iterators; 
} dict;
typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;
typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *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)行訪(fǎng)問(wèn)時(shí),獲取、刪除、更新,redis都會(huì)將當(dāng)前 dictEntry索引中的所有 keyrehashht[2]字典中。一旦 rehashidx=-1表示 rehash結(jié)束。

跳表(skip list)

skip listzset的底層數(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相接近。

typedef struct zskiplistNode {
    robj *obj;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned int span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} 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ì)象 *objscore分值,每個(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ù)集合編碼。

SADD set:userid 100 200 300
(integer) 3
OBJECT encoding set:userid
"intset"

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

typedef struct intset {
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;

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

#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_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)存空間中。

typedef struct zlentry {
    unsigned int prevrawlensize, prevrawlen;
    unsigned int lensize, len;
    unsigned int headersize;
    unsigned char encoding;
    unsigned char *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-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

上述配置分別用來(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)和編碼。

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:REDIS_LRU_BITS; 
    int refcount;
    void *ptr;
} robj;

這是一個(gè)很 OO的設(shè)計(jì),redisObject->type5種數(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 */
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
#define REDIS_ENCODING_RAW 0     
#define REDIS_ENCODING_INT 1    
#define REDIS_ENCODING_HT 2
#define REDIS_ENCODING_ZIPMAP 3
#define REDIS_ENCODING_LINKEDLIST 4
#define REDIS_ENCODING_ZIPLIST 5 
#define REDIS_ENCODING_INTSET 6  
#define REDIS_ENCODING_SKIPLIST 7  
#define REDIS_ENCODING_EMBSTR 8 

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

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

上圖是 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。

typedef struct redisClient {
    uint64_t id;
    int fd;
    redisDb *db;
} redisClient;

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

typedef struct redisDb {
    dict *dict;
    dict *expires;
    dict *blocking_keys;
    dict *ready_keys;
    dict *watched_keys;
    struct evictionPoolEntry *eviction_pool;
    int id;
    long long avg_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++;
    else
        server.stat_keyspace_hits++;
    return val;
}
int expireIfNeeded(redisDb *db, robj *key) {
    mstime_t when = getExpire(db,key);
    mstime_t now;

    if (when < 0) return 0; /* No expire for this key */

    if (server.loading) return 0;

    now = server.lua_caller ? server.lua_time_start : mstime();
    if (server.masterhost != NULL) return now > when;

    /* Return when this key has not expired */
    if (now <= when) return 0;

    /* Delete the key */
    server.stat_expiredkeys++;
    propagateExpire(db,key);
    notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,"expired",key,db->id);
    return dbDelete(db,key);
}

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

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    /* Handle background operations on Redis databases. */
    databasesCron();
}
void databasesCron(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不限制。線(xiàn)上的服務(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:客戶(hù)端嘗試執(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命令查看 鍵空間的命中率和未命中率。

# Stats
keyspace_hits:33
keyspace_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)

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

#save  

save 900 1
save 300 10
save 60 10000
CONFIG GET save
1) "save"
2) "3600 1 300 100 60 10000"

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

struct redisServer {
long long dirty;/* Changes to DB from the last save */
time_t lastsave; /* Unix time of last successful save */
long long dirty_before_bgsave;
pid_t rdb_child_pid;/* PID of RDB saving child */
struct saveparam *saveparams; /* Save points array for RDB */
}
struct saveparam {
    time_t seconds;
    int changes;
};

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_t     time_t; 
typedef long    __darwin_time_t;    /* time() */
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
         for (j = 0; j < server.saveparamslen; j++) {
            struct saveparam *sp = server.saveparams+j;
            if (server.dirty >= 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ì)一直不被持久化。

struct redisServer {
 /* AOF buffer, written before entering the event loop */
 sds aof_buf;
 }
struct sdshdr {
    unsigned int len;
    unsigned int free;
    char buf[];
};

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

appendonly no 
appendfilename "appendonly.aof"

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

appendfsync always
appendfsync everysec
appendfsync no

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-rewrite no/yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

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


分享標(biāo)題:Redis數(shù)據(jù)結(jié)構(gòu)與內(nèi)存管理策略(下)
網(wǎng)站路徑:http://weahome.cn/article/gepspc.html

其他資訊

在線(xiàn)咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部