mysql的儲存引擎與鎖

來源:互聯網
上載者:User

標籤:

一、背景知識

1、鎖是電腦協調多個進程或線程並發訪問某一資源的機制。

A、鎖分類。

| 共用鎖定(讀鎖):在鎖定期間,多個使用者可以讀取同一個資源,讀取過程中資料不會發生變化。

| 獨佔鎖定(寫鎖):在鎖定期間,只允許一個使用者寫入資料,其它使用者的讀取,寫入等操作都會被拒絕。

B、鎖顆粒

| 表鎖:開銷小,加鎖快;不會出現死結;鎖定粒度大,發生鎖衝突的機率最高,並發度最低。

| 行鎖:開銷大,加鎖慢;會出現死結;鎖定粒度最小,發生鎖衝突的機率最低,並發度也最高。

| 頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死結;鎖定粒度界於表鎖和行鎖之間,並發度一般。

2、事務是由一組SQL語句組成的邏輯處理單元。

A、事務(Transaction)及其ACID屬性。

| 原子性(Atomicity):事務是一個原子操作單元,其對資料的修改,要麼全都執行,要麼全都不執行。

|   一致性(Consistent):在事務開始和完成時,資料都必須保持一致狀態。這意味著所有相關的資料規則都必須應用於事務的修改,以保持資料的完整性;事務結束時,所有的內部資料結構(如B樹索引或雙向鏈表)也都必須是正確的。

| 隔離性(Isolation):資料庫系統提供一定的隔離機制,保證事務在不受外部並行作業影響的“獨立”環境執行。這意味著交易處理過程中的中間狀態對外部是不可見的,反之亦然。

| 持久性(Durable):事務完成之後,它對於資料的修改是永久性的,即使出現系統故障也能夠保持。

銀行轉帳就是事務的一個典型例子。

B、事務並發問題

| 更新丟失(Lost Update):兩個或多個事務同時更新一個資源,前面的事務的操作結果會被最後面的事務的操作結果覆蓋。

| 髒讀(Dirty Reads):事務A正在更新記錄X,事務B讀取了記錄X,並藉此做進一步處理,事務A更新完畢記錄X,此時資料庫中的記錄X與事務B讀到的記錄X並不是一致的。那麼事務B讀取到的就是“髒資料”,此類現象稱之為“髒讀”。

| 不可重複讀取(Non-Repeatable Reads):事務A讀取了記錄X,然後事務B更新了記錄X,然後事務A再次讀取記錄X。此類情況下,事務A讀取的記錄X並不一定是相同的。

| 幻讀(Phantom Reads):事務A按相同的查詢條件讀取已經讀取過的記錄X時,發現,事務B更新的記錄Y也符合事務A的查詢條件。那麼,事務A將會讀取到記錄Y,而不是記錄X。此類現象稱之為“幻讀”。

C、事務隔離解決事務並發問題

隔離等級 說明 髒讀 不可重複讀取 幻讀
Read Uncommitted(讀取未提交內容) 所有事務都可以看到其他未提交事務的執行結果。
Read Committed(讀取提交內容) 一個事務只能看見已經提交事務所做的改變。
Repeatable Read(可重讀) 這是MySQL的預設交易隔離等級,它確保同一事務的多個執行個體在並發讀取資料時,會看到同樣的資料行。
Serializable(可序列化) 它是在每個讀的資料行上加上共用鎖定。在這個層級,可能導致大量的逾時現象和鎖競爭。

二、各個儲存引擎的特點

特點 MyISAM InnoDB Memory Archive
儲存限制 256TB 64TB
事務安全 - 支援 - -
支援索引 支援 支援 支援  
鎖顆粒 表鎖 行鎖 表鎖 行鎖
資料壓縮 支援 - - 支援
支援外鍵 - 支援 - -

三、MyISAM的鎖詳解

在我的test資料庫中,有兩張MyISAM儲存引擎的表分別是User與Log。

下面將示範兩個線程(A、B)同時操作一張表的情況,開兩個cmd視窗一個代表A一個代表B按順序執行下面的代碼。

