如何保證數據庫讀寫事務

場景

先講下我當時遇到的一個應用場景:mysql

一份含有5萬條數據的表,但願每次都能從中獲取到一條未被使用過的數據,而後再標記該條數據已被使用。sql

數據庫時mysql,爲方便起見,轉化成sql的語義就是:數據庫

一張表:併發

value status
v1 1
v2 1
... 1

 

 

 

 

咱們須要一次讀取一行數據,再把該行的status字段改爲0。它必然涉及到一次select,一次update,那如何保證的事務呢?性能

特別說明:表結構並非必須這樣,你能夠本身設計。spa

 

解決思路

若是你以爲很簡單:直接select這條數據,而後update它便可。那就真的悲劇了。設計

好比說這樣的讀寫SQLcode

SELECT value FROM table WHERE status = 1 LIMIT 1
UPDATE table SET status = 0 WHERE value = 'v1'

若是每次請求都以下圖同樣,是依次順序執行,的確是沒有任何問題blog

可是若是應用的併發量很高時,這樣就會出現問題,好比下圖中,用戶2在用戶1執行完畢select但沒執行完畢update時,執行了select。那麼用戶1和用戶2將得到同一條數據,這明顯不是咱們想要的結果。事務

那又什麼好的解決方案呢?相信不少人立馬想到的方案確定是事務。不錯,用事務的確是種不錯的解決方案

解決方案一:事務 

START TRANSACTION;
SELECT value FROM table WHERE status = 1 LIMIT 1
UPDATE table SET status = 0 WHERE value = 'v1'
COMMIT;或rollback;

 但畢竟事務是種很影響性能的方法, 那有沒有能夠不用事務的方法呢?

解決方案二:根據UPDATE結果斷定是否有效

依據的原理是,在Mysql中執行UPDATE語句時,它會返回執行結果,具體以下:

mysql> UPDATE table SET status = 0 WHERE value = 'v1' AND status = 1;
Query OK, 0 rows affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

這裏 Rows matched 表示匹配的數量,Changed表示改動的行數

回到剛到的問題,咱們能夠用這些信息來判斷是否成功

SELECT value FROM table WHERE status = 1 LIMIT 1
UPDATE table SET status = 0 WHERE value = 'v1' AND status = 1

當返回結果是:

Rows matched: 1  Changed: 1  Warnings: 0

表示此次數據更新是有效的。

當返回結果是:

Rows matched: 0  Changed: 0  Warnings: 0

表示此次數據更新失敗,由於該行數據已經別人佔用,須要重試

但這種方法的問題,就是併發量特別高時,不少請求會出現衝突,須要重試。

解決方案三:利用表的自增id屬性

id value status
1 v1 1
2 v2 1

 

 

 

id 是該表的自增主鍵(auto_increment)

須要另一張表table2

id ...
1 ...
2 ...
... ...

 

 

 

 

id 也是該表的自增主鍵(auto_increment)

 

INSERT INTO table2 ( ....) VALUES( .... );
獲取到table2 中最近的id
UPDATE table SET status = 0 WHERE id = id
SELECT value FROM table WHERE id = id

 因爲自增id是惟一性的,因此能夠保證最終獲得的數據也是惟一性的,但缺點也很是明顯:多須要一張表,並且兩張表的id須要必定的映射關係。

解決方案四:引入requestID字段

表設置爲:

value status request_id
v1 1  
v2 1  

 

 

 

使用SQL:

UPDATE table SET status = 0, request_id = 'xxxxx'  WHERE status = 1 LIMIT 1
SELECT value FROM table WHERE request_id = 'xxxxx'

只要保證request_id是惟一性,咱們獲得的結構也確定是有效的

 

但願對你們有所幫助。^v^

相關文章
相關標籤/搜索