先講下我當時遇到的一個應用場景: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;
但畢竟事務是種很影響性能的方法, 那有沒有能夠不用事務的方法呢?
依據的原理是,在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 | 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須要必定的映射關係。
表設置爲:
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^