濟(jì)南企業(yè)建站品牌騰訊企點(diǎn)
執(zhí)行了 save 命令,會在主進(jìn)程生成 RDB 文件,由于和執(zhí)行操作命令在同一個線程,所以如果寫入 RDB 文件的時間太長,會阻塞主進(jìn)程。
執(zhí)行 bgsave 過程中,由于是交給子進(jìn)程來構(gòu)建 RDB 文件,主進(jìn)程還是可以繼續(xù)工作的,此時主進(jìn)程依然可以繼續(xù)處理操作命令,也就是數(shù)據(jù)是能被修改的,關(guān)鍵的技術(shù)就在于寫時復(fù)制技術(shù)。
執(zhí)行 bgsave 命令的時候,主進(jìn)程會通過 fork() 創(chuàng)建子進(jìn)程,此時子進(jìn)程和父進(jìn)程是共享同一片內(nèi)存數(shù)據(jù)的,因為在創(chuàng)建子進(jìn)程的時候,會復(fù)制父進(jìn)程的頁表,且頁表指向的物理內(nèi)存是同一個,此時如果主進(jìn)程執(zhí)行讀操作,則主進(jìn)程和 bgsave 子進(jìn)程互不影響。
如果主進(jìn)程執(zhí)行寫操作,則被修改的數(shù)據(jù)會復(fù)制一份副本,然后 bgsave 子進(jìn)程會把它的副本數(shù)據(jù)寫入 RDB 文件,在這個過程中,主進(jìn)程仍然可以直接修改原來的數(shù)據(jù)。
注意,只有在發(fā)生修改內(nèi)存數(shù)據(jù)的情況時,物理內(nèi)存才會被復(fù)制一份。
舉例來說,如果主進(jìn)程要修改共享數(shù)據(jù)里的某一塊數(shù)據(jù)(比如鍵值對 A)時,就會發(fā)生寫時復(fù)制。于是這塊數(shù)據(jù)的物理內(nèi)存就會被復(fù)制一份(鍵值對 A’)。然后主進(jìn)程在這個數(shù)據(jù)副本(鍵值對 A’)上進(jìn)行修改操作。與此同時,bgsave 子進(jìn)程可以繼續(xù)把原來的數(shù)據(jù)(鍵值對 A)寫入到 RDB 文件。
注意,在發(fā)生了寫時復(fù)制后,RDB 快照保存的是原本的內(nèi)存數(shù)據(jù),而主進(jìn)程剛修改的數(shù)據(jù)是沒辦法在這一時間寫入 RDB 文件的,只能交由下一次的 bgsave 快照。
所以 Redis 在使用 bgsave 快照過程中,如果主進(jìn)程修改了內(nèi)存數(shù)據(jù),不管是否是共享的內(nèi)存數(shù)據(jù),RDB 快照都無法寫入主進(jìn)程剛修改的數(shù)據(jù),因為此時主進(jìn)程的內(nèi)存數(shù)據(jù)和子進(jìn)程的內(nèi)存數(shù)據(jù)已經(jīng)分離了,子進(jìn)程寫入到 RDB 文件的內(nèi)存數(shù)據(jù)只能是原本的內(nèi)存數(shù)據(jù)(快照的定義)。
如果系統(tǒng)恰好在 RDB 快照文件創(chuàng)建完畢后崩潰了,那么 Redis 將會丟失主進(jìn)程在快照期間修改的數(shù)據(jù)。