面試總結(jié)(三)-redis

redis

寫在前面:要想深入了解redis,《redis設(shè)計(jì)與實(shí)現(xiàn)(第二版)》這本書(shū)是很不錯(cuò)的選擇;關(guān)于redis的底層存儲(chǔ)結(jié)構(gòu)會(huì)在下一篇文章中寫,本章切掉這部分內(nèi)容了。

一、redis 持久化方案(RDB、AOF)

1、RDB(默認(rèn)持久化方案)
服務(wù)運(yùn)行時(shí)將內(nèi)存文件保存到.rdb 文件中,重啟時(shí):讀取數(shù)據(jù)存儲(chǔ)文件.rdb,加載到內(nèi)存中,還原DB。

  • 保存文件時(shí),rdb文件保存會(huì)阻塞主線程;解決方法:rdbbackgroud 方法后臺(tái)運(yùn)行
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
    pid_t childpid;
    long long start;

    if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR;
    if ((childpid = fork()) == 0) {
        int retval;

        /* Child */
        closeListeningSockets(0);
        redisSetProcTitle("redis-rdb-bgsave");
        retval = rdbSave(filename,rsi);
       .............省略一部分
        /* Parent */
        server.stat_fork_time = ustime()-start;
        server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
        latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
        if (childpid == -1) {
            closeChildInfoPipe();
            server.lastbgsave_status = C_ERR;
            serverLog(LL_WARNING,"Can't save in background: fork: %s",
                strerror(errno));
            return C_ERR;
        }
        serverLog(LL_NOTICE,"Background saving started by pid %d",childpid);
        server.rdb_save_time_start = time(NULL);
        server.rdb_child_pid = childpid;
        server.rdb_child_type = RDB_CHILD_TYPE_DISK;
        updateDictResizePolicy();
        return C_OK;
    }
    return C_OK; /* unreached */
}

  • 啟動(dòng)時(shí)會(huì)讀取rdb文件到內(nèi)存中,載入過(guò)程中,不支持其他請(qǐng)求,下面的這段代碼值得好好看一下
/* Load an RDB file from the rio stream 'rdb'. On success C_OK is returned,
 * otherwise C_ERR is returned and 'errno' is set accordingly. */
int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi) {
  
    rdb->update_cksum = rdbLoadProgressCallback;
    rdb->max_processing_chunk = server.loading_process_events_interval_bytes;
   
    while(1) {
        。。。 省略一部分代碼
        /* Read type. */
        if ((type = rdbLoadType(rdb)) == -1) goto eoferr;

        /* Handle special types. */
        if (type == RDB_OPCODE_EXPIRETIME) {
            /* EXPIRETIME: load an expire associated with the next key
             * to load. Note that after loading an expire we need to
             * load the actual type, and continue. */
            if ((expiretime = rdbLoadTime(rdb)) == -1) goto eoferr;
            /* We read the time so we need to read the object type again. */
            if ((type = rdbLoadType(rdb)) == -1) goto eoferr;
            /* the EXPIRETIME opcode specifies time in seconds, so convert
             * into milliseconds. */
            expiretime *= 1000;
        } else if (type == RDB_OPCODE_EXPIRETIME_MS) {
            /* EXPIRETIME_MS: milliseconds precision expire times introduced
             * with RDB v3. Like EXPIRETIME but no with more precision. */
            if ((expiretime = rdbLoadMillisecondTime(rdb)) == -1) goto eoferr;
            /* We read the time so we need to read the object type again. */
            if ((type = rdbLoadType(rdb)) == -1) goto eoferr;
        } else if (type == RDB_OPCODE_EOF) {
            /* EOF: End of file, exit the main loop. */
            break;
        }
            decrRefCount(auxkey);
            decrRefCount(auxval);
            continue; /* Read type again. */
        }
。。。 省略一部分代碼
}

rdb 文件保存時(shí)間點(diǎn),在redis.conf中進(jìn)行配置 ' save <seconds> <changes>'

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""

save 900 1    //900秒保存一個(gè)key
save 300 10    //300秒保存10個(gè)key
save 60 10000    //60秒保存10000個(gè)key

2、AOF
aof是一個(gè)協(xié)議文本方式的存儲(chǔ)方案,對(duì)數(shù)據(jù)庫(kù)的操作命令記錄到aof文件中,達(dá)到記錄記錄數(shù)據(jù)的目的(有點(diǎn)類似MySQL的bin log)
寫入流程:

  • 1、將請(qǐng)求命令轉(zhuǎn)換為網(wǎng)絡(luò)協(xié)議文本方式
  • 2、將協(xié)議文本內(nèi)容寫到server.aof_buf
  • 3、達(dá)到某種條件的時(shí)候,講aof_buf寫入到磁盤中
    • AOF_FSYNC_NO 不保存只會(huì)追加到cof_buf 文件中,在緩存寫滿的情況下,會(huì)保存;正常關(guān)閉redis,會(huì)保存;AOF功能關(guān)閉,會(huì)保存。
    • AOF_FSYNC_ALWAYS 每次執(zhí)行一條命令都會(huì)進(jìn)行一次保存操作
    • AOF_FSYNC_EVERYSEC 每秒保存一次,默認(rèn)配置后臺(tái)執(zhí)行,不會(huì)阻塞住進(jìn)程。

