1. 程式人生 > >Mysql 數據鎖與事務

Mysql 數據鎖與事務

數量 行數據 共享 engine ror 系統啟動 del 關心 dirty

一、鎖

常用命令

查看表的存儲引擎:mysql> show create table myLock;

修改當前表的存儲引擎:mysql> alter table myLock engine=myisam;

查看數據庫當前默認的存儲引擎:mysql> show variables like ‘%storage_engine%‘;

1、讀寫鎖(數據的操作類型)

讀鎖(共享鎖):對於同一條記錄,多個讀操作可以同時進行而不會互相影響,會阻塞寫操作。

寫鎖(排他鎖):當前寫操作沒有完成前,會阻礙其他寫鎖與讀鎖。

2、行鎖表鎖(鎖的力度)

                       表鎖

表鎖表級別的鎖定是MySQL各存儲引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特點是實現邏輯非常簡單,帶來的系統負面影響最小。所以獲取鎖和釋放鎖的速度很快。由於表級鎖一次會將整個表鎖定,所以可以很好的避免困擾我們的死鎖問題。當然,鎖定顆粒度大所帶來最大的負面影響就是出現鎖定資源爭用的概率也會最高,致使並大度大打折扣。(MyISAM存儲引擎支持表鎖)

創建數據

mysql> create table myLock(id int not null primary key auto_increment,name varchar(20))engine myisam;
Query OK, 0 rows affected (0.01
sec) mysql> insert into myLock (name) values("A"); Query OK, 1 row affected (0.00 sec) mysql> insert into myLock (name) values("B"); Query OK, 1 row affected (0.00 sec)

給表myLock加讀鎖:mysql> lock table myLock read;

在查看表的鎖狀態

mysql> show open tables from jalja_deal;
+------------+------------+--------+-------------+
| Database | Table | In_use | Name_locked | +------------+------------+--------+-------------+ | jalja_deal | jalja_user | 0 | 0 | | jalja_deal | myLock | 1 | 0 | | jalja_deal | deal_pay | 0 | 0 | | jalja_deal | deal_order | 0 | 0 | +------------+------------+--------+-------------+

解除表myLock的讀鎖:

mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)

mysql> show open tables from jalja_deal;
+------------+------------+--------+-------------+
| Database   | Table      | In_use | Name_locked |
+------------+------------+--------+-------------+
| jalja_deal | jalja_user |      0 |           0 |
| jalja_deal | myLock     |      0 |           0 |
| jalja_deal | deal_pay   |      0 |           0 |
| jalja_deal | deal_order |      0 |           0 |
+------------+------------+--------+-------------+
4 rows in set (0.00 sec)

模擬讀鎖:

第一個session:

mysql> lock table myLock read;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from myLock;
+----+------+
| id | name |
+----+------+
|  1 | A    |
|  2 | B    |
+----+------+
2 rows in set (0.00 sec)

第二個session:

mysql> select * from myLock;
+----+------+
| id | name |
+----+------+
|  1 | A    |
|  2 | B    |
+----+------+
2 rows in set (0.00 sec)

從這裏可以看出讀鎖之間是共享的。

我們的第一個session在不釋放鎖的情況下進行以下操作:

修改當前表的信息:

mysql> update myLock set name=C where id =1;
ERROR 1099 (HY000): Table myLock was locked with a READ lock and cant be updated

查詢其他表:

mysql> select * from jalja_user;
ERROR 1100 (HY000): Table jalja_user was not locked with LOCK TABLES

第二個session修改myLock表的數據:沒有錯誤也沒有執行結果,應為他被阻塞了正在排隊等待獲取myLock表的鎖

mysql> update myLock set name="N" where id =1;

模擬寫鎖:

#加鎖
mysql> lock table myLock write; Query OK, 0 rows affected (0.00 sec) //讀數據 mysql> select * from myLock; +----+------+ | id | name | +----+------+ | 1 | N | | 2 | B | +----+------+ 2 rows in set (0.00 sec) //寫數據 mysql> update myLock set name="N" where id=1; Query OK, 0 rows affected (0.03 sec) Rows matched: 1 Changed: 0 Warnings: 0 //操作其他表 mysql> select * from jalja_user; ERROR 1100 (HY000): Table jalja_user was not locked with LOCK TABLES

第二個session:

讀數據組阻塞

mysql> select * from myLock;

寫數據阻塞

mysql> update myLock set name="G" where id=1;

結論:

  1. MyISAM在執行查詢語句前,會自動給涉及到的表加讀鎖,在執行寫操作前,會自動給涉及到的表加寫鎖。
  2. 對MyISAM表的讀操作,不會阻塞其他進程對同一表的請求,但會阻塞對同一表的寫請求。
  3. 對MyISAM表的寫操作,會阻塞其他進程對同一表的讀和寫操作。
  4. MyISAM引擎的讀寫鎖調度是寫優先,這也是myISAM不適合做寫為主表的引擎。因為寫鎖時,其他線程不能做任何操作,大量的更新操作會使查詢很難得到鎖,從而造成永遠的阻塞

                    行鎖

行鎖:行級鎖定最大的特點就是鎖定對象的顆粒度很小,也是目前各大數據庫管理軟件所實現的鎖定顆粒度最小的。由於鎖定顆粒度很小,所以發生鎖定資源爭用的概率也最小,能夠給予應用程序盡可能大的並發處理能力而提高一些需要高並發應用系統的整體性能。雖然能夠在並發處理能力上面有較大的優勢,但是行級鎖定也因此帶來了不少弊端。由於鎖定資源的顆粒度很小,所以每次獲取鎖和釋放鎖需要做的事情也更多,帶來的消耗自然也就更大了。此外,行級鎖定也最容易發生死鎖。使用行級鎖定的主要是InnoDB存儲引擎。

二、鎖分析

A、查看數據的鎖狀態 (數據庫 jalja_deal)

mysql> show open tables from jalja_deal;
+------------+----------------+--------+-------------+
| Database   | Table          | In_use | Name_locked |
+------------+----------------+--------+-------------+
| jalja_deal | InnoDB_monitor |      0 |           0 |
| jalja_deal | jalja_user     |      0 |           0 |
| jalja_deal | myLock         |      0 |           0 |
| jalja_deal | deal_pay       |      0 |           0 |
| jalja_deal | deal_order     |      0 |           0 |
+------------+----------------+--------+-------------+

In_use =0 說明沒有表被鎖

B、分析表的鎖定情況

mysql> show status like table%;
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Table_locks_immediate      | 75    |
| Table_locks_waited         | 0     |
| Table_open_cache_hits      | 3     |
| Table_open_cache_misses    | 5     |
| Table_open_cache_overflows | 0     |
+----------------------------+-------+

Table_locks_immediate:產生表級鎖定的次數,表示可以立即獲取鎖的查詢次數,每立即獲取鎖時該值加1
Table_locks_waited:出現表級鎖爭用而發生等待的次數(不能立即獲取鎖的次數,每等待一次該值加1),該值高則說明存在較為嚴重的表級鎖爭用的情況。

InnoDB存儲引擎中對鎖的觀察:

mysql> show status like InnoDB_row_lock%;
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 0     |
| Innodb_row_lock_time_avg      | 0     |
| Innodb_row_lock_time_max      | 0     |
| Innodb_row_lock_waits         | 0     |
+-------------------------------+-------+

InnoDB 的行級鎖定狀態變量不僅記錄了鎖定等待次數,還記錄了鎖定總時長,每次平均時長,以及最大時長,此外還有一個非累積狀態量顯示了當前正在等待鎖定的等待數量。對各個狀態量的說明如下:
InnoDB_row_lock_current_waits:當前正在等待鎖定的數量;
InnoDB_row_lock_time:從系統啟動到現在鎖定總時間長度;
InnoDB_row_lock_time_avg:每次等待所花平均時間;
InnoDB_row_lock_time_max:從系統啟動到現在等待最常的一次所花的時間;
InnoDB_row_lock_waits:系統啟動後到現在總共等待的次數;
對於這5個狀態變量,比較重要的主要是InnoDB_row_lock_time_avg(等待平均時長),InnoDB_row_lock_waits(等待總次數)以及InnoDB_row_lock_time(等待總時長)這三項。尤其是當等待次數很高,而且每次等待時長也不小的時候,我們就需要分析系統中為什麽會有如此多的等待,然後根據分析結果著手指定優化計劃。

監控:

如果發現鎖爭用比較嚴重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比較高,還可以通過設置InnoDB Monitors來進一步觀察發生鎖沖突的表、數據行等,並分析鎖爭用的原因。鎖沖突的表、數據行等,並分析鎖爭用的原因。具體方法如下:

mysql> create table InnoDB_monitor(a INT) engine=InnoDB;
然後就可以用下面的語句來進行查看:
mysql> show engine InnoDB status;
監視器可以通過發出下列語句來停止查看:
mysql> drop table InnoDB_monitor;
設置監視器後,會有詳細的當前鎖等待的信息,包括表名、鎖類型、鎖定記錄的情況等,便於進行進一步的分析和問題的確定。可能會有讀者朋友問為什麽要先創建一個叫InnoDB_monitor的表呢?因為創建該表實際上就是告訴InnoDB我們開始要監控他的細節狀態了,然後InnoDB就會將比較詳細的事務以及鎖定信息記錄進入MySQL的errorlog中,以便我們後面做進一步分析使用。打開監視器以後,默認情況下每15秒會向日誌中記錄監控的內容,如果長時間打開會導致.err文件變得非常的巨大,所以用戶在確認問題原因之後,要記得刪除監控表以關閉監視器,或者通過使用“--console”選項來啟動服務器以關閉寫日誌文件。

InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,後者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味著:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!

三、事務

一個支持事務的數據庫必須必須具備ACID的屬性

原子性 (Atomicity):

  原子性是指事務包含的所有操作要麽全部成功,要麽全部失敗回滾,這和前面兩篇博客介紹事務的功能是一樣的概念,因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

一致性(Consistency):  

  一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。
例如:
假設用戶A和用戶B兩者的錢加起來一共是5000,那麽不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。

隔離性(Isolation):

  隔離性是當多個用戶並發訪問數據庫時,比如操作同一張表時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作所幹擾,多個並發事務之間要相互隔離。
  即要達到這麽一種效果:對於任意兩個並發的事務T1和T2,在事務T1看來,T2要麽在T1開始之前就已經結束,要麽在T1結束之後才開始,這樣每個事務都感覺不到有其他事務在並發地執行。
  關於事務的隔離性數據庫提供了多種隔離級別,稍後會介紹到。

持久性(durability):

  一旦事務提交,則其所做的修改就會永久保存到數據庫中。此時即使系統崩潰,修改的數據也不會丟失。(持久性的安全性與刷新日誌級別也存在一定關系,不同的級別對應不同的數據安全級別。)

二、高並發操作會引發的問題

1、丟失更新(Lost Update)

撤銷一個事務時,把其他事務已提交的更新數據覆蓋
例子:A和B事務並發執行,A事務執行更新後,提交;B事務在A事務更新後,B事務結束前也做了對該行數據的更新操作,然後回滾,則兩次更新操作都丟失了

2、臟讀(Dirty Reads)

一個事務讀到另一個事務未提交的更新數據

例子:A和B事務並發執行,B事務執行更新後,A事務查詢B事務沒有提交的數據,B事務回滾,則A事務得到的數據不是數據庫中的真實數據。也就是臟數據,即和數據庫中不一致的數據,不符合一致性要求。

3、不可重復讀(Non-Repeatable Reads)

一個事務讀到另一個事務已提交的更新數據

例子:A和B事務並發執行,A事務查詢數據,然後B事務更新該數據,A再次查詢該數據時,發現該數據變化了

4、覆蓋更新

這是不可重復讀中的特例,一個事務覆蓋另一個事務已提交的更新數據

例子:A事務更新數據,然後B事務更新該數據,A事務查詢發現自己更新的數據變了

5、虛讀(幻讀 Phantom Reads)

一個事務讀到另一個事務已提交的新插入的數據

例子:A和B事務並發執行,A事務查詢數據,B事務插入或者刪除數據,A事務再次查詢發現結果集中有以前沒有的數據或者以前有的數據消失了

四、隔離級別

隔離級別 讀數據一致性 臟讀 不可重復讀 幻讀
未提交讀(Read uncommitted) 最低級別隔離,只能保證不讀取物理上損壞的數據
已提交讀(Read committed) 語句級別
可重復讀(Repeatable read) 事務級別
可序列化(Serializable) 最高級別,事務級

事務的隔離級別越高,並發引發的副作用就越小,但付出的代價越大,因為事務隔離實質上就是使事務在一定程度上“串行化”進行,這顯然與“並發”是矛盾的。同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,比如許多應用對“不可重復讀” 和“幻讀”並不敏感,可能更關心數據庫並發訪問能力。

查看當前數據庫的隔離級別

mysql> show variables like tx_isolation;
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+

Mysql 數據鎖與事務