自我介紹

暱稱: Hohshen

目前擔任: 肝苦碼農

DB Lock

By Hohshen

目錄

  1. 這樣做才不會受傷
  2.  對, 就是在這裡
  3. 沒有你的世界
  4. 捆綁上鎖
  5. 兩位小鎖匠
  6. 芭比Q惹
  7. 我們一起去惹

隱藏黑暗力量的鑰匙啊,

在我面前顯示真正的力量吧!

跟擬定下約定的BESG命令你,封印解除!

這樣做才不會受傷

大家都知道鎖要上在c.s.上,
但鎖的對象是誰要特別注意

Application Concurrent Access

public class HelloRunnable implements Runnable {
    @Override
    public void run() {
        int c = counter.get();
        c++; // c--;
        counter.set(c);
    }
    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
        (new Thread(new HelloRunnable())).start();
    }
}

Application Concurrent Access

Application Concurrent Access

time thread A thread B C
1 get c //c=0 0
2 get c //c=0 0
3 c++ //c=1
4
c++ //c=1
5 set c //c=1
1
6 set c //c=1 1
public void run() {
        int c = counter.get();
        c++; // c--;
        counter.set(c);
}
class Counter {
    private int c = 0;
    public void set(int c) { This.c = c; }
    public int get() { return c; }
}

Synchronization

class Counter {
    private int c = 0;
    public synchronized void set(int c) {
        this.c = c;
    }
    public synchronized int get() {
        return c;
    }
}

class Counter {
    private int c = 0;
    public synchronized void set(int c) {
        this.c = c;
    }
    public synchronized int get() {
        return c;
    }
}

Synchronization

time thread A thread B C
1 get c //c=0 0
2 block
3 get c //c=0 0
4 c++ //c=1
5 c++ //c=1
6 set c //c=1 1
7 block
8 set c //c=1 1

Still Wrong!

A1: callee provides atomic methods

time thread A thread B C
1 get c //c=0 0
2 get c //c=0 0
3 c++ //c=1 1
4 block
5 c++ //c=1 2
6 get c //c=2
7 get c //c=2

class Counter {
    private int c = 0;
    public synchronized increment() {
        this.c++;
    }
    public int get() {
        return c;
    }
}

class Counter {
    private int c = 0;
    public synchronized increment() {
        this.c++;
    }
    public int get() {
        return c;
    }
}

A2: callers set c.s.

time thread A thread B C
1 get c //c=0 0
2 c++ //c=1 0
3 set c //c=1 1
4 block
5 get c //c=1 1
6 c++ //c=2 1
7 set c //c=2 2

public synchronized void run() {
        int c = counter.get();
        c++; // c--;
        counter.set(c);
}

public synchronized void run() {
        int c = counter.get();
        c++; // c--;
        counter.set(c);
}

補充: blocked v.s. waiting

blocked:
    outside c.s.

waiting:
    in a c.s., stop and enter waiting state

對, 就是這裡

對應到資料庫呢?

先回想一下鎖的功用

wLock.lock();
try {
    content += line;
} finally {
    wLock.unlock();
}

tx.start()
try {
   // some sql instructions
} finally {
    tx.commit();
}

剛好對應上了TRANS
其實TX就是一種C.S.

問大家一個選擇題

題目:
我們操作DB時, 大部分都只是單句的CRUD,
單句的CRUD大約佔了80%的使用量,
在資料庫中, 何時會使用到tx

A. 因為是單句CRUD,所以剩下的20%使用tx
B. 100%全部都使用到tx
C. 0%使用tx, 我都直接在應用層上鎖
D. 以上皆非

乾, 完蛋,
我開始意識到生命危險

Skip~

快右滑!

其實在db內是100%使用tx

就算只有一句也是tx

default值是把auto commit=ture
所以丟進來後會自動幫忙加上tx start,tx commit

因為tx才是真正操作單位
default isolation level=Read Committed

Serialized or interleaved operations?

交錯的操作tx可以增加throughput

透過pipelining cpu and io

沒有你的世界

剛剛知道了,
db是multi-thread system,
為了增加throuthput,
但如果在tx內沒處理好鎖呢?