上面的常量,也是在redis.conf 中配置aof的參數(shù)。 默認(rèn)appendfsync no

aof的出現(xiàn)是為了解決什么問(wèn)題?官方說(shuō)明:rds 這種方案本來(lái)已經(jīng)是一種很好的方案了,能夠適應(yīng)絕大多數(shù)的情況,但是在redis進(jìn)程或電源發(fā)生故障的情況下,可能會(huì)造成小部份的數(shù)據(jù)丟失,這取決于配置的保存時(shí)間點(diǎn);
Appendonly是一種能夠提供非常好的持久化的模式,例如使用默認(rèn)的Fsync方案,redis能在發(fā)生服務(wù)器電源故障或操作系統(tǒng)仍然正常運(yùn)行但redis進(jìn)程莫名掛掉的情況下,只丟失1秒的數(shù)據(jù)。 aof與rdb模式可以同時(shí)啟用,這并不沖突。如果aof是可用的,那redis啟動(dòng)時(shí)將自動(dòng)加載aof,這個(gè)文件能夠提供更好的持久性保障。

原文:
############################## APPEND ONLY MODE ###############################

# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
  • AOF保存數(shù)據(jù)的還原:講協(xié)議文本轉(zhuǎn)換為命令執(zhí)行,從而還原數(shù)據(jù)。
AOF VS RDB
  • AOF 文件通常會(huì)大于RDB文件
  • AOF 的效率取決于配置策略
  • RDB 設(shè)置過(guò)大,操作耗時(shí)過(guò)多,可能會(huì)出現(xiàn)短時(shí)間無(wú)法響應(yīng)的情況;設(shè)置過(guò)小,也會(huì)有IO瓶頸

二、redis 常用數(shù)據(jù)結(jié)構(gòu)

  • string: key value 服務(wù);應(yīng)用:一般用于緩存
  • set: 集合去重,集合的交、并、差操作;應(yīng)用: 可以用作共同、二度好友的計(jì)算
  • sortset: 帶有排序的set集合; 應(yīng)用:取Top10、消息隊(duì)列、設(shè)置權(quán)重
  • hash: 結(jié)構(gòu)化數(shù)據(jù)的存儲(chǔ)、通常用于對(duì)象屬性的更新;(結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)建議使用hash,效率更高)
  • list: 鏈表 ,pop push,也可以用作隊(duì)列
  • pub/sub: 消息系統(tǒng),key的發(fā)布和訂閱(也可以用作定時(shí)任務(wù))

三、redis的啟動(dòng)流程分析

任何一個(gè)組件,包括redis,MySQL或者說(shuō)servlet的啟動(dòng)過(guò)程都是有著很多的相似之處,對(duì)比學(xué)習(xí)是一個(gè)很好的學(xué)習(xí)方法;

redis 啟動(dòng)過(guò)程包括兩個(gè)主要操作:初始化服務(wù)器配置、初始化功能模塊
初始化服務(wù)器配置,主要做以下事情

  • 初始化server變量、設(shè)置redis默認(rèn)值
  • 讀取配置文件,接受參數(shù),替換服務(wù)器默認(rèn)配置
  • 初始化功能模塊
    • 1 注冊(cè)信號(hào)事件
    • 2 初始化客戶端連接
    • 3 初始共享對(duì)象
    • 4 監(jiān)測(cè)最大連接數(shù)
    • 5 初始化DB
    • 6 初始化network
    • 7 初始化服務(wù)器實(shí)時(shí)統(tǒng)計(jì)
    • 8 初始化后臺(tái)后臺(tái)計(jì)劃任務(wù)
    • 9 初始化lua腳本
    • 10 初始化慢查詢?nèi)罩?/li>
    • 11 初始化后臺(tái)線程任務(wù)系統(tǒng)
  • 從RDB或者AOF重載數(shù)據(jù)(磁盤數(shù)據(jù)加載內(nèi)存)
  • 網(wǎng)絡(luò)監(jiān)聽(tīng)服務(wù)啟動(dòng)前的準(zhǔn)備操作
  • 開(kāi)啟事件監(jiān)聽(tīng)、開(kāi)始接受客戶端請(qǐng)求

下面是轉(zhuǎn)的一個(gè)流程圖,整個(gè)過(guò)程表達(dá)的很清晰


圖片轉(zhuǎn)自http://processon.com/chart_image/id/58441b02e4b0e742e493404c.png 如有侵權(quán)請(qǐng)聯(lián)系本人
最后編輯于
?著作權(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)容

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