前面的文章我們學到了:資料庫用「表格」來儲存資料,讓資料格式統一、位置固定、程式好處理。
但資料庫的好處不只這些。這篇文章要介紹資料庫另外兩個非常重要的功能——交易(Transaction) 和 鎖定(Lock)。
這兩個功能都是為了解決同一個問題:確保資料的正確性與一致性。
聽起來很專業?別擔心,讓我們用兩個 ATM 的故事來說明。
問題一:轉帳時資料不一致
情境:你要買東西,需要轉帳給賣家
假設你在網路上買了一個東西,要轉帳 2,000 元給賣家。
- 你的帳戶餘額:10,000 元
- 賣家的帳戶餘額:20,000 元
正常情況下,轉帳完成後應該是:
- 你的帳戶餘額:8,000 元(扣掉 2,000)
- 賣家的帳戶餘額:22,000 元(增加 2,000)
如果系統出了問題…
現在想像一個情況:轉帳過程中,系統出了一點問題。
賣家那邊的帳戶成功更新了,餘額變成 22,000 元。但不知道為什麼,你這邊的帳戶卻沒有被扣款,餘額還是 10,000 元。
結果變成:
- 你的帳戶餘額:10,000 元(沒扣到!)
- 賣家的帳戶餘額:22,000 元(已經增加)
這會造成什麼問題?
對你來說,這當然很爽——你平白多了 2,000 元。
但對銀行來說,這是一場災難。銀行憑空少了 2,000 元,而且這種錯誤如果一直發生,銀行很快就會倒閉。
反過來想,如果是你這邊被扣款了,但賣家那邊卻沒收到錢,那你就虧大了。
問題出在哪裡?
這個轉帳的動作,其實包含了兩個資料操作:
- 從你的帳戶「扣掉」2,000 元
- 在賣家的帳戶「增加」2,000 元
這兩個動作是綁在一起的,必須符合一個原則:
要嘛兩個都成功,要嘛兩個都不要發生。
不能只有一邊成功、另一邊失敗。
- ✅ 兩邊都成功 → 正常轉帳完成
- ✅ 兩邊都失敗 → 轉帳沒完成,但至少資料是對的
- ❌ 只有一邊成功 → 資料不一致,出大問題!
什麼是交易(Transaction)?
資料庫提供了一個功能,叫做交易(Transaction)。
交易的定義
交易就是把「一組資料操作」綁在一起,當成一個整體來執行。
回到剛才的轉帳例子,這個動作其實包含兩個操作:
- 從你的帳戶扣掉 2,000 元
- 在賣家的帳戶增加 2,000 元
這兩個操作看起來是分開的,但它們在邏輯上是「一件事」——就是轉帳。如果只做了第一個、沒做第二個,或是只做了第二個、沒做第一個,都不算「轉帳完成」。
所以我們需要把這兩個操作「綁」在一起,告訴資料庫:「這是一筆交易,請一起處理。」
交易的規則
當你把多個操作包成一筆交易,資料庫會遵守一個規則:
全部成功,才算成功;只要有一個失敗,就全部取消。
什麼意思呢?
- 如果所有操作都順利完成 → 資料庫才會把這些變更「正式寫入」
- 如果其中任何一個操作失敗 → 資料庫會把已經做的操作「全部復原」,回到交易開始前的狀態
這個「復原」的動作,專業術語叫做回溯(Rollback)。
為什麼要這樣設計?
因為這樣可以確保資料永遠是「一致」的狀態。
以轉帳來說:
- ✅ 你扣了錢,賣家也收到錢 → 資料一致,正確
- ✅ 你沒扣錢,賣家也沒收到錢 → 資料一致,正確(只是轉帳沒成功)
- ❌ 你扣了錢,賣家沒收到錢 → 資料不一致,錯誤!
- ❌ 你沒扣錢,賣家卻收到錢 → 資料不一致,錯誤!
交易機制保證了:不會出現後面兩種「不一致」的情況。
用轉帳的例子來說明
當你發起一筆轉帳,資料庫會這樣處理:
開始交易
1. 從你的帳戶扣掉 2,000 元
2. 在賣家的帳戶增加 2,000 元
結束交易- 如果第 1 步和第 2 步都成功 → 交易完成,資料正式寫入
- 如果第 1 步成功,但第 2 步失敗 → 整筆交易取消,第 1 步也會被回溯(rollback),你的帳戶餘額會恢復原狀
- 如果第 1 步就失敗 → 交易直接取消,什麼都不會改變
這樣就能確保資料永遠是一致的,不會出現「一邊扣了、一邊沒加」的情況。
問題二:多人同時操作資料
情境:你和朋友共用一個帳戶
假設你和你的朋友共用同一個帳戶(現實中不太會發生,但我們用這個例子來說明)。
你們的帳戶餘額是 10,000 元。
有一天,你們兩個剛好同時在不同的 ATM 提款:
- 你要提 6,000 元
- 你的朋友要提 3,000 元
正常情況下,結果應該是:10,000 – 6,000 – 3,000 = 1,000 元
如果系統沒處理好…
假設這台 ATM 的程式寫得有點蠢,它的運作方式是:
- 先讀取目前餘額
- 計算提款後的新餘額
- 把新餘額寫回去
當你們同時操作時,會發生什麼事?
- 你登入 ATM,系統讀取餘額:10,000 元
- 同一時間,你朋友也登入 ATM,系統也讀取餘額:10,000 元
- 你提了 6,000 元,ATM 計算新餘額:10,000 – 6,000 = 4,000 元,寫回去
- 你朋友提了 3,000 元,ATM 計算新餘額:10,000 – 3,000 = 7,000 元,寫回去
最後餘額變成:7,000 元
資料怎麼出錯了?
你們兩個總共提了 9,000 元,但帳戶只少了 3,000 元!
問題出在哪裡?因為你朋友讀取餘額的時候,你的提款還沒完成,所以他看到的還是 10,000 元。等他寫回去的時候,就把你的操作結果覆蓋掉了。
對你們來說很爽,但對銀行來說又是一場災難。
什麼是鎖定(Lock)?
資料庫提供了另一個功能,叫做鎖定(Lock)。
鎖定的定義
鎖定就是:當某筆資料正在被操作時,禁止其他人同時操作這筆資料。
回到剛才的提款例子,問題出在哪裡?
你和朋友「同時」讀取了餘額,都看到 10,000 元。接著你們各自計算、各自寫回去,結果後寫的人把先寫的結果覆蓋掉了。
如果有鎖定機制,流程會變成:
- 你開始操作 → 資料庫把這筆資料「鎖住」
- 你朋友也想操作 → 資料庫說:「這筆資料被鎖住了,請等一下」
- 你完成操作 → 資料庫「解鎖」
- 你朋友現在可以操作了
就像上廁所要鎖門一樣——裡面有人的時候,外面的人就要排隊等。
鎖定的規則
鎖定的核心規則很簡單:
一次只能有一個人操作同一筆資料,其他人必須排隊等待。
這樣就能避免「同時操作、互相覆蓋」的問題。
鎖定的層級
鎖定可以有不同的範圍,根據你的需求來選擇:
- 鎖定單筆資料(Row Lock):只鎖定正在操作的那一筆資料,其他筆資料不受影響
- 鎖定整張表格(Table Lock):整張表的資料都不能被同時操作
通常會選擇「鎖定單筆資料」,因為這樣影響範圍最小。只有在特殊情況下,才會鎖定整張表格。
為什麼要這樣設計?
因為這樣可以確保資料不會被「同時修改」而出錯。
以提款來說:
- ✅ 你先提款,朋友等你完成後再提款 → 餘額正確
- ❌ 你和朋友同時提款,互相覆蓋 → 餘額錯誤
鎖定機制保證了:同一筆資料在同一時間只會被一個人修改,不會發生覆蓋的問題。
用提款的例子來說明
如果 ATM 有做鎖定,流程會變成:
- 你登入 ATM,系統讀取餘額:10,000 元,同時把這筆資料鎖住
- 你朋友也想登入,但系統發現這筆資料被鎖住了,告訴他:「有人正在操作,請稍後再試」
- 你提了 6,000 元,餘額更新為 4,000 元,解除鎖定
- 現在你朋友可以操作了,系統讀取餘額:4,000 元
- 你朋友提了 3,000 元,餘額更新為 1,000 元
最後餘額:1,000 元 ✅
這才是正確的結果!
鎖定的層級
鎖定可以有不同的範圍:
- 鎖定單筆資料:只有這一筆資料不能被同時操作
- 鎖定整張表格:整張表的資料都不能被同時操作
根據不同的需求,可以選擇適合的鎖定層級。
交易與鎖定的差異
這兩個功能都是為了確保資料正確,但解決的問題不太一樣:
| 交易(Transaction) | 鎖定(Lock) | |
|---|---|---|
| 解決的問題 | 多個操作必須「一起成功或一起失敗」 | 多個人不能「同時操作同一筆資料」 |
| 情境 | 轉帳時,扣款和入帳必須同時完成 | 提款時,不能讓兩個人同時改餘額 |
| 關鍵字 | 全部成功,或全部回溯 | 排隊,一個一個來 |
實際上,這兩個功能常常會一起使用,來確保資料的完整性和一致性。
什麼時候需要交易和鎖定?
你可能會想:「這些功能聽起來很棒,那是不是所有系統都應該用?」
其實不一定。
交易和鎖定的代價
這兩個功能雖然能確保資料正確,但會影響效能:
- 交易:系統要做很多檢查,確認所有操作都成功,失敗時還要回溯
- 鎖定:其他人要排隊等待,不能同時操作
如果你的系統有很多人同時使用,這些機制可能會讓速度變慢。
什麼時候需要?什麼時候不需要?
需要交易和鎖定的情境:
- 銀行轉帳、付款系統
- 電商的庫存管理
- 機票、飯店訂位系統
- 任何「資料錯誤會造成嚴重損失」的情境
不一定需要的情境:
- 社群網站按讚數(少算一個讚,其實沒什麼大不了)
- 網站瀏覽次數統計
- 任何「資料稍微不準確也可以接受」的情境
重點是:根據你的需求來決定要不要使用這些功能。
小結
資料庫提供了兩個重要功能來確保資料正確:
交易(Transaction)
- 把多個操作綁在一起
- 全部成功才算成功,有一個失敗就全部回溯
- 解決「操作做到一半出錯」的問題
鎖定(Lock)
- 當資料正在被操作時,禁止其他人同時操作
- 一個一個排隊來
- 解決「多人同時操作造成資料錯亂」的問題
這就是為什麼我們需要用資料庫——因為資料庫已經幫你把這些功能做好了,你不需要自己從頭寫。當你需要確保資料一致性的時候,直接用就可以了。