MySQL事務與鎖實例教程詳解

MySQL事務和鎖

事務

說到關系型的數據庫的事務,相信大傢對四大特性都不陌生,分別是原子性、一致性、隔離性、持久性,簡稱為ACID特性。

MySQL中支持3種不同的存儲引擎:

MyISAM存儲引擎、Memory存儲引擎、和InnoDB存儲引擎

註:隻有InnoDB才支持事務。

事務的控制語句

控制語句 作用
begin或者start transaction 開啟一個事務
commit 或者 commit work 提交事務,進行持久性修改
rollback 或者 rollback work 回滾事務,撤銷已經進行修改但未提交的操作
savepoint [保存點] 在事務中創建一個保存點,一個事務可以有多個保存點
releasavepoint [保存點] 回滾到指定的保存點
set transaction 設置事務的隔離級別

事務隔離級別設置

先復習一個事務的四大隔離級別

  • 讀未提交(READ-UNCOMMITTED)
  • 讀已提交(READ-COMMITTED)
  • 可重復讀(RE-PEATABLE-READ)
  • 可序列化讀(SERIALIZABLE)

下面是操作過程。

首先,查看默認的事務隔離級別,可以看到是可重復讀(REPEATABLE-READ),

show variables like '%isolation%';

臟讀

我們來演示一下臟讀的場景,下面這張圖是展示瞭我原先已經創建好的兩個用戶的賬號都為100元。

分別打開兩個連接mysql的會話窗口,其中一個會話的隔離級別為READ-UNCOMMITED,然後再另一個窗口中開啟一個事務,例如,lisi給zhangsan轉賬100元,

set session transaction isolation level read uncommitted;
begin;
update user set money=money-100 where user=lisi;

我們在另外一個讀未提交的窗口中查看,zhangsan看到錢已經轉過來瞭,但是實際上lisi的事務還沒有提交,假如這個時候,lisi不想轉賬瞭,回滾事務,那zhangsan就讀到臟數據瞭。

不可重復讀

下面來展示一下不可重復讀的場景。

首先我們將zhangsan的窗口的事務隔離級別設置成READ-COMMITTED,並且在兩個窗口都開啟事務

set session transaction isolation level read committed;

假設zhangsan現在想統計全部人的錢有多少,很明顯200;

但是這個時候lisi往賬戶裡面存瞭100元,並提交事務,但是這個時候,zhangsan再次查詢總和,我們會查詢到總金額為300,但是這次查詢是處於同一個事務中,查詢到兩次不一樣的結果,屬於不可重復讀的情況。

update user set money=money+100 where user='lisi';
commit;

幻讀

為瞭解決不可重復讀的問題,我們將事務的隔離等級設置成RE-PEATABLE-READ,即MySQL默認的事務隔離等級,然後在兩邊都開啟一個事務。

set session transaction isolation level repeatable read;

 我們先在一個窗口插入一條數據並提交,然後在另外一個窗口查看,此時是查詢不到這個記錄的,但是假如這個時候我們新插入一條主鍵和剛插入的記錄一樣的話,我們就可以發現

insert into user values('zly1',100);
# 另外一個窗口
insert into user values('zly1',100);
ERROR 1062 (23000): Duplicate entry 'zly1' for key 'user.PRIMARY'

這樣也算是一種幻讀的現象,但是網上也有一種說法在可重復讀的等級下,幻讀是可避免的,這種說法不是非常準確的,如果在進行更新和插入時就可能會出現幻讀的情況,如果想要解決幻讀的情況,可以將事務隔離等級設置到SERIALIZABLE

鎖機制

InnoDB的行級鎖

InnoDB默認采用的行級鎖,分為以下這兩種,分別為共享鎖和排他鎖。

這兩個概念我在這篇文章中也有介紹。

共享鎖(S鎖):也叫讀鎖,如果在該數據對象上加瞭共享鎖,該事務可以讀取但是不能修改數據。其他事務也可以在該對象上加共享鎖,但是不能修改數據。

排他鎖(X鎖):也叫寫鎖,在一個數據對象隻有一把排他鎖,獲取到該鎖的事務可以讀取數據和修改數據。

(加鎖:一般的查詢語句不會加任何的鎖類型,當然也可以為數據加鎖,比如在select * from … for update,這樣可以為數據添加排他鎖,而使用select … lock in share mode 可以為數據添加共享鎖。

鎖實戰

首先關閉事務自動提交

set autocommit=0;

我們先在一個窗口輸入一條獲取到排他鎖,雖然操作的是一條數據,但是鎖的是整張表,因為我們沒有添加索引。

select * from user where user='zly1' for update;

這個時候我們對user這一列添加索引,就可以看到我們對其進行加鎖就不會出現阻塞的情況瞭。

alter table user add index(user);

註:在MySQL的行級鎖是針對索引加的鎖,而不是針對表中的行加級鎖,雖然訪問不同行的記錄,但是如果不存在對應的索引,或者使用相同的索引的話,就會造成鎖的沖突而鎖住整張表。

死鎖

死鎖是指兩個或者兩個以上的額事務在執行過程中,因為互相的等待或者因為爭搶相同的資源而造成的互相等待現象。

我們還是采用剛剛的例子,還是將事務的自動提交關閉掉,首先先在會話1中,更新id為1的記錄,然後在會話2中更新id為2的記錄,這個時候我們再回來在會話1更新id為2的記錄,在會話2中更新id為1的記錄,就會產生一個死鎖;

總結

本文主要介紹瞭事務的隔離等級和事務的鎖機制,主要更加偏向於實戰部分,我前面也有一些文章涉及到。

到此這篇關於MySQL事務與鎖實例教程詳解的文章就介紹到這瞭,更多相關MySQL事務與鎖內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: