小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Redis編程小技巧拾遺

 icecity1306 2014-11-18

最近接觸了一下Redis數(shù)據(jù),出于好奇看了下它的源碼,覺得這是一個值得一讀的開源項目。關(guān)于Redis的源碼分析,已經(jīng)有很多網(wǎng)友寫了各種分析筆記,而且也有相關(guān)的書籍《Redis設(shè)計與實現(xiàn)》,因此我覺得完整的寫一系列的博客就沒有必要了,這里主要記錄一些個人覺得有意思或者是值得了解的東西(之前面試也有問到一些問題,如果我早一點接觸這些東西的話,可以回答的更好)。

如果對Redis源碼有興趣的話,可以先看一看1.0 Beta版的代碼,非常的簡短,對一些基本的東西有一個大致的了解之后再選一個新的穩(wěn)定版本的源碼進行閱讀和學習。

1. 空數(shù)組
對于結(jié)構(gòu)體成員中大小不確定的地方,可以考慮放一個空數(shù)組到結(jié)構(gòu)體的末尾,這樣通過動態(tài)內(nèi)存分配,就可以合理的設(shè)置空間了,當然需要一個成員記錄元素的個數(shù)。

SDS是Redis封裝的一個字符串類型,因為字符串的長度需要動態(tài)的控制,所以就用了空數(shù)組這個小技巧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct sdshdr {
    unsigned int len;
    unsigned int free;
    char buf[];     // 空數(shù)組
};
 
sds sdsnewlen(const void *init, size_t initlen) {
    struct sdshdr *sh;
    // 額外的空間大小: 字符串長度initlen,以及用于填充空字符的1字節(jié)
    if (init) {
        sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
    } else {
        sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
    }
    if (sh == NULL) return NULL;
    sh->len = initlen;
    sh->free = 0;
    if (initlen && init)
        memcpy(sh->buf, init, initlen);
    sh->buf[initlen] = '\0';
    return (char*)sh->buf;
}

同樣的技巧在跳躍表(skiplist)中也有用到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 跳躍表節(jié)點的定義
typedef struct zskiplistNode {
    robj *obj;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned int span;
    } level[];      // 空數(shù)組
} zskiplistNode;
 
// 跳躍表的定義
typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} zskiplist;
 
zskiplistNode *zslCreateNode(int level, double score, robj *obj) {
    // 通過level計算出額外需要的空間大小
    zskiplistNode *zn = zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));
    zn->score = score;
    zn->obj = obj;
    return zn;
}
 
zskiplist *zslCreate(void) {
    int j;
    zskiplist *zsl;
 
    zsl = zmalloc(sizeof(*zsl));
    zsl->level = 1;
    zsl->length = 0;
    zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);
    // 初始化level數(shù)組
    for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
        zsl->header->level[j].forward = NULL;
        zsl->header->level[j].span = 0;
    }
    zsl->header->backward = NULL;
    zsl->tail = NULL;
    return zsl;
}

2. 宏定義中使用do while(0)
Redis中宏定義中的很多地方都使用了do { } while (0)進行了包裹,如:

1
2
3
4
5
6
#define dictSetVal(d, entry, _val_) do {     if ((d)->type->valDup)         entry->v.val = (d)->type->valDup((d)->privdata, _val_);     else         entry->v.val = (_val_); } while(0)

如果宏里面的代碼包含多條語句的時候,這里的作用就是將其封裝為一條語句,這樣即使放到?jīng)]有大括號的if后面也不會有問題了。在網(wǎng)上還看到了另一種do { } while (0)的使用場景(使代碼更優(yōu)美):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool foobar()
{
    int *p = new int[10];
    bool bOk = true;
 
    do
    {
        bOk = func1();
        if(!bOk) break;
 
        bOk = func2();
        if(!bOk) break;
 
        bOk = func3();
        if(!bOk) break;
 
        // ..........
    }while(0);
 
    delete[] p;
    return bOk;
}

3. 調(diào)試信息打印
自定義assert函數(shù),當條件不通過時打印文件名、行號以及條件信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// #_e 將_e轉(zhuǎn)換為字符串
#define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
 
void _redisAssert(char *estr, char *file, int line) {
    bugReportStart();
    redisLog(REDIS_WARNING,"=== ASSERTION FAILED ===");
    redisLog(REDIS_WARNING,"==> %s:%d '%s' is not true",file,line,estr);
#ifdef HAVE_BACKTRACE
    server.assert_failed = estr;
    server.assert_file = file;
    server.assert_line = line;
    redisLog(REDIS_WARNING,"(forcing SIGSEGV to print the bug report.)");
#endif
    *((char*)-1) = 'x';
}

另外redisLog打印日志時,可以根據(jù)第一個參數(shù)進行過濾操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define REDIS_DEBUG 0
#define REDIS_VERBOSE 1
#define REDIS_NOTICE 2
#define REDIS_WARNING 3
 
void redisLog(int level, const char *fmt, ...) {
    va_list ap;
    char msg[REDIS_MAX_LOGMSG_LEN];
    // server.verbosity 從配置文件讀取設(shè)定
    if ((level&0xff) < server.verbosity) return;
 
    va_start(ap, fmt);
    vsnprintf(msg, sizeof(msg), fmt, ap);
    va_end(ap);
 
    redisLogRaw(level,msg);
}

4. 其他

1
2
// 消除函數(shù)未使用參數(shù)的警告信息
#define REDIS_NOTUSED(V) ((void) V)

待補充……

參考
1. do…while(0)的妙用


本文地址: 程序人生 >> Redis編程小技巧拾遺
作者:代碼瘋子(Wins0n) 本站內(nèi)容如無聲明均屬原創(chuàng),轉(zhuǎn)載請保留作者信息與原文鏈接,謝謝!


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多