假設沒有在讀寫實作上任何鎖,將會發生甚麼事情

居然可以讀到尚未 commit 的值,
表示在做 update 的過程中,直接改到 db 上

time tx1 tx2
1 BEGIN
2 select * from "persons" where "id"=0;
3 BEGIN
4 update "persons" set "name"='tx2' where "id"=0;
5 select * from "persons" where "id"=0; //name=tx2
6 ROLLBACK

發生Dirty read

沒關係, 我改!

只可以讀取commit完的資料,
沒commit的不給讀

time tx1 tx2
1 BEGIN
2 select * from "persons" where "id"=0;
3 BEGIN
4 update "persons" set "name"='tx2' where "id"=0;
5 select * from "persons" where "id"=0; //結果同上句
6 ROLLBACK

But!

告訴大家只可以讀已經commit的資料

 

確實是只讀commit的資料,
但怎麼會同一個tx讀出的結果居然不同

發生non repeatable read
來! 我讀過後不能改

time tx1 tx2
1 BEGIN
2 select * from "persons" where "id"=0;
3 BEGIN
4 update "persons" set "name"='tx2' where "id"=0;
5 commit
6 select * from "persons" where "id"=0; //name=tx2

沒關係, 我改!

只可以讀取commit完的資料,
讀過的上鎖不給改

time tx1 tx2
1 BEGIN
2 select * from "persons" where "id"=0;
3 BEGIN
4 (blocking)update "persons" set "name"='tx2' where "id"=0;
5 select * from "persons" where "id"=0; //結果同上句

But!

沒關西,
我插入!
我刪除!

發生Phatom幻讀
來! 我就鎖整張表!

告訴大家只可以讀過後不能改!

 
time tx1 tx2
1 BEGIN
2 select count(*) from "persons"
3 BEGIN
4 insert "persons" values ...
5 commit
6 select count(*) from "persons"

確實是讀過的不能改,
但怎麼會同一個tx統計出來的不同

沒關係, 我改!

只可以讀取commit完的資料,
讀過的上鎖不給改
且查詢時鎖整張表

time tx1 tx2
1 BEGIN
2 select count(*) from "persons"
3 BEGIN
4 (blocking)insert "persons" values ...
5 select count(*) from "persons"

 write skew同理(Phantom Read 的一種)
(serializable主要就是解此問題)

由剛剛的問題可以知道

如果要解決上面的問題
必須在資料或表加上鎖

捆綁上鎖

該如何上鎖呢?有哪些鎖可以使用?

IO的操作基本上分為兩類: 讀,寫
不論是對於一筆資料或一個資料表

問題目標

Semphore讀者優先範例

//common
Semphore RWMutex=Semphore(1);//讀寫控制,使讀寫互斥
int RCount=0;//Read數
Semphore RCountMutex=Semphore(1);//Read數控制
// Writer
void Writer(){
  P(RWMutex)
  write()
  V(RWMutex)
}

單純寫時上鎖,
寫完解鎖

void Reader(){
  P(RCountMutex);
  if(RcountMutex==0){
    P(RWMutex); //要求讀取權限
  }
  RCount++;
  V(RCountMutex);

  read()
    
  P(RCountMutex);
  RCount--;
  if(RcountMutex==0){
    V(RWMutex); //釋放讀取權限
  }
  V(RCountMutex);
}

讀時紀錄有幾個讀,
全部讀完才釋放

類型有三種:

  1. 讀者優先
  2. 寫者優先
  3. 讀寫公平

問題:
讀者優先,
只要一直有讀,
寫就進不來(飢餓)

規則:

  1. R+R=>OK
  2. R+W=X
  3. W+W=X

上鎖的規則

歸納出一個結論

1. 寫寫互斥 - 寫的過程中不給其他tx寫

2. 讀寫互斥 - 讀的過程中不給其他tx寫

3. 讀讀不互斥 - 反正都讀一樣的資料

4. 寫大於讀 - 寫的限制大於讀, 讀鎖可以升級寫鎖, 反之亦然

基本上可以分為兩類鎖:

1. 讀鎖:
    讀時可以讓其他tx讀, 但不可以讓其他tx讀寫
