1. 程式人生 > 實用技巧 >[msyql]-鎖機制

[msyql]-鎖機制

概述

定義

  鎖是計算機協調多個程序或執行緒併發訪問某一資源的機制。

在資料庫中,傳統的計算機資源(CPU、IO、RAM)的爭用外,資料也是一種供許多使用者共享的資源。如何保證資料併發的訪問的一致性、有效性是所有的資料庫必須解決的一個問題,鎖衝突也是資料庫併發訪問的效能的一個重要因素。從這個角度來說,鎖是對資料庫而言顯的尤為重要,也更加複雜。

鎖的分類

從對資料操作的型別(讀/寫)分

  • 讀鎖(共享鎖):針對同一份資料,多個讀操作可以統一同時進行而不會互相影響。
  • 寫鎖(排他鎖):當親寫操作沒有完成前,它會阻斷其他寫鎖客讀鎖。

從資料操作的粒度分

  • 表鎖
  • 行鎖

表鎖(便讀)

特點

偏向MyISAM儲存引擎,開銷小,加鎖快;無死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。

案例分析

建表SQL

create table mylock (
id int not null primary key auto_increment,
name varchar(20) default ''
) engine myisam;

insert into mylock(name) values('a');
insert into mylock(name) values('b');
insert into mylock(name) values('c');
insert into mylock(name) values('d');
insert into mylock(name) values('e');

select * from mylock;

表鎖命令

#查看錶上加過的鎖
show open tables;

#給mylock上讀鎖,給book上寫鎖
lock table my lock read, book write;

#釋放表鎖
unlock tables;

讀鎖案例

  • 加讀鎖,為mylock表加read鎖
#開啟兩個mysql會話

#會話1
#為mylock加鎖(讀)
lock table  mylock read;

#會話1(可以讀不可寫)
update mylock set name='a2' where id = 1;
ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated

#會話1,中mylock上 read 期間,其他表不可以讀
mysql> select * from book;
ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES
#會話2
#在會話1對mylock上 read 期間可以讀mylock與其他表

#會話2
#在會話1對mylock上 read 期間可以需要 會話1 解鎖 才能對mylock進行修改

總結(讀鎖)

寫鎖案例

  • 在會話1中新增寫鎖
mysql> LOCK TABLE `mylock` WRITE;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
|  1 | a3   |
|  2 | b    |
|  3 | c    |
|  4 | d    |
|  5 | e    |
+----+------+
5 rows in set (0.00 sec)

mysql> update mylock set name='a4' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#不能讀其他表
mysql> select * from book;
ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES
  • 會話2
#阻塞了,需要會話一釋放鎖
mysql> select * from mylock;


總結(寫鎖)

案例總結

MyISAM引擎在執行查詢語句SELECT之前,會自動給涉及到的所有表加讀鎖,在執行增刪改之前,會自動給涉及的表加寫鎖。

MySQL的表級鎖有兩種模式:

  • 表共享讀鎖(Table Read Lock)。

  • 表獨佔寫鎖(Table Write Lock)。

MyISAM表進行操作,會有以下情況:

  • MyISAM表的讀操作(加讀鎖),不會阻塞其他執行緒対同一表的讀操作,但是會阻塞其他執行緒対同一表的寫操作。只有當讀鎖釋放之後,才會執行其他執行緒的寫操作。
  • MyISAM表的寫操作(加寫鎖),會阻塞其他執行緒対同一表的讀和寫操作,只有當寫鎖釋放之後,才會執行其他執行緒的讀寫操作。

簡而言之,就是讀鎖會阻塞寫,但是不會阻塞讀。而寫鎖會把讀和寫都阻塞。

分析

  • 看看哪些表被鎖了
    show open tables;

  • 如何分析表鎖定
    SHOW STATUS LIKE 'table%';
    可以通過Table_locks_immediateTable_locks_waited狀態變數來分析系統上的表鎖定。

Table_locks_immediate:產生表級鎖定的次數,表示可以立即獲取鎖的查詢次數,每立即獲取鎖值加1。

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

此外,MyISAM的讀寫鎖排程是寫優先,這也是MyISAM不適合作為主表的引擎。因為寫鎖後,其他執行緒不能進行任何操作,大量的寫操作會使查詢很難得到鎖,從而造成永遠阻塞。

行鎖(偏寫)

特點

  • 偏向InnoDB儲存引擎,開銷大,加鎖慢:會出現死鎖;鎖粒度最小,發生鎖衝突的概率最低,併發度也最高。
  • InnoDB與MyISAM的最大不同有兩點:一是支援事務(TRANSACTION);二是採用了行級鎖

併發事務帶來的問題

  • 更新丟失
當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,就會發生丟失更新題--最後的更新覆蓋了由其他事務所做的更新。
例如,兩個程式設計師修改同一java檔案。每程式設計師獨立地更改其副本,然後儲存更改後的副本,這樣就覆蓋了原始文件。最後儲存其更改副本的編輯人員覆蓋前一個程式設計師所做的更改。
如果在一個程式設計師完成並提交事務之前,另一個程式設計師不能訪問同一檔案,則可避免此問題。
  • 髒讀
  • 不可重複復
  • 幻讀

事務隔離級別

行鎖案例

建表sql

CREATE TABLE test_innodb_lock (a INT(11),b VARCHAR(16))ENGINE=INNODB;
INSERT INTO test_innodb_lock VALUES(1,'b2');
INSERT INTO test_innodb_lock VALUES(3,'3');
INSERT INTO test_innodb_lock VALUES(4, '4000');
INSERT INTO test_innodb_lock VALUES(5,'5000');
INSERT INTO test_innodb_lock VALUES(6, '6000');
INSERT INTO test_innodb_lock VALUES(7,'7000');
INSERT INTO test_innodb_lock VALUES(8, '8000');
INSERT INTO test_innodb_lock VALUES(9,'9000');
INSERT INTO test_innodb_lock VALUES(1,'b1');
CREATE INDEX test_innodb_a_ind ON test_innodb_lock(a);
CREATE INDEX test_innodb_lock_b_ind ON test_innodb_lock(b);

set autocommit=0;

讀己知所寫

# 會話1 

# 會話1対test_innodb_lock表做寫操作,但是沒有commit。
# 執行修改SQL之後,查詢一下test_innodb_lock表,發現數據被修改了。
mysql> UPDATE `test_innodb_lock` SET `b` = '88' WHERE `a` = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM `test_innodb_lock`;
+------+------+
| a    | b    |
+------+------+
|    1 | 88   |
|    2 | 3    |
|    3 | 4000 |
|    4 | 5000 |
|    5 | 6000 |
|    6 | 7000 |
|    7 | 8000 |
|    8 | 9000 |
+------+------+
8 rows in set (0.00 sec)

# 會話2 

# 會話2這時候來查詢test_innodb_lock表。發現會話2是讀不到SESSION1未提交的資料的。
mysql> SELECT * FROM `test_innodb_lock`;
+------+------+
| a    | b    |
+------+------+
|    1 | b2   |
|    2 | 3    |
|    3 | 4000 |
|    4 | 5000 |
|    5 | 6000 |
|    6 | 7000 |
|    7 | 8000 |
|    8 | 9000 |
+------+------+
8 rows in set (0.00 se

兩個回話同時更新同一條資料

兩個回話同時更新不同行資料

回話之間互不影響