MySQL 的 MVCC 是如何工作的?我們可以通過一個簡單的示例來了解它。
-- 創(chuàng)建一個測試表 CREATE TABLE test ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT NOT NULL ) ENGINE=InnoDB; -- 插入兩行數(shù)據(jù) INSERT INTO test (name, age) VALUES ('Alice', 20); INSERT INTO test (name, age) VALUES ('Bob', 25);
現(xiàn)在這個表有兩行數(shù)據(jù):
+----+-------+-----+ | id | name | age | +----+-------+-----+ | 1 | Alice | 20 | | 2 | Bob | 25 | +----+-------+-----+
現(xiàn)在我們開啟一個事務(wù),并讀取第一行數(shù)據(jù):
BEGIN; SELECT * FROM test WHERE id = 1;
現(xiàn)在我們在另一個會話中修改這一行數(shù)據(jù):
BEGIN; UPDATE test SET age = 22 WHERE id = 1; COMMIT;
現(xiàn)在我們回到第一個會話中,再次查詢這一行數(shù)據(jù):
SELECT * FROM test WHERE id = 1;
你可能期望看到的是 Alice 的年齡變成了 22,但是實際上,你仍然會得到 20:
+----+-------+-----+ | id | name | age | +----+-------+-----+ | 1 | Alice | 20 | +----+-------+-----+
這是為什么呢?因為 MySQL 的 MVCC (多版本并發(fā)控制)保證了每個事務(wù)看到的數(shù)據(jù)是一致的,而且不會受到其他事務(wù)的影響。
當我們開啟一個事務(wù)時,MySQL 會對這個事務(wù)創(chuàng)建一個 snapshot(快照),這個 snapshot 包含了數(shù)據(jù)庫在這個時刻的所有數(shù)據(jù)。因為我們在第一個會話中開啟了事務(wù),因此 MySQL 會創(chuàng)建一個 snapshot 用于這個事務(wù)。
當我們在第一個會話中查詢第一行數(shù)據(jù)時,MySQL 會去讀這個 snapshot,而不是直接讀取數(shù)據(jù)庫的最新數(shù)據(jù)。因為 snapshot 是在事務(wù)開啟時創(chuàng)建的,因此它只包含了事務(wù)開啟時已經(jīng)存在的數(shù)據(jù)。
當我們在另一個會話中修改第一行數(shù)據(jù)時,實際上是在數(shù)據(jù)庫中插入了一行新數(shù)據(jù),而不是更新原來的數(shù)據(jù)。這行新數(shù)據(jù)的版本號比原來的數(shù)據(jù)版本號高,因此它可以在 snapshot 之后被讀取。
因此,當我們在第一個會話中再次查詢第一行數(shù)據(jù)時,MySQL 會發(fā)現(xiàn)在 snapshot 之后有了新數(shù)據(jù)版本,因此會去讀取最新的數(shù)據(jù)版本。這樣,我們就能夠獲得正確的結(jié)果。
總結(jié)一下,MySQL 的 MVCC 保證了每個事務(wù)看到的數(shù)據(jù)是一致的,并且不會受到其他事務(wù)的影響。對于讀多寫少的場景,MVCC 可以大大提高數(shù)據(jù)庫的并發(fā)能力。