2. 寫鎖:
    寫時不可以讓其他tx讀寫

這就是鼎鼎大名的
2PL(Two-Phase Locking)

2PL 協議將事務分爲兩部分:

  • 擴展階段(獲取鎖,並且不允許釋放鎖)

  • 收縮階段(釋放所有鎖,並且無法進一步獲取其他鎖)。

意味著?

Starvation=>
S2PL(Strict Two-Phase Locking)

Lock Table

鎖上一筆紀錄

使用讀寫鎖上鎖
讀鎖:  S(),  Shared (S) lock
寫鎖:  X(), Exclusive (X) lock 

鎖上一個範圍呢?

鎖上一個範圍呢?

lock granularity (lock 粒度)

multiple granularity locking (MGL)

DB=DB
Table=Table
Pages=records
Tuples=record

先知道鎖一個records

實做一下IS,IX

Tx1(blue): wants to share-lock a record
Tx2(green): wants to exclusive-lock a file

Locks need to be released in leaf-to-root orde

Intention-shared (IS) 意向共享鎖
Intention-exclusive (IX) 意向互斥鎖

隔離層級

透過這些鎖我們就可以時做出隔離層級,
並解決剛剛提出的問題

level  read update/delete insert
Read UnCommit no IX File,Block;
X Record;
X File,Block,Record;
Read Commit IS File,Block,Record=>release;
S Record=>release
IX File,Block;
X Record;
X File,Block,Record;
Repeatable Read IS File,Block,Record=>release; S Record IX File,Block;
X Record;
X File,Block,Record;
Serializable IS File,Block;
S Record;
IX File,Block;
X Record;
X File,Block,Record;
level dirty reads Unrepeatable read phantoms
Read UnCommit T T T
Read Commit F T T
Repeatable Read F F T
Serializable F F F

由該表可知

Read commit: 只讀有commit過的, 解決了dirty read
Repeatable: 讀過的不能改,解決了Nonrepeatable
Serializable: 使用過的地方會標上標記, 解決了Phantom

level dirty reads Unrepeatable read phantoms
Read UnCommit T T T
Read Commit F T T
Repeatable Read F F T
Serializable F F F

isolation level並不是避免race condition的出現,
而是對於race condition side effect的最大容忍程度

 

讓 Race condition 發生後不出現「致命錯誤」,讓系統向不致命的方向發生錯誤

Title Text

Subtitle

分類一下鎖

兩位小鎖匠

這是一個鎖匠的故事,  在很久很久以前......有兩個鎖匠

第一個鎖.......

DB lock

  • 悲觀鎖(Pessimistic Lock)

    • 隱式鎖

    • 顯示鎖

什麼是隱式鎖

就是剛剛講的isolation level
db預設幫你加上的

什麼是顯式鎖?

自己加上的鎖

e.g.
select ... for share
select .... for update

為什麼需要顯式鎖-舉個例子1

Conflict promotion:
     在read commit level 想要讀過不能改, 即加上s lock
     e.g. 購物系統中, 賣太好, 想中途改變價格
             1. 前面訂的要算數, 且是原價
             2. 正在進行中的交易不能開價到結帳不同

time tx1 tx2
1 BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
2 select * from "product" where "id"=0 for share;
3 BEGIN ... READ COMMITTED;
4 update inventory set balance = balance-1 (blocking or not)update "product" set "price"='100' where "id"=0;
5 commit

為什麼需要顯式鎖-舉個例子2

Conflict materialization:
     在read commit level 想要插入新資料不照成rr problem,
     即加上x lock
     e.g. 商品系統中, 想中途改加上該商品的更多細節
             1. 正在進行中的不能讀兩次都不同

time tx1 tx2
1 BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
2 select * from "product" where "id"=0 for share; BEGIN ... READ COMMITTED;
3 (blocking or not) Select * from "product" where "id"=0 for update;
4 select * from "product_detail" where "id"=0;
5 commit
6 insert "product_detail" ....

為什麼需要顯式鎖

盡量統一隔離層級,如果有特殊需要,自行加上鎖;

大部分的應用都是Read Committed 中使用 Conflict promotion + Conflict materialization
便足夠了,不建議用更高階的 isolation

