Python--Redis實戰:第四章:數據安全與性能保障:第4節:複製

上一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第3節:AOF持久化
下一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第5節:處理系統故障

對於有擴展平臺以適應更高負載經驗的工程師和管理員來講,複製(replication)是不可或缺的。複製可讓其餘服務器擁有一個不斷地更新的數據副本,從而使得擁有數據副本的服務器能夠用於處理客戶端發送的讀請求,。關係數據庫一般會使用一個主服務器(數據庫

master)向多個從服務器(slave)發送更新,並使用從服務器來處理全部讀請求。Redis也採用了一樣的方法來實現本身的複製特性,並將其用做擴展性能的一種手段。本節將對Redis的複製配置選項進行討論,並說明Redis在進行復制的各個步驟。segmentfault

儘管Redis的性能的很是優秀,但它也會趕上沒辦法快速的處理請求的狀況,特別是在對集合和有序集合進行操做的時候,涉及的元素可能會有上萬個甚至上百萬個,在這種狀況下,執行操做所花費的時間可能須要以秒來進行計算,而不是毫秒或者微妙。但即便一個命令值須要花費10毫秒就能完成,單個Redis實例1秒也只能處理100個命令。安全

sunionstore命令的性能:

做爲對Redis性能的一個參考,在主頻爲2.4GHz的英特爾酷睿2處理器上,對兩個分別包含10 000個元素的集合執行sunionstore命令併產生一個包含20 000個元素的結果集合,須要花費Redis七八毫秒的時間。服務器

在須要擴展讀請求的時候,或者在須要寫入臨時數據的時候,用戶能夠經過設置額外的Redis【從服務器】來保存數據集的副本。在接受到主服務器發送的數據初始副本(initial copy of data)以後,客戶端每次向【主服務器】進行寫入時,【從服務器】都會實時地獲得更新。在部署好主從服務器以後,客戶端就能夠向任意一個服務器發送讀請求了,而沒必要再像以前同樣,老是把每一個讀請求都發送給主服務器(客戶端一般會隨機地選擇使用哪一個從服務器,從而將負載平均分配到各個從服務器上)。網絡

接下來將介紹配置Redis主從服務器的方法,並說明Redis在整個複製過程當中所作的各項操做。app

對Redis的複製相關選項進行配置

以前介紹過,當從服務器鏈接主服務器的時候,主服務器會執行bgsave操做。所以爲了正確的使用複製特性,用戶須要保證主服務已經正確的配置了dir選項和dbfilename選項,而且這兩個選項所指示的路徑和文件對於Redis進程來講都是可寫的。ide

儘管有多個不一樣的選項能夠控制從服務器自身的行爲,但開啓從服務器所必須的選項只有slaveof一個。若是用戶在啓動Redis服務器的時候,指定了一個包含slaveof host port選項的配置文件,那麼Redis服務器將根據該選項給定的IP地址和端口號來鏈接主服務器。對於一個正在運行的Redis服務器,用戶能夠經過發送slaveof no one命令來讓服務器終止複製操做,再也不接受主服務器的數據更新,也能夠經過發送slaveof host port命令來讓服務器開始複製一個新的主服務器。函數

開啓Redis的主從複製特性並不須要進行太多的配置,但瞭解Redis服務器是如何變成主服務器或者從服務器的,對於咱們來講將是很是有用和有趣的過程。性能

Redis複製的啓動過程

上面曾將說過,從服務器在鏈接一個主服務器的時候,主服務器會建立一個快照文件並將其發送至從服務器,但這只是主從複製執行的其中一步,下表完整的列出了當從服務器鏈接主服務器時,主從服務器執行的全部操做:ui

步驟 主服務器操做 從服務器操做
1 (等待命令進入) 鏈接(或者重鏈接)主服務,發送sync命令。
2 開始執行bgsave,並使用緩衝區記錄bgsave以後執行的全部寫命令 根據配置選項來決定是繼續使用現有的數據(若是有的話)來處理客戶端的命令請求,仍是向發送請求的客戶端返回錯誤。
3 bgsave執行完畢,向從服務器發送快照文件,並在發送期間繼續使用緩衝區記錄被執行的寫命令 丟棄全部舊數據(若是有的話),開始載入主服務器發來的快照文件。
4 快照文件發送完畢,開始向從服務器發送存儲在緩衝區裏面的寫命令。 完成對快照文件的解釋操做,像往常同樣開始接受命令請求。
5 緩衝區存儲的寫命令發送完畢;從如今開始,每執行一個寫命令,就向從服務器發送相應的寫命令。 執行主服務器發送的全部存儲在緩衝區裏面的寫命令;並從如今開始,接受並執行主服務傳來的每一個寫命令。

