iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0

Day 14、15 的 RDB 跟 AOF,檔案都存在同一台機器的硬碟上。硬碟壞了、整台機器掛了,檔案一樣跟著沒。

主從複製就是多開一台 Redis,主節點寫進去的東西,自動複製一份到從節點。主節點負責寫,從節點跟著抄。

常用指令

  • REPLICAOF 主機 port(當某一台的從節點,也可以寫在啟動參數)
  • ROLE(看自己是主節點還是從節點)
  • INFO replication 的 master_link_status(從節點跟主節點還連著嗎)

開兩台

Day 2 開的 redis30days 不動,在 Day 2 的 docker-compose.yml 旁邊另外寫一份 docker-compose-replica.yml:

# Day 16 主從複製:一主一從,跟 Day 2 的 redis30days 分開跑
name: day16 # 網路會叫 day16_default

services:
  redis-master:
    image: redis:7-alpine
    container_name: redis-master

  redis-replica:
    image: redis:7-alpine
    container_name: redis-replica
    command: redis-server --replicaof redis-master 6379 # 當 redis-master 的從節點

這邊是在同一台電腦開兩個容器來模擬,正式環境的主從要放在不同機器上,不然電腦一掛還是一起消失。

兩台都沒有對外開 port,全部用 docker exec 操作,不會跟 redis30days 搶 6379。

先只開主節點,灌 10 萬筆資料(在主機的終端機下):

docker compose -f docker-compose-replica.yml up -d redis-master   # -f 指定用哪一份 compose 檔

for i in $(seq 1 100000); do echo "SET user:$i $i"; done | docker exec -i redis-master redis-cli --pipe

再開從節點(在主機的終端機下):

docker compose -f docker-compose-replica.yml up -d redis-replica

https://ithelp.ithome.com.tw/upload/images/20260928/20184209d3VClGBUfR.png

全量同步

從節點第一次連上,要先拿到主節點的全部資料。等 5 秒左右看主節點的 log(grep -i sync 只留跟同步有關的行,在主機的終端機下):

docker logs redis-master | grep -i sync

https://ithelp.ithome.com.tw/upload/images/20260928/20184209HtlrpMsDOF.png

Full resync 就是全量同步。Starting BGSAVE 是 Day 14 那招:fork 一個副本拍一份 RDB,只是這次不寫硬碟,直接從網路傳給從節點。中間停的 5 秒左右,是在等看看還有沒有別台要一起同步。

再看從節點這邊(在主機的終端機下):

docker logs --tail 9 redis-replica

https://ithelp.ithome.com.tw/upload/images/20260928/201842099We7HSit8w.png

從節點先把自己原本的資料清空(Flushing old data),再載入主節點傳來的 RDB。keys loaded: 100000,10 萬筆都過來了。

看一下兩台現在的身分(在主機的終端機下):

docker exec redis-master redis-cli ROLE

docker exec redis-replica redis-cli ROLE

https://ithelp.ithome.com.tw/upload/images/20260928/2018420903qX4ymwc9.png

https://ithelp.ithome.com.tw/upload/images/20260928/20184209XiYjm7qaaL.png

一台 master,一台 slave(從節點的舊稱)。主節點底下列著從節點的 IP,從節點這邊寫著跟的是 redis-master,connected 就是連上了。

從節點不能寫

在從節點寫一筆試試看(在主機的終端機下):

docker exec redis-replica redis-cli SET price 1

https://ithelp.ithome.com.tw/upload/images/20260928/20184209QA3THj1b2u.png

從節點預設唯讀,寫入只能找主節點。常見的用法是寫去主節點、讀去從節點,讓從節點分擔讀的流量,叫做讀寫分離。

拔掉網路線

主節點自己寫完就回 OK,不會等從節點,複製是之後才送過去的。平常一下子就傳到了,看不出來,所以把從節點的網路線拔掉,讓它傳不過去(在主機的終端機下):

docker exec redis-master redis-cli FLUSHALL             # 先清空所有資料

docker exec redis-master redis-cli SET price 100

docker exec redis-master redis-cli SET coupon 50 EX 60  # 60 秒後過期,等等用

docker network disconnect day16_default redis-replica   # 拔掉從節點的網路線

docker exec redis-master redis-cli SET price 200

docker exec redis-replica redis-cli GET price

https://ithelp.ithome.com.tw/upload/images/20260928/20184209vsRhJjtIPG.png

主節點成功寫入 200,但從節點還是讀到 100。如果主節點這時候掛了,200 這筆就跟著沒了,從節點從來沒拿到過。

過期 key 從節點不刪

Day 12 說過,過期 key 只有主節點會刪。等 1 分鐘讓 coupon 過期,兩邊各看一次(在主機的終端機下):

docker exec redis-master redis-cli DBSIZE          # 1(price)

docker exec redis-replica redis-cli EXISTS coupon  # 0

docker exec redis-replica redis-cli DBSIZE         # 2(price & coupon)

主節點已經刪了,剩 1 個 key。從節點說 coupon 不存在(0),DBSIZE 卻還是 2:過期了它會當作沒有,但不會自己刪,要等主節點的 DEL 傳過來。

增量同步

從節點超過 60 秒沒收到主節點的消息,會自己判定斷線。多等一下,看到 down 再把網路線接回去(太早接回去,要等比較久才會補上,在主機的終端機下):

docker exec redis-replica redis-cli INFO replication | grep master_link_status

https://ithelp.ithome.com.tw/upload/images/20260928/20184209hdeZdkbVni.png

接回去,等幾秒再看主節點的 log(在主機的終端機下):

docker network connect day16_default redis-replica

docker logs redis-master | grep -i sync

https://ithelp.ithome.com.tw/upload/images/20260928/20184209j4UFLnay3y.png

這三行是:主節點先把逾時的從節點踢掉,從節點重連回來,Partial resynchronization(增量同步)只補斷線這段缺的 142 bytes(SET price 200、DEL coupon,加上主節點每 10 秒一次的 PING),沒有再 BGSAVE 一次。

從節點這邊也補上了(在主機的終端機下):

docker exec redis-replica redis-cli GET price  # 200(price)

docker exec redis-replica redis-cli DBSIZE     # 1

200 過來了,coupon 也被主節點的 DEL 刪掉了。

主節點會把最近的寫入留一份在記憶體(repl-backlog-size,預設 1MB),從節點回來就從這裡補。斷線期間寫超過 repl-backlog-size 大小,就只能再全量同步一次。

為什麼要在意

  • 主節點回 OK 之後、傳到從節點之前掛掉,那幾筆就沒了。 跟 Day 15 的 everysec 是同一類問題,只是換成兩台之間。想確認從節點收到了,可以寫完再下 WAIT(等幾台從節點確認),但它只負責回報節點確認數量,沒等到也不會把寫入收回來。
  • 讀寫分離會讀到舊資料。 剛改完的資料馬上去從節點讀,可能還是舊的。一定要讀到剛寫進去的東西,就讀主節點。
  • 主從不是備份。 主節點刪掉的,從節點跟著刪(剛剛的 FLUSHALL,從節點上那 10 萬筆 user 也一起清掉了)。主節點沒開持久化又重開,會空著回來,從節點照樣全量同步,先清空自己再載入一份空的,兩台一起空掉。

明天

主節點掛了,從節點不會自己升上去,只會一直重連。明天來看 Sentinel,讓它自動挑一台從節點接手/images/emoticon/emoticon61.gif


上一篇
Day 15|AOF
下一篇
Day 17|Sentinel
系列文
從購物車到秒殺:30 天 Redis 高併發自學筆記 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言