有悲觀鎖?有樂觀鎖嗎?





樂觀鎖( Optimistic Lock)

有多樂觀?
樂觀能解決問題嗎?

id name num
1 iphone 10

一般的表

樂觀鎖- 先看悲觀實例

id name num
1 iphone 10

一般的products表

begin
// 讓其他tx不可讀寫
select num from products where id=1 for update
//更新數量
update products set num=num-1 where id=1
//查詢最後剩餘多少
select num from products
commit

其他tx在select時就會block住了(轉圈圈),
直到第一個tx釋放鎖後開始執行

樂觀鎖- 先看樂觀實例

id name num version
1 iphone 10 0

加上version(or timestamp) 的 products表

begin
select num from products where id=1 //version=0
update products set num=num-1, version=version+1 \
              where id=1 and version=0
select num from products
commit

其他tx在select時會取得相同version
但更新時where不到version=0
所以就更新0筆記錄

過程中完全沒上
任何顯式鎖

樂觀鎖是好的方案嗎?

對於db演進的發展

先拉回來我們剛剛認識的db

等等...那隱式鎖呢?
等等...那不就是說R會block U?
等等...那不就是說會常常deadlock?
等等...那把commit的時間點拉長不就gg了

完了丸了八比Q了

先拉回來我們剛剛認識的db

Timeout & rollack (deadlock detection)
Wait-die (deadlock prevention)

陪你一起學習學校沒教的知識

好像沒真的被鎖上誒?

 

  • MVCC 的資料庫的 Record 只有 X lock ,而沒有 S lock

  • MVCC 在讀取時,會最該 Record 「最新」的 Committed 版本,所以自然地沒有了 Dirty Read

  • 因為讀不會被阻擋, 所以只有write-werit confliet

類似剛剛的樂觀鎖,在資料表上加上version欄位,
每次的insert/update都會額外增加版本

當前讀

MVCC 實作- postgres 為例0

其實Pg每個表中都有一些隱藏欄位
(如要顯示,要在create table時with oids)

欄位 代表意思
oid resourceId(table,index, view,record....都有的id)//objectId
ctid (ItemPointer), 在表中的物理位置 //current tuple(record) pointer id
xmin born 建立record時, 當前tx的id
xmax die 刪除record時, 當前tx的id, default=0
cmin 當前tx最早的txid, 從0開始
cmax 下個tx進來取的號碼牌

MVCC 實作- postgres 為例1-insert

ctid xmin xmax cmin cmax oid id clog
(0,1) 80853357 0 0 0 17569 1
0x01 已提交
(0,2) 80853358 0 0 0 17570 2 0x01 已提交
(0,3) 80853359 0 0 0 17571 3 0x01 已提交

insert 3筆記錄

那該如何判斷該record有效呢?
pg把tx的狀態記錄到clog (commit log)中,
每讀一行就會到該文件檢查tx的狀態, 狀態如下

  • #define TRANSACTION_STATUS_IN_PROGRESS=0x00 正在进行中
  • #define TRANSACTION_STATUS_COMMITTED=0x01 已提交
  • #define TRANSACTION_STATUS_COMMITTED=0x02 已回滚
  • #define TRANSACTION_STATUS_SUB_COMMITTED=0x03 子事务已提交

MVCC 實作- postgres 為例2-update

begin
select txid_current() //80853360
update t1 set id=4 where id=1

ctid xmin xmax cmin cmax oid id clog
(0,1) 80853357 80853360 0 0 17569 1 0x00 進行中
(0,2) 80853358 0 0 0 17570 2 0x01 已提交
(0,3) 80853359 0 0 0 17571 3 0x01 已提交
(0,4) 80853360 0 0 0 17569 4 0x00 進行中

tx2=80853361

tx1= 80853360

MVCC 實作- postgres 為例3-delete

delete from t1 where id=2

ctid xmin xmax cmin cmax oid id
(0,1) 80853357 80853360 0 0 17569 1
(0,2) 80853358 80853360 1 1 17570 2
(0,3) 80853359 0 0 0 17571 3

tx2=80853361

tx1= 80853360

