Mysql 數據鎖與事務

1、鎖

經常使用命令

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

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

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

一、讀寫鎖(數據的操做類型)

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

寫鎖(排他鎖):當前寫操做沒有完成前,會阻礙其餘寫鎖與讀鎖。服務器

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

                       表鎖

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

建立數據併發

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 can't 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存儲引擎。

 

 

2、鎖分析

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將使用表鎖!

3、事務

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

原子性 (Atomicity):

  原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾,這和前面兩篇博客介紹事務的功能是同樣的概念,所以事務的操做若是成功就必需要徹底應用到數據庫,若是操做失敗則不能對數據庫有任何影響。

一致性(Consistency):  

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

隔離性(Isolation):

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

持久性(durability):

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

2、高併發操做會引起的問題

一、丟失更新(Lost Update)

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

二、髒讀(Dirty Reads)

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

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

三、不可重複讀(Non-Repeatable Reads)

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

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

四、覆蓋更新

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

例子:A事務更新數據,而後B事務更新該數據,A事務查詢發現本身更新的數據變了

五、虛讀(幻讀 Phantom Reads)

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

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

4、隔離級別

 

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

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

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

mysql> show variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
相關文章
相關標籤/搜索