[TOC]
### 什么是鎖
鎖是數據庫系統區別于文件系統的關鍵特性。鎖機制用于管理對共享資源的并發訪問。
### lock 與 latch
latch 一般稱為閂鎖(輕量級的鎖),因為其要求鎖定的世界必須非常短。若持續的時間長,則應用性能會非常差。分為mutex(互斥量)和rwlock(讀寫鎖)。其目的是用來保證并發線程操作臨界資源的正確性,并且通常沒有死鎖檢測的機制。
lock 的對象是事務,用來鎖定的是數據庫中的對象,如表、頁、行。并且一般的對象盡在事務commit 或者rollback后釋放。有死鎖機制的。

對于latch 可以通過`show engine innodb mutex`來進行查看


### Innodb 存儲引擎中的鎖
(1)共享鎖(s),允許事務讀一行數據。
(2)排它鎖(x)事務刪除或者更新一行數據。

意向鎖是將鎖定的對象分為多個層次,意向鎖意味著事務希望在更細粒度(fine granularity)上進行加鎖。
在innodb 1.0版本之前,用戶只能通過命令show full processlist,show engine innodb status等來查看當前數據庫中鎖的請求,然后在判斷事務鎖的情況。 1.0版本之后 在INFORMATION_SCHEMA 架構下添加了表INNODB_TRX、INNODB_LOCK、INNODB_LOCK_WAITS。這三張表分析鎖問題。

如上圖可以通過下面的命令,獲取鎖的信息:
`select * from information_schema.innodb_trx\G;`

輸入`select * from information_schema.innodb_locks`;
可以獲取如上圖的相關信息
InnoDB存儲引擎對于select語句支持兩種一致性的鎖定讀(locking read)操作:
(1)`select ... for update`(排它鎖 X)其他事務不能對已鎖定的行加上任何鎖。
(2)`select ... lock in share mode`(共享鎖) 其他事務可以向被鎖定的行加s鎖,但是如果加x鎖就會別阻塞。
#### 自增長及鎖
mysql5.1.22版本開始 innodb存儲引擎提供了`innodb_autoinc_lock_mode`來控制自增長的模式 默認值為1。

innodb:必須是索引的第一列 myisam不需要。
#### 外鍵和鎖
innodb 使用外鍵如果沒有加索引 搜索引擎會自動將其添加索引。因為這樣就可以避免表鎖。
### 鎖的算法
#### 行鎖的3中算法
(1)Record Lock:單行記錄上的鎖 鎖住的是索引記錄 如果沒有任何一個索引 那么使用隱式的主鍵來進行鎖定。
(2)Gap Lock:間隙鎖,鎖定一個范圍,但不包含記錄本身
(3)Next-key Lock : Gap Lock + Record Lock,鎖定一個范圍,并鎖定記錄本身。

~~~~
create table z (a int,b int ,primary key(a),key(b));
insert into z select 1,1;
insert into z select 3,1;
insert into z select 5,3;
insert into z select 7,6;
insert into z select 10,8;
select * from z where b=3 for update;
~~~~
很明顯,這時sql語句通過索引列b進行查詢, 會使用傳統的Next-key Lock 技術加鎖,并且由于兩個索引,其需要分別進行鎖定。對于聚集索引,其僅對列a等于5的索引加上Record Lock。 而對于輔助索引,其加上的是Next-Key Lock,鎖定的范圍是(1,3),特別需要注意的是,InnoDB存儲引擎還會對輔助索引下一個鍵位加上gap lock,即還有一個輔助索引范圍為(3,6)的鎖,因此,若在新會話B中運行下面的sql語句執行會被堵塞:
`select * from z where a= 5 lock in share mode;`(第一條)
`insert into z select 4,2;`(第二條)
`insert into z select 6,5;`(第三條)
第一條語句不會被執行,因為會話A中執行的sql語句已經吧聚集索引列a=5的值加上X鎖,因此執行會阻塞。
第二天語句主鍵插入沒有4,沒有問題,但是插入輔助索引值2在鎖定的范圍(1,3)中,因此執行同樣會被阻塞。第三個sql語句,插入的主鍵沒有被鎖定,5也不在(1,3)之間。但插入的值5在另一個鎖定的范圍(3,6)中,故同樣需要等待。
下面的sql語句不會被阻塞,可以立即執行:
`insert into z select 8,6;`
`insert into z select 2,0;`
`insert into z select 6,7;`
若此時沒有Gap Lock 鎖定(3,6),那么用戶可以插入索引b列為3的記錄,這會導致會話A中的用戶再次執行插敘時會返回不同的記錄,即導致 phantom problem(幻讀)問題的產生。
可以通過一些的方式顯式地關閉Gap Lock;
(1)將事務的隔離級別設置為READ COMMITTED
(2)將參數innodb_locks_unsafe_for_binlog設置為1
在上述的配置下,除了外鍵約束和唯一性檢查依然需要的Gap Lock,其余情況僅使用Record Lock進行鎖定。 上述設置破壞了事務的隔離性,并且對于replication(復制),可能會導致主從數據的不一致。 而且從性能上來看 read committed 不會優于事務隔離級別read repeatable。
對于inset的操作, 如果被鎖定則不允許查詢。 會話A中表示已經鎖定了 所以其他會話插入同樣會導致阻塞。
#### 解決 phantom problem(幻讀)