在爲PostgreSQL編寫一些SQL查詢時,我發現了一些不尋常的行爲,這讓我感到有點不安。具有顯式鎖定和併發事務的錯誤PostgreSQL查詢結果
假設我們有如下表 「測試」:
+----+-------+---------------------+
| id | value | created_at |
+----+-------+---------------------+
| 1 | A | 2014-01-01 00:00:00 |
| 2 | A | 2014-01-02 00:00:00 |
| 3 | B | 2014-01-03 00:00:00 |
| 4 | B | 2014-01-04 00:00:00 |
| 5 | A | 2014-01-05 00:00:00 |
| 6 | B | 2014-01-06 00:00:00 |
| 7 | A | 2014-01-07 00:00:00 |
| 8 | B | 2014-01-08 00:00:00 |
+----+-------+---------------------+
有兩筆交易,A和B,並行運行。
A: begin; /* Begin transaction A */
B: begin; /* Begin transaction B */
A: select * from test where id = 1 for update; /* Lock one row */
B: select * from test where value = 'B' order by created_at limit 3 for update; /* This query returns immediately since it does not need to return row with id=1 */
B: select * from test where value = 'A' order by created_at limit 3 for update; /* This query blocks because row id=1 is locked by transaction A */
A: update test set created_at = '2014-01-09 00:00:00' where id = 1; /* Modify the locked row */
A: commit;
一旦事務A提交併釋放與ID = 1,交易的阻塞查詢乙返回以下結果行:
+----+-------+---------------------+
| id | value | created_at |
+----+-------+---------------------+
| 1 | A | 2014-01-09 00:00:00 |
| 2 | A | 2014-01-02 00:00:00 |
| 5 | A | 2014-01-05 00:00:00 |
+----+-------+---------------------+
這些行肯定不是「created_at有序「並且id = 1的行甚至不應該在返回的行之間。事務A和B同時運行的事實導致了事務B中的錯誤結果,如果事務一個接一個地執行,則不會發生這種結果。這似乎違反了事務隔離。
這是一個錯誤?
如果這不是一個錯誤,並且這些結果是預期的,那麼這對數據庫返回的結果的可靠性意味着什麼?如果我有一個高度併發的環境,並且後面的代碼依賴於實際按日期排序的行,則會出現錯誤。
但是,如果我們運行相同的指令序列如上,但具有下列替換更新語句:
update test set value = 'B', created_at = '2014-01-09 00:00:00' where id = 1;
...然後阻塞查詢返回正確的結果:
+----+-------+---------------------+
| id | value | created_at |
+----+-------+---------------------+
| 2 | A | 2014-01-02 00:00:00 |
| 5 | A | 2014-01-05 00:00:00 |
| 7 | A | 2014-01-07 00:00:00 |
+----+-------+---------------------+
在這種情況下,阻止的查詢會執行兩次,因爲它的初始結果會失效,因此會執行兩次?
我對PostgreSQL最感興趣,但我也想知道是否支持行級鎖定的其他RDBMS,如Oracle,SQL Server和MySQL。
在MySQL中,會話b中的第一個選擇已被阻止,並等待會話A提交或回滾。在Oracle中這很難重現,因爲沒有'LIMIT'子句,通常使用具有'rownum'的派生表和order by將完全改變查詢的含義。 – 2014-10-06 18:18:36