由此可知: PG在做delete,udpate時,並不會真的刪除,更新.
更新: 而是插入新的tuple(record)後, 把原紀錄標記刪除
刪除: 把原紀錄標記刪除

由tx2可以看到之前版本的信息(都是用標記的),
不同tx擁有各自的數據空間 ,
不會對彼此產生干擾, 保證事務的隔離性

ctid xmin xmax cmin cmax oid id clog
(0,1) 80853357 80853360 0 0 17569 1 0x00 進行中
(0,2) 80853358 80853360 1 1 17570 2 0x00 進行中
(0,3) 80853359 0 0 0 17571 3 0x01 已提交
(0,4) 80853360 0 0 0 17569 4 0x00 進行中

MVCC 實作- postgres為例4-tx1 commit後

commit

tx1= 80853360

commit後,寫入in memory 內的東西
有xmax的刪除掉,
新增的ctdi存起來

ctid xmin xmax cmin cmax oid id clog
(0,1) 80853357 80853360 0 0 17569 1 0x01 已提交
(0,2) 80853358 80853360 1 1 17570 2 0x01 已提交
(0,3) 80853359 0 0 0 17571 3 0x01 已提交
(0,4) 80853360 0 0 0 17569 4 0x01 已提交

MVCC 實作- postgres為例4-tx1 rollback後

rollback

tx1= 80853360

rollback後,把in memory內的東西drop掉
但, xmax居然沒還原回去

ctid xmin xmax cmin cmax oid id
(0,1) 80853357 80853360 0 0 17569 1
(0,2) 80853358 80853360 1 1 17570 2
(0,3) 80853359 0 0 0 17571 3
(0,4) 80853360 0 0 0 17569 4

基於效率的原因, 如果rollback也清除標記,
就變成要找到該列, 更新,(非整個record的操作)降低了效能

ctid xmin xmax cmin cmax oid id clog
(0,1) 80853357 80853360 0 0 17569 1 0x02 已回滚
(0,2) 80853358 80853360 1 1 17570 2 0x02 已回滚
(0,3) 80853359 0 0 0 17571 3 0x01 已提交
(0,4) 80853360 0 0 0 17569 4 0x00 已回滚

MVCC 實作- postgres為例4-tx1 rollback後

ctid xmin xmax cmin cmax oid id
(0,1) 80853357 80853360 0 0 17569 1
(0,2) 80853358 80853360 1 1 17570 2
(0,3) 80853359 0 0 0 17571 3
(0,4) 80853360 0 0 0 17569 4

基於效率的原因, 如果rollback也清除標記,
就變成要找到該列, 更新,(非整個record的操作)降低了效能

MVCC 實作- postgres為例5-
rollback垃圾問題

MVCC 實作- postgres為例5-
rollback垃圾問題

ctid xmin xmax cmin cmax oid id
(0,1) 80853357 80853360 0 0 17569 1
(0,2) 80853358 80853360 1 1 17570 2
(0,3) 80853359 0 0 0 17571 3
(0,4) 80853360 0 0 0 17569 4

可以看到
ctid(0,1)更新操作下遺留的標記
ctid(0,2)刪除操作下留下的標記
頻繁操作的表會累積大量標記, 佔用硬碟空間
 

PG的解決方法是提供vacuum命令來清理過期的數據(下集待續)

兩種 isolation 流派

 

 SX lock 和 MVCC 都能逹到 Isolation 目的,

MVCC: PostgreSQL, Oracle
SX lock: MySQL, MSSQL
 

因為其底層機制不同,所以雖然大家都說自己支持 ACID ,
支持 4個 isolation level ,其 isolation 的行為卻有所不同

