昨天介紹了虛擬表,尤其是MTV,更是將要Query的資料與Tx完全分離,達到OLAP的目的。
而今天要介紹的是讀寫分離,一樣是將要Query的資料與Tx完全分離,
但這裡乾脆直接複製一份db副本來應付所有Query,而做write動作的tx全都在master db server上。
今天將會簡單的建立一個讀寫分離,稍微提一下實作讀寫分離時所帶來的問題。
還記得之前介紹的WAL機制嗎?DB在寫表之前為了希望資料庫能快點回應我們 Committed,
先把修改的操作都先以日誌的方式存起來,等到比較不忙時,
在將日誌回放(Replay),處理 index,FSM...的,正確的存到 Table 之中。
而這個WAL log就是我們拿來做讀寫分離的關鍵所在。
當wal log新增紀錄,就代表還有資料尚未寫入硬碟之中,
master更新wal log,然後透過同步WAL log的方式讓slave 將資料寫入。
slave透過WAL 回放(Redo,roll-forward recovery)機制,不斷的寫入新的資料。
關鍵就是wal log的存取,所以可以有各種花式的存取
,
可以透過以下兩大類同步wal log的方式來做混和。
要建構一個簡單的系統,最簡單又方便的莫非就是直接寫dokcer-compose了。
這裡使用bitnami/postgresql這個image來手把手的做讀寫分離。
# ./compose.yaml
version: '2'
services:
postgresql-master:
image: 'bitnami/postgresql:latest'
ports:
- '5432'
environment:
- POSTGRESQL_REPLICATION_MODE=master
- POSTGRESQL_REPLICATION_USER=repl_user
- POSTGRESQL_REPLICATION_PASSWORD=repl_password
- POSTGRESQL_USERNAME=my_user
- POSTGRESQL_PASSWORD=my_password
- POSTGRESQL_DATABASE=my_database
postgresql-slave:
image: 'bitnami/postgresql:latest'
ports:
- '5432'
depends_on:
- postgresql-master
environment:
- POSTGRESQL_REPLICATION_MODE=slave
- POSTGRESQL_REPLICATION_USER=repl_user
- POSTGRESQL_REPLICATION_PASSWORD=repl_password
- POSTGRESQL_MASTER_HOST=postgresql-master
- POSTGRESQL_MASTER_PORT_NUMBER=5432
- POSTGRESQL_PASSWORD=my_password
啟動時,直接docker-compose up
即可,
如果想開多個slave,可以加上--scale
,
像這樣docker-compose up --detach --scale postgresql-master=1 --scale postgresql-slave=3
這樣就成功建立了一個簡單的讀寫分離的環境了,就是這麼簡單。
這裡要特別注意一下,REPLICATION_USER是沒有任何權限的,單純拿來做WAL LOG的同步。
你可以新建一個USER來做READ操作,也可以給予這個帳號READ權限,
或者直接使用POSTGRESQL_USERNAME與POSTGRESQL_PASSWORD來做操作。
你可以使用docker ps看一下目前node port開在哪裡,
再隨意寫個程式或DB IDE來連線,
看到master建立table,slave也會隨著建立table。
master插入資料,slave也會隨著插入資料。
至於如果你想要在slave做write操作看master會不會隨著write
你可以看到這個錯誤訊息ERROR: cannot execute INSERT in a read-only transaction
。
在orm中我們可以自由的選擇使用哪條連線來做tx
dataSource.createQueryRunner("master")
有人可能會問,一個tx又讀又寫會發生什麼事情?
該不會一部分sql執行在slave,一部分執行在master吧?這樣怎麼維持acid?
聰明的你一定不會這麼想,因為一個tx只會對應到一條連線,
想當然這整個tx只會選擇master或slave來做連線。
當如果有write操作時,就是直接選master來做該tx。
我們知道master/slave之間的資料是利用同步wal log的方式來完成,
既然是同步檔案,就一定會有時間差
想像一個情境,如擬更新了你的資訊,
但查看時卻還是舊的資訊,所以你又在更新了一次,點進去看又正常了?
其實有可能就是同步時的時間差。無法讀己之寫。
這個時候我們可以想辦法讓write後的request也讀master的資料,
就可以達到讀己所寫的目的了,不論是紀錄client資訊+debounce time等方式。
其實做讀寫分離並不困難,困難的是在該架構下隨之而來的問題。
這裡只是將較常見的讀己之寫問題寫出,其實不只這些,還有許許多多問題可以探討。
一方面難Debug,另一方面也增加了程式設計的難度,要特別注意有哪些side effect。
這些議題,將會再下次介紹給大家。