Redis在複製進行期間也會盡量地處理接受到的命令請求,可是,若是主從服務器之間的網絡帶寬不足,或者主服務器沒有足夠的內存來建立子進程和建立記錄寫命令的緩衝區,那麼Redis處理命令請求的效率會受到影響。所以,儘管這並非必需的,但在實際中華最好仍是讓主服務器只使用50%~65%的內存,預留30%~45%內存用於執行bgsave命令和建立記錄寫命令的緩衝區。

設置從服務器的步驟很是簡單,用戶既能夠經過配置選項slaveof host port來將一個Redis服務器設置爲從服務器,又能夠經過向運行中的Redis服務器發送slaveof命令來將其設置爲從服務。若是用戶使用的是slaveof配置選項,那麼Redis在啓動時首先載入當前可用的任何快照問價或者AOF文件,而後鏈接主服務並執行上表的複製過程。若是用戶使用的是slaveof命令,那麼Redis會當即嘗試鏈接主服務器,並在鏈接成功後,開始上表所示的複製過程。

從服務器在運行同步時,會清空本身的全部數據:

由於有些用戶在第一次使用從服務器時會忘記這件事,因此在這裏要特別提醒一下:從服務器在於主服務器進行初始鏈接時,數據庫中原有的全部數據將丟失,並被替換成主服務器發送來的數據。


警告:Redis不支持主主複製:

由於Redis容許用戶在服務器啓動以後使用slaveof命令來設置從服務器選項,因此可能會有讀者誤覺得能夠經過將兩個Redis實例互相設置爲對方的主服務器來實現多主複製,遺憾的是,這種作法是行不通的:被互相設置爲主服務器的兩個Redis實例只會持續的佔用大量處理器資環並持續不斷的嘗試與對方進行通訊,根據客戶端連接的服務器的不一樣,客戶端的請求可能會獲得不一致的數據或者徹底得不到數據。

當多個從服務器嘗試鏈接同一個主服務器的時候,就會出現下表所示的兩種狀況中的一種:

當有新的從服務器鏈接主服務時 主服務器的操做
當上表步驟3還沒有執行 全部從服務器都會接受到相同的快照文件和相同的緩衝區寫命令
當上表步驟3正在執行或者已經執行完畢 當主服務器與較早進行連的從服務器執行完複製所需的5個步驟以後,主服務會與新鏈接的從服務器執行一次進的步驟1至步驟5.

在大部分狀況下,Redis都會盡量地減小複製所需的工做,然而,若是從服務器鏈接主服務器的時間並不湊巧,那麼主服務器就須要多作一些額外的工做,。另外一方面,當多個從服務器同時鏈接主服務器的時候,同步多個主服務器所佔用的寬帶可能會使得其餘命令請求難以傳遞給主服務器,與主服務器位於同一網絡中的其餘硬件的網速可能也會所以而下降。

主從鏈

有些用戶發現,建立多個從服務器可能或形成網絡不可用:當複製須要經過互聯網進行或者須要在不一樣數據中心之間進行時,尤其如此。由於Redis的主服務和從服務器並無特別不一樣的地方,因此從服務器也能夠擁有本身的從服務器,並由此造成主從鏈。

從服務器對從服務器進行復制在操做上和那個服務器對主服務器進行復制的惟一區別在於,若是從服務器X擁有從服務器Y,那麼當從服務器X在執行上節複製步驟的步驟4時,它將斷開與從服務器Y的鏈接,致使從服務器Y須要從新鏈接並從新同步。

當【讀請求】的重要性明顯高於【寫請求】的重要性,而且讀請求的數量遠遠超出一臺Redis服務器能夠處理的範圍時,用戶就須要添加新的從服務器來處理【讀請求】。隨着負載不斷上升,主服務器可能會沒法快速地更新全部從服務器,或者由於從新鏈接和從新同步從服務器而致使系統超載。爲了緩解這個問題,用戶能夠建立一個由Redis主從節點組成的中間層來分擔主服務器的複製工做。以下圖所示:

圖片描述