MySQL 和 MSSQL 近年試圖在其 SX lock 架構上再加上 MVCC (😱

SX lock 還是 MVCC?

高流量環境, MVCC 比較高效能。

因為 MVCC 的 READ 永遠不被阻擋
同一時間資料庫能處理更多的 TX


MVCC 只有 X lock 而沒有 S lock
其 Deadlock detector 要管理的 lock 的數目一定更少,
所以一定比較快

只考慮一個 TX , MVCC 使用比較多的 IO 還有 CPU ,所以比 較花時間

1. 決定那個版本才是「最新」
2. 管理舊的版本的刪除

MVCC is type of Optimistic Lock?

Yes!

Two widely known algorithms of
optimistic concurrency control are:

begin~commit的時間很長

SX lock:
更新某資料後就卡死,無法讀取,且容易deadlock

MVCC :
更新某資料後, 仍然可讀,
但如加上顯式鎖仍然如同SX lock

Mysql RR problem

Mysql RR problem
(5.7,8.0可重現)

RR本來就不防幻讀,
如果有人跟你說mysql RR發生幻讀,
你要跟他說每個db的RR都會發生幻讀

RR是防止同一列讀第二次數值不同.
理想上read/write會抓到同一個版本的值,而非最新commit點. mysql只符合read version相同.

MVCC 實作- Mysql為例0

Mysql 會在每筆紀錄的最後加上trx_id,
來紀錄最後一次修改該row的交易ID

id name num (trx_id)
1 iphone 10 0

假設此筆資料為建立table時隨之建立的,
所以trx_id=0 (不是重點)

當下select指令時, 會產生一個叫ReadView(snapshot)的表

欄位 代表意思
m_ids 當前還沒commit/rollback的tx_ids
min_trx_id 最小的m_ids(最早的tx)
max_trx_id 下一個tx_id的號碼牌
creator_trx_id 創建此ReadView的txId

MVCC 實作- Mysql為例0-ReadView

欄位 代表意思
m_ids 當前還沒commit/rollback的tx_ids
min_trx_id 最小的m_ids(最早的tx)
max_trx_id 下一個tx_id的號碼牌
creator_trx_id 創建此ReadView的txId

會透過以下四個if來決定該record是否該被當前tx看到

判斷 代表意思 可否讀
trx_id=creator_trx_id 該readview是自己建立的
trx_id<min_trx_id 已經commit過的tx
trx_id>max_trx_id 該row是此readView出生後才建立的 不可
min_trx_id<
trx_id<max_trx_id
看trx_id是否在m_ids中
存在: 尚未commit=>不可讀
不存在: 已經commit=>可讀
存在: 不可
不存在: 可
 

該row的

MVCC 實作- Mysql為例0-ReadView

看到問題了嗎?
只有對read做mvvc

MVCC 實作- Mysql為例-RR problem 

time tx1 tx2
1 tx(RR)
2 select num from products where id=1
3 tx(RR)
4 select num from products where id=1
5 Update products set num=num-10 where  id=1
//10-10=0
6 commit
7 Update products set num=num-5 where  id=1
//理應10-5=5, 但發生了0-5=-5
這裡pg會throw error
8 commit

快照讀, 當前寫

Q: why time 5 is ok? mvvc即為不加鎖的讀

解法: for update or DIY Optimistic Lock

MVCC 實作- 此範例如果在pg呢?

time tx1 tx2
1 tx(RR)
2 select num from products where id=1
3 tx(RR)
4 select num from products where id=1
5 Update products set num=num-10 where  id=1
6 commit
7 Update products set num=num-5 where  id=1
8 commit
ctid xmin xmax cmin cmax oid id num
(0,1) 752 753 0 0 17569 1 10
(0,2) 753 0 0 0 17569 1 0

tx2=753

throw error

ctid xmin xmax cmin cmax oid id num
(0,1) 752 753 754 0 0 17569 1 10
x 0 0 17569 1 0

tx1=754

任何本質上是SX lock
但又自稱支援MVCC的RDBMS,
抱持欣賞的態度.
 請自求多福了......

我們一起去惹

分散式鎖

CAP定理

C-onsistency: (Strong Consistency): 強一致性(每次讀到都最新)
A-vailability: 系統不會返回錯誤(不保證都最新 e.g. 斷路器模式(Circuit Breaker))
P-artition Tolerance: 就算腦裂(Split-brain)可正常運作可復原 e.g. 斷線

note: A這裡的錯誤是系統錯誤(網路,溝通), 其他沒錯的情況下

CAP定理

"在分散式系統中,
不可能同時滿足上面三個性質,最多只能同時滿足兩個。"
C3取2=3 所以有三種選擇

CAP定理

C-onsistency: (Strong Consistency): 強一致性(每次讀到都最新)
A-vailability: 系統不會返回錯誤(不保證都最新 e.g. 斷路器模式(Circuit Breaker))
P-artition Tolerance: 就算腦裂(Split-brain)可正常運作可復原 e.g. 斷線

CA:
求: 強一致性且不返回錯誤=>只有單機可符合

CP:
求: 強一致性且腦裂可正常運作=>常常會傳掉線

AP:
求: 系統不會返回錯誤且腦裂可正常運作=>結果可能不是最新的
NoSQL Database,強調的就是Availablity,也就是系統保證運作,但是可能返回的結果不是最新的

BASE 介紹

B-asically A-vailable:

   服務基本上保持可用( CAP 的A-vailable一樣的概念)
S-oft State:
   Hard-State, 同等於Strong Consistency,反之Soft-State就是非大家都統一
E-ventual consistency:

   偏向CAP的Consistency, 最終一致性

BASE原則是根據CAP的AP原則演變而來,
NoSql通常遵守BASE, 而非ACID 強項不同

一開始覺得很奇怪,
後面覺得還不錯?
這個感覺就

BASE 介紹

ACID 介紹

1. Atomicity:

         * 一次全成功或全失敗

2. Consistency: 非CAP的C(Strong Consistency)

  • 每次commit後都符合db Constraint
  • E.g., posts.authorId must be a valid users.id

3. Isolation:

  • Concurrent txs = serial txs (in some order)

4.Durability:

  • commit後就不會在遺失(就算事後發生crashs)

2PC
(2 Phase Commitment)

分布式交易強一致性 CP
Strong Consistency
P-artition Tolerance

這是一個拜占庭將軍的故事

我們稱拜占庭將軍問題
(Byzantine Generals Problem)

大家開始進攻!

大家開始進攻!

我還沒準備好Q

rollback

大家開始進攻!

大家開始進攻!

大家開始進攻!

prepare

殺阿!commit

殺阿!commit

殺阿!commit

commit

請給我木頭

準備就緒

請給我石頭

end

準備就緒

準備就緒

準備就緒

begin

叛徒?

2PC介紹

原本的tx

2pc的tx

begin
...
commit

-- rollback
begin
...
 end
...
 prepare TRANSACTION 'T1'
...
commit prepared 'T1'

-- rollback prepared 'T1'

2PC介紹-範例

假設有兩個db,
一個國泰世華(cathaybk),
一個中國信託(ctbc)
兩個db都有一張cash_account的table,
今天想要從國泰世華轉帳1000元到中國信託
cathaybk.cash_account =>ctbc.cash_account

-- DB cathaybk
-- Table cash_account

+--------+---------+
| name   | balance |
+--------+---------+
| Mike   |    1000 |
| Error  | 9999999 |
+--------+---------+
-- DB ctbc
-- Table cash_account

+--------+---------+
| name   | balance |
+--------+---------+
| Mike   |       0 |
| Error  | 9999999 |
+--------+---------+

2PC介紹-範例

-- DB cathaybk
-- Table cash_account

+--------+---------+
| name   | balance |
+--------+---------+
| Mike   |    1000 |
| Error  | 9999999 |
+--------+---------+
-- DB ctbc
-- Table cash_account

+--------+---------+
| name   | balance |
+--------+---------+
| Mike   |       0 |
| Error  | 9999999 |
+--------+---------+
begin
update cash_account set balance=balance-1000 where name=Mike
end

prepare TRANSACTION 'T1'
begin
update cash_account set balance=balance+1000 where name=Mike
end

prepare TRANSACTION 'T1'
commit prepared 'T1'
commit prepared 'T1'

如果兩tx其中一個再進入prepare前發生錯誤, 就可以rollback

世上只有一種一致性算法Paxos
所有其他一致性算法都是Paxos算法的不完整版。

ACD of Sagas
下次講分散式系統時再補充XD

謝謝大家

Q&A+工商時間

DDIA放在後面呦~

歷史不是讓我們記住仇恨,而是學會各個環境下的人性
DB不單讓我們學會操控DB,而是學會設計App的精隨

DB Lock v2

By Shen Hoh

DB Lock v2

  • 103