14 01 MySQL 幻讀如何解決?

2021-10-25 21:20:15 字數 2936 閱讀 9095

如何解決幻讀問題?

首先明確select … 和 select … for update 視為不相同的查詢,前者是「快照讀」,而後者是「當前讀」。

假設有如下結構的表和資料:

create tablet(

idint(11) not null,

cint(11) default null,

dint(11) default null,

primary key (id),

keyc(c)

) engine=innodb;

insert into t values(0,0,0),(5,5,5);

並且執行流程如下:

time session a

t0 mysql> begin;

query ok, 0 rows affected (0.00 sec)

t1 mysql> select * from t where d=5;

mysql> select * from t where d=5;

±—±-----±-----+

| id | c | d |

±—±-----±-----+

| 5 | 5 | 5 |

±—±-----±-----+

1 rows in set (0.00 sec)

time session b

t2 mysql> insert into t values(10, 10, 5);

query ok, 1 row affected (0.01 sec)

t3 mysql> update t set d=5 where id=0;

query ok, 1 row affected (0.00 sec)

rows matched: 1 changed: 1 warnings: 0

t4 mysql> select * from t;

mysql> select * from t;

±—±-----±-----+

| id | c | d |

±—±-----±-----+

| 0 | 0 | 5 |

| 5 | 5 | 5 |

| 10 | 10 | 5 |

±—±-----±-----+

3 rows in set (0.01 sec)

time session a

t5 mysql> select * from t where d=5;

mysql> select * from t where d=5;

±—±-----±-----+

| id | c | d |

±—±-----±-----+

| 5 | 5 | 5 |

±—±-----±-----+

1 rows in set (0.00 sec)

time session a

t6 mysql> update t set c=15 where d=5;

query ok, 3 rows affected (0.00 sec)

rows matched: 3 changed: 3 warnings: 0

t7 mysql> select * from t where d=5;

mysql> select * from t where d=5;

±—±-----±-----+

| id | c | d |

±—±-----±-----+

| 0 | 15 | 5 |

| 5 | 15 | 5 |

| 10 | 15 | 5 |

±—±-----±-----+

3 rows in set (0.01 sec)

可以看到 session b 在 t2 和 t3 時刻分別匯入和更新了一筆記錄,由於 mvcc 機制,結果對 session a 不可見(t5),但是當 session a 在 t6 時刻更新 d=5 的記錄時,卻同時更新了 3 筆記錄,這是因為所有的 update 語句都是「當前讀」,使得 session a 在更新時可以看到 session b 提交的記錄。

接下來奇怪的事情發生了,session a 再次執行相同的查詢時,卻返回了三條記錄。其實也不奇怪,還是由於 mvcc 機制,在事務中是能夠看到自己更新過的記錄的。

幻讀出現了!!!

這裡補充一點,一般都說幻讀都是由於新增記錄導致的,但是從上面的流程可以看到,session b 在 t3 時刻更新資料也同樣導致 session a 出現「幻覺」,看到了這條本不應該看到的記錄。

解決方式有兩種:

1.一種就是提高資料庫的隔離級別為「序列化」,那麼當 session a 開啟事務後,session b 執行的任何寫操作都會被阻塞,直到 session a 完成事務的提交,如此,session a 在執行期間只有自己會更新資料(當前讀),而且更新的資料又對自己可見(相當於當前讀),也就沒有了幻讀。

方法簡單,但是將所有的寫事務都序列化,對效能的影響是巨大的,不推薦。

2.另一種是使用 select … for update 代替 select …,首先前者是「當前讀」,會讀取到記錄的最新狀態,然後還會對記錄以及記錄間的間隙進行加鎖,也就是行鎖和間隙鎖,合起來又稱為 next-key 鎖,行鎖僅僅會鎖索引,next-key 鎖則還包括索引之前的間隙。

mysql 是一邊遍歷索引一邊新增鎖的,對於上面 t 表中的 d 字段,該欄位沒有建立索引,所以走的全表的查詢,也就是全表的所有記錄和其之前的間隙都被加鎖了,此時任何的其它事務都無法進行寫操作,也就不會出現上面幻讀的情況。

但是如果查詢的是擁有索引的 c 字段,則只會對 (0,5] 和 (5, +supremum] 區間進行加鎖,innodb 給每個索引加了乙個不存在的最大值 supremum,以滿足前開後閉區間。

Mysql如何解決幻讀

日常開發中接觸到最多的事務隔離級別分別是read committed和repeatable read也就是我們常說的提交讀和可重複讀。innodb的rr級別和rc級別最大的區別就是增加了gap鎖也就是間隙鎖,那麼間隙鎖是如何解決幻讀的呢?回憶一下幻讀和髒讀的概念,髒讀就是,乙個事物讀到了另乙個事務未...

20200429 mysql 如何解決幻讀

前言 事務的隔離級別有四種,讀未提交,讀已提交,可重複讀和序列化,下面結合具體的問題,在mysql中,innodb引擎是怎麼解決幻讀的?什麼是幻讀?一次事務中,多次查詢,結果集的個數不一樣,就叫幻讀 多出來的行就叫做幻行 為什麼要解決幻讀 高併發資料庫,要保持資料庫的一致性 mysql解決幻讀的方式...

MySQL 是如何解決幻讀的

在一次事務裡面,多次查詢之後,結果集的個數不一致的情況叫做幻讀。而多或者少的那一行被叫做幻行 在高併發資料庫系統中,需要保證事務與事務之間的隔離性,還有事務本身的一致性。如果你看到了這篇文章,那麼我會預設你了解了髒讀 不可重複讀與可重複讀。多數資料庫都實現了多版本併發控制,並且都是靠儲存資料快照來實...