一個Redis主從複製樹示例,樹的中層有3個幫助開展複製工做的服務器,底層與9個從服務器。

儘管主從服務器之間並不必定要像上圖那樣組成一個樹狀結構,但記住並理解這種樹狀結構對於Redis複製來講是可行的而且是合理的,有主於讀者理解以後的內容。AOF持久化的同步選項能夠控制數據丟失的時間長度:經過將每一個寫命令同步到硬盤裏面,用戶幾乎能夠不損失任何數據(除非系統崩潰或者硬盤驅動器損壞),但這種作法會對服務器的性能形成影響;另外一方面,若是用戶將同步的頻率設置爲每秒一次,那麼服務器的性能將回到正常水平,但故障可能會形成1秒的數據損失。經過同時使用複製和AOF持久化,咱們能夠將數據持久化到多臺服務器上面。

爲了將數據保存到多臺服務器上面,用戶首先須要爲主服務器設置多個從服務器,而後對每一個從服務設置appendonly yes選項和sppendonly everysec選項(若是有須要得我話,也能夠對主服務器進行相同的設置),這樣的話,用戶就可讓多臺服務器以每秒1次的頻率將數據同步到硬盤上了,但這還只是第一步:由於用戶還必須等待主服務器發送的寫命令到達從服務器,而且在執行後續操做以前,檢查數據是否已經被同步到硬盤裏面。

檢查硬盤寫入

爲了驗證主服務器是否已經將寫數據發送至從服務器,用戶須要在向主服務器寫入真正的數據以後,再向從服務器寫入一個惟一的虛構值,而後經過驗證虛構值是否存在於從服務器來判斷寫數據是否已經到達從服務器,這個操做很容易就能夠實現。另外一方面,判斷數據是否已經被保存到硬盤裏面則要困難的多。對於每秒同步一次AOF文件的Redis服務器來講,用戶老是能夠經過等待1秒來確保數據已經被保存到硬盤裏面,但更節約時間的作法是,檢查info命令的輸出結果中aof_pending_bio_fsync屬性的值是否爲0,若是是的話,那麼就表示服務器已經將已知的全部數據都保存到硬盤裏面了。在向主服務器寫入數據以後,用戶能夠將主服務器和從服務器的鏈接做爲參數,調用下面函數來能自動進行上述的檢查操做。

import time
import uuid


def wait_for_sync(mconn,sconn):
    identifier=str(uuid.uuid4())
    #將令牌添加至主服務器
    mconn.zadd('sync:wait',identifier,time.time())

    #若是有必要的話,等待從服務器完成同步
    while not sconn.info()['master_lin_status']!='up':
        time.sleep(.001)

    #等待從服務器接收數據更新
    while not sconn.zscore('sync:wait',identifier):
        time.sleep(.001)

    deadline=time.time()+1.01
    #最多隻等待一秒
    while time.time()<deadline:
        #檢查數據更新是否已經被同步到了硬盤
        if sconn.info()['aof_pending_bio_fsync']==0:
            break
    #清理剛剛建立的新令牌以及以前可能留下的舊令牌
    mconn.zrem('sync:wait',identifier)
    mconn.zremrangebyscore('sync:wait',0,time.time()-900)
info命令中的其餘信息:

info命令提供了大量的與Redis服務器當前狀態相關的信息,好比內存佔用量、客戶端鏈接數、每一個數據庫包含的鍵的數量、上一次建立快照文件以後執行的命令數量、等待。總的來講,info命令對於瞭解Redis服務器的綜合狀態很是有幫助,網上有不少資源對info命令進行了詳細的介紹。

爲了確保操做能夠正確執行,上面函數會首先確認從服務器已經鏈接上主服務器,而後檢查本身添加到等待同步有序集合裏面的值是否已經存在於從服務器。在發現值已經存在於從服務器以後,函數會檢查從服務器寫入緩衝區的狀態,並在一秒以內,等待從服務器將緩衝區中的全部數據寫入硬盤裏面。雖然函數最多會花費1秒來等待同步完成,但實際上大部分同步都會在很短的時間完成。最後,在確認數據已經被保存到硬盤以後,函數會執行一些清理操做。

經過同步使用複製和AOF持久化,用戶能夠加強Redis對於系統崩潰的抵抗能力。

上一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第3節:AOF持久化
下一篇文章: Python--Redis實戰:第四章:數據安全與性能保障:第5節:處理系統故障
相關文章
相關標籤/搜索