操作 說明 線程
mysql> show tables;
+----------------+
| Tables_in_test   |
+----------------+
| log                   |
| user                 |
+----------------+
2 rows in set
顯示所有的表 A
mysql> show status like ‘table%‘;
+-----------------------+-------+
| Variable_name            | Value   |
+-----------------------+-------+
| Table_locks_immediate | 50      |
| Table_locks_waited      | 0        |
+-----------------------+-------+
2 rows in set (0.00 sec)
顯示表級鎖爭用情況 A
mysql> lock table log write local;
Query OK, 0 rows affected
給表log顯式加上寫鎖,“local”選項的作用是在滿足MyISAM表並發插入條件的情況下,允許其它線程在表尾並發插入記錄。 A
mysql> select * from log;

線程B讀取已經被線程A上了讀鎖的表log,可以看到線程B正在等待。
mysql> select * from user;
1100 - Table ‘user‘ was not locked with LOCK TABLES
線程A讀取了沒有被加鎖的表User,可以看到,mysql不讓讀取。
mysql> unlock tables;
Query OK, 0 rows affected

線程A釋放了對log表的鎖。 A
mysql> show status like ‘table%‘;
+-----------------------+-------+
| Variable_name            | Value   |
+-----------------------+-------+
| Table_locks_immediate | 51      |
| Table_locks_waited      | 0        |
+-----------------------+-------+
2 rows in set (0.00 sec)
顯示表級鎖爭用情況,可以看到Table_locks_immediate的值加1了。 A
+--------+-----+---------+-------------+
| log_id | uid | content | create_time |
+--------+-----+---------+-------------+
|      1 |   2       | 呵呵       |          10      |
|      2 |   2       | 哈哈       |          20      |
+--------+-----+---------+-------------+
2 rows in set (5 min 47.34 sec)
線程B讀取到了log的資料。 B

1、總結

| MyISAM,在預設情況下會自動加鎖,並不需要顯式加鎖。
| MyISAM總是一次獲得SQL語句所需要的全部鎖。這也正是MyISAM表不會出現死結(Deadlock Free)的原因。其它未加鎖的表,並不允許操作。
| MyISAM加的是表級鎖。
| 當線程A與線程B同時要給某個表上讀鎖與寫鎖的時候,MyISAM預設讓線程B先上寫鎖。大量的讀與寫操作並存的時候,寫操作可能會一直得到寫鎖,導致讀操作處於阻塞狀態。

2、並發插入調度

MyISAM儲存引擎有一個系統變數concurrent_insert,專門用以控制其並發插入的行為,其值分別可以為0、1或2。
| 當concurrent_insert設定為0時,不允許並發插入。
| 當concurrent_insert設定為1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的預設設定。
| 當concurrent_insert設定為2時,無論MyISAM表中有沒有空洞,都允許在表尾並發插入記錄。

3、MyISAM的鎖調度

| 通過指定啟動參數 low-priority-updates,使MyISAM引擎預設給予讀請求以優先的權利。
| 通過執行命令SET LOW_PRIORITY_UPDATES=1,使該串連發出的更新要求優先順序降低。
| 通過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,降低該語句的優先順序。
| 給系統變數max_write_lock_count設定一個合適的值,當一個表的讀鎖達到這個值後,MySQL就暫時將寫請求的優先順序降低,給讀操作一定獲得鎖的機會。

 四、InnoDB的鎖詳解

在我的test資料庫中,有兩張InnoDB儲存引擎的表分別是User與Log。

下面將示範兩個線程(A、B)同時操作一張表的情況,開兩個cmd視窗一個代表A一個代表B按順序執行下面的代碼。

第一個表格是,獨佔鎖定的執行個體。

操作 說明 線程
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| log                 |
| user               |
+----------------+
2 rows in set
顯示所有的表 A
mysql> select * from log;
+--------+-----+---------+-------------+
| log_id | uid | content | create_time |
+--------+-----+---------+-------------+
|      1 |   1 | 呵呵         |           0     |
|      2 |   2 | 哈哈         |           0     |
+--------+-----+---------+-------------+
2 rows in set
顯示log表的資訊 A
mysql> show status like ‘innodb_row_lock%‘;
+-------------------------------+----------+
| Variable_name                       | Value       |
+-------------------------------+----------+
| Innodb_row_lock_current_waits | 0          |
| Innodb_row_lock_time              | 185770 |
| Innodb_row_lock_time_avg       | 26538   |
| Innodb_row_lock_time_max      | 51620   |
| Innodb_row_lock_waits             | 7          |
+-------------------------------+----------+
5 rows in set (0.00 sec)
查看InnoDB的鎖爭用情況  B
mysql> start transaction;
Query OK, 0 rows affected
開啟事務 A
mysql> update log set create_time=1 where uid=1;
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0

