1. 程式人生 > 其它 >P2846 [USACO08NOV]Light Switching G(動態開點寫法)

P2846 [USACO08NOV]Light Switching G(動態開點寫法)

如何使用

通過持久化功能,Redis保證了即使在伺服器重啟的情況下也不會損失(或少量損失)資料。但是由於資料是儲存在一臺伺服器上的,如果這臺伺服器出現硬碟故障等問題,也會導 致資料丟失。

為了避免單點故障,通常的做法是將資料庫複製多個副本以部署在不同的伺服器上,這樣即使有一臺伺服器出現故障,其他伺服器依然可以繼續提供服務。

為此, Redis 提供了複製(replication)功能,可以實現當一臺資料庫中的資料更新後,自動將更新的資料同步到其他資料庫上。

在複製的概念中,資料庫分為兩類,一類是主資料庫(master),另一類是從資料庫(slave)。主資料庫可以進行讀寫操作,當寫操作導致資料變化時會自動將資料同步給從資料庫。而從資料庫一般是隻讀的,並接受主資料庫同步過來的資料。

一個主資料庫可以擁有多個從資料庫,而一個從資料庫只能擁有一個主資料庫。

在 Redis 中使用複製功能非常容易,只需要在從資料庫的配置檔案中加入slaveof 主數 據庫地址 主資料庫埠 即可,主資料庫無需進行任何配置。

例如,這裡有兩臺伺服器,主伺服器A的IP為 192.168.0.106,從伺服器B的IP為 192.168.7.477。

首先使用預設埠啟動主伺服器:

redis-server

指定埠號並加上 svlavefo 引數啟動從伺服器:

redis-server --port 6380 --slaveof 192.168.0.106 6379

此時在主資料庫中的任何資料變化都會自動地同步到從資料庫中,我們開啟 redis-cli 例項A並連線到主資料庫:

redis-cli -p 6379

再開啟redis-cli例項B並連線到從資料庫:

redis-cli -p 6380

這時我們使用INFO命令來分別在例項A和例項B中獲取Replication節的相關資訊:

127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.7.144,port=6380,state=online,offset=85,lag=1
master_repl_offset:85
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:84
127.0.0.1:6379>

可以看到,例項A的角色(上面輸出中的role)是master,即主資料庫,同時已連線的從資料庫(上面輸出中的connected_slaves)的個數為1。

同樣在例項B中獲取相應的資訊為:

127.0.0.1:6380> INFO replication
# Replication
role:slave
master_host:192.168.0.106
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:155
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:47c2652dbfc83bc2e813a8c055b9232e64cd229b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:155
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:154

這裡可以看到,例項 B 的 role 是 slave,即從資料庫,同時其主資料庫的地址為 192.168.0.106,埠為 6379。

這時,在例項B中使用SET命令設定一個鍵的值,會報錯:

127.0.0.1:6380> SET foo hi
(error) READONLY You can't write against a read only replica.

因為預設情況下,從資料庫是隻讀的。

可以通過設定從資料庫的配置檔案中的 slave-read-only 為 no 以使從資料庫可寫,但是因為對從資料庫的任何更改都不會同步給任何其他資料庫,並且一旦主資料庫中更新了對應的資料就會覆蓋從資料庫中的改動,所以通常的場景下不應該設定從資料庫可寫,以免導致易被忽略的潛在應用邏輯錯誤。

配置多臺從資料庫的方法也一樣,在所有的從資料庫的配置檔案中都加上 slaveof引數指向同一個主資料庫即可。

除了通過配置檔案或命令列引數設定 slaveof引數,還可以在執行時使用 SLAVEOF命令修改:

127.0.0.1:6380> SLAVEOF 192.168.0.106 6379

如果該資料庫已經是其他主資料庫的從資料庫了,SLAVEOF 命令會停止和原來資料庫的同步轉而和新資料庫同步。此外對於從資料庫來說,還可以使用 SLAVEOF NO ONE命令來使當前資料庫停止接收其他資料庫的同步並轉換成為主資料庫。

原理