更新uid=1的資料

Select…For Update 語句可以在讀取資料的時候就鎖定它。

A
mysql> update log set create_time=2 where uid=2;

更新uid=2的資料,可以看線程B在等待,因為此時InnoDB加的是表級獨佔鎖定。

B
mysql> commit;
Query OK, 0 rows affected
提交事務 A
mysql> update log set create_time=2 where uid=2;
Query OK, 1 row affected (9.69 sec)
Rows matched: 1  Changed: 1  Warnings: 0
線程B執行update成功 B
mysql> show status like ‘innodb_row_lock%‘;
+--------------------------------+---------+
| Variable_name                         | Value     |
+--------------------------------+---------+
| Innodb_row_lock_current_waits | 0          |
| Innodb_row_lock_time              | 196980 |
| Innodb_row_lock_time_avg       | 24622   |
| Innodb_row_lock_time_max      | 51620   |
| Innodb_row_lock_waits             | 8          |
+--------------------------------+---------+
5 rows in set (0.00 sec)

再次查看InnoDB的鎖爭用情況 。

如 InnoDB_row_lock_waits 和InnoDB_row_lock_time_avg 的值比較高,則鎖的爭用情況嚴重。

B
mysql> select * from log;
+--------+-----+---------+-------------+
| log_id | uid | content | create_time |
+--------+-----+---------+-------------+
|      1 |   1 | 呵呵          |           1    |
|      2 |   2 | 哈哈          |           2    |
+--------+-----+---------+-------------+
2 rows in set (0.00 sec)
顯示log表的資訊 B

 第二個表格是,共用鎖定的執行個體。

操作 說明 線程
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
開啟事務 A
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
開啟事務 B
mysql> select * from log where uid=1 lock in share mode;
+--------+-----+---------+-------------+
| log_id | uid | content | create_time |
+--------+-----+---------+-------------+
|      1 |   1 | 呵呵    |           1 |
+--------+-----+---------+-------------+
1 row in set (0.00 sec)
線程A取得共用鎖定
mysql> select * from log where uid=1 lock in share mode;
+--------+-----+---------+-------------+
| log_id | uid | content | create_time |
+--------+-----+---------+-------------+
|      1 |   1 | 呵呵    |           1 |
+--------+-----+---------+-------------+
1 row in set (0.00 sec)
線程B取得共用鎖定
mysql> update log set create_time=1 where uid=1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

線程A更新一條記錄,暫時不要去操作下面的步驟,直到出現報錯。

由於線程B的共用鎖定,導致了線程A無法更新資料。

A
mysql> update log set create_time=1 where uid=1;
Query OK, 0 rows affected (7.82 sec)
Rows matched: 1  Changed: 0  Warnings: 0
線程A再次更新記錄,同時線上程A還在等待的時候,線程B也執行了更新記錄。 A
mysql> update log set create_time=1 where uid=1;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
線程B執行更新記錄。注意,當線程B執行更新語句的時候,線程B失去了共用鎖定,線程A獲得了獨佔鎖定,導致線程B的語句立刻出現了報錯。 B
mysql> select * from log where uid=1 lock in share mode;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

線程B再次執行擷取共用鎖定的語句,可以看到,由於線程A已經取得了獨佔鎖定,導致線程B爭鎖失敗。

如果線程A執行commit語句釋放獨佔鎖定後,線程B則可以立馬獲得共用鎖定。

B

總結:

InnoDB行鎖是通過給索引上的索引項目加鎖來實現的,無索引的情況下,InnoDB將使用表鎖!實際應用中,要特別注意此特性。避免大量鎖衝突影響並發效能。

mysql的儲存引擎與鎖

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.