當一個從資料庫啟動後,會向主資料庫傳送 SYNC 命令。同時主資料庫接收到 SYNC命 令後會開始在後臺儲存快照(即RDB持久化的過程),並將儲存快照期間接收到的命令快取起來。當快照完成後,Redis會將快照檔案和所有快取的命令傳送給從資料庫。從資料庫收到後,會載入快照檔案並執行收到的快取的命令。以上過程稱為複製初始化。複製初始化結束後,主資料庫每當收到寫命令時就會將命令同步給從資料庫,從而保證主從資料庫資料一致。

讀寫分離與一致性

通過複製可以實現讀寫分離,以提高伺服器的負載能力。在常見的場景中(如電子商務 網站),讀的頻率大於寫,當單機的Redis無法應付大量的讀請求時(尤其是較耗資源的請求,如 SORT 命令等)可以通過複製功能建立多個從資料庫節點,主資料庫只進行寫操作,而從資料庫負責讀操作。這種一主多從的結構很適合讀多寫少的場景,而當單個的主資料庫不能夠滿足需求時,就需要使用Redis 3.0 推出的叢集功能。

從資料庫持久化

另一個相對耗時的操作是持久化,為了提高效能,可以通過複製功能建立一個(或若干個)從資料庫,並在從資料庫中啟用持久化,同時在主資料庫禁用持久化。當從資料庫崩潰重啟後主資料庫會自動將資料同步過來,所以無需擔心資料丟失。

然而當主資料庫崩潰時,情況就稍顯複雜了。手工通過從資料庫資料恢復主資料庫資料時,需要嚴格按照以下兩步進行。

  1. 在從資料庫中使用 SLAVEOF NO ONE命令將從資料庫提升成主資料庫繼續服務。
  2. 啟動之前崩潰的主資料庫,然後使用SLAVEOF命令將其設定成新的主資料庫的從 資料庫,即可將資料同步回來。

【注意】

當開啟複製且主資料庫關閉持久化功能時,一定不要使用 Supervisor 以及類似的程序管理工具令主資料庫崩潰後自動重啟。同樣當主資料庫所在的伺服器因故關閉時,也要避免直接重新啟動

這是因為當主資料庫重新啟動後,因為沒有開啟持久化功能,所以資料庫中所有資料都被清空,這時從資料庫依然會從主資料庫中接收資料,使得所有從資料庫也被清空,導致從資料庫的持久化失去意義。

無論哪種情況,手工維護從資料庫或主資料庫的重啟以及資料恢復都相對麻煩,好在Redis提供了一種自動化方案哨兵來實現這一過程,避免了手工維護的麻煩和容易出錯的問題。

無硬碟複製

前面說過 Redis 複製的工作原理時介紹了複製是基於RDB方式的持久化實現的,即主資料庫端在後臺儲存 RDB 快照,從資料庫端則接收並載入快照檔案。

這樣的實現優點是可以顯著地簡化邏輯,複用已有的程式碼,但是缺點也很明顯。

  1. 當主資料庫禁用RDB快照時(即刪除了所有的配置檔案中的save語句),如果執行了複製初始化操作,Redis依然會生成RDB快照,所以下次啟動後主資料庫會以該快照恢復資料。因為複製發生的時間不能確定,這使得恢復的資料可能是任何時間點的。
  2. 因為複製初始化時需要在硬碟中建立RDB快照檔案,所以如果硬碟效能很慢(如網路硬碟)時這一過程會對效能產生影響。舉例來說,當使用 Redis 做快取系統時,因為不需要持久化,所以伺服器的硬碟讀寫速度可能較差。但是當該快取系統使用一主多從的叢集架構時,每次和從資料庫同步,Redis都會執行一次快照,同時對硬碟進行讀寫,導致效能降低。

因此從2.8.18版本開始,Redis引入了無硬碟複製選項,開啟該選項時,Redis在與從資料庫進行復制初始化時將不會將快照內容儲存到硬碟上,而是直接通過網路傳送給從資料庫,避免了硬碟的效能瓶頸。

可以在配置檔案中使用如下配置來開啟該功能:

repl-diskless-sync yes