相關免費學習推薦:mysql教學
資源開銷比行鎖少,不會出現死鎖的情況,但是發生鎖衝突的機率很大。被大部分的mysql引擎支持,MyISAM和InnoDB都支援表級鎖,但是InnoDB預設的是行級鎖。
表鎖定由 MySQL Server 實現,一般在執行 DDL 語句時會對整個表進行加鎖,比如說 ALTER TABLE 等操作。在執行 SQL 語句時,也可以明確指定對某個表進行加鎖。 表鎖使用的是一次性鎖技術,也就是說,在會話開始的地方使用lock 指令將後續需要用到的表都加上鎖,在表釋放前,只能存取這些加鎖的表,不能存取其他表,直到最後透過unlock tables 釋放所有表鎖。 除了使用unlock tables 顯示釋放鎖定之外,會話持有其他表鎖定時執行lock table 語句會釋放會話之前持有的鎖定;會話持有其他表鎖定時執行start transaction 或begin 開啟事務時,也會釋放先前持有的鎖。共享鎖定用法:
LOCK TABLE table_name [ AS alias_name ] READ复制代码
排它鎖定用法:
LOCK TABLE table_name [AS alias_name][ LOW_PRIORITY ] WRITE复制代码
解鎖用法:
unlock tables;复制代码行鎖行級鎖定是Mysql中鎖定粒度最細的一種鎖,表示只針對目前操作的行進行加鎖。
行級鎖定能大幅減少資料庫操作的衝突。其加鎖粒度最小,但加鎖的開銷也最大。有可能會出現死鎖的情況。 行級鎖定依使用方式分為共享鎖和排他鎖。
不同儲存引擎的行鎖實作不同,後續沒有特別說明,則行鎖特指 InnoDB 實作的行鎖。 在了解 InnoDB 的加鎖原理之前,需要對其儲存結構有一定的了解。 InnoDB 是叢集索引,也就是 B 樹的葉節點既儲存了主鍵索引也儲存了資料行。而 InnoDB 的二級索引的葉節點儲存的則是主鍵值,所以透過二級索引查詢資料時,還需要拿對應的主鍵去叢集索引中再次進行查詢。關於MySQL索引的詳細知識可以查看《MySQL索引底層資料結構與演算法》。 下面以兩條 SQL 的執行為例,請說明 InnoDB 對於單行資料的加鎖原理。update user set age = 10 where id = 49; update user set age = 10 where name = 'Tom';复制代码第一條SQL 使用主鍵索引來查詢,則只需要在id = 49 這個主鍵索引上加上寫鎖定;第二個SQL 則使用二級索引來查詢,則先在name = Tom 這個索引上加寫鎖,然後由於使用InnoDB 二級索引還需再次根據主鍵索引查詢,所以還需要在id = 49 這個主鍵索引上加寫鎖,如上圖所示。 也就是說使用主鍵索引需要加一把鎖,使用二級索引需要在二級索引和主鍵索引上各加一把鎖。 根據索引對單行資料進行更新的加鎖原理了解了,那如果更新操作涉及多個行呢,例如下面 SQL 的執行場景。
update user set age = 10 where id > 49;复制代码這種場景下的鎖的釋放較為複雜,有多種的優化方式,我對這塊暫時還沒有了解,還請知道的小夥伴在下方留言解釋。 頁鎖定頁級鎖定是MySQL中鎖定粒度介於行級鎖定和表格層級鎖定中間的一種鎖定。表級鎖定速度快,但衝突多,行級衝突少,但速度慢。所以取了折衷的頁級,一次鎖定鄰近的一組記錄。 BDB支援頁級鎖定。
共享鎖定又稱為讀鎖,是讀取操作所建立的鎖定。其他使用者可以並發讀取數據,但任何事務都不能對數據進行修改(獲取數據上的排他鎖),直到已釋放所有共享鎖。
如果交易T對資料A加上共享鎖定後,則其他交易只能對A再加共享鎖,不能加排他鎖。獲準共享鎖的事務只能讀數據,不能修改數據。
SELECT ... LOCK IN SHARE MODE;
在查詢語句後面增加LOCK IN SHARE MODE
#, Mysql會對查詢結果中的每行都加共享鎖,當沒有其他執行緒對查詢結果集中的任何一行使用排他鎖時,可以成功申請共享鎖,否則會被阻塞。其他執行緒也可以讀取使用了共享鎖的表,而這些執行緒讀取的是同一個版本的資料。
排他鎖又稱寫鎖,如果事務T對資料A加上排他鎖後,則其他事務不能再對A加任任何類型的封鎖。獲準排他鎖的事務既能讀取數據,又能修改數據。
SELECT ... FOR UPDATE;
#在查詢語句後面增加FOR UPDATE
,Mysql就會對查詢結果中的每行都加排他鎖,當沒有其他執行緒對查詢結果集中的任何一行使用排他鎖時,可以成功申請排他鎖,否則會被阻塞。
在資料庫的鎖定機制中介紹過,資料庫管理系統(DBMS)中的並發控制的任務是確保在多個事務同時存取資料庫中同一資料時不會破壞事務的隔離性和統一性以及資料庫的統一性。
樂觀並發控制(樂觀鎖)和悲觀並發控制(悲觀鎖)是並發控制主要採用的技術手段。
無論是悲觀鎖或樂觀鎖,都是人們定義出來的概念,可以認為是一種想法。其實不只是關係型資料庫系統中有樂觀鎖和悲觀鎖的概念,像memcache、hibernate、tair等都有類似的概念。
針對於不同的業務場景,應該選用不同的並發控制方式。所以,不要把樂觀並發控制和悲觀並發控制狹義的理解為DBMS中的概念,更不要把他們和資料中提供的鎖機制(行鎖、表鎖、排他鎖、共享鎖)混為一談。其實,在DBMS中,悲觀鎖定正是利用資料庫本身所提供的鎖定機制來實現的。
在關係資料庫管理系統裡,悲觀並發控制(又稱“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)是一種並發控制的方法。它可以阻止一個事務以影響其他使用者的方式來修改資料。如果一個事務執行的操作對某行資料套用了鎖,那就只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖衝突的操作。悲觀並發控制主要用於資料爭用激烈的環境,以及發生並發衝突時使用鎖定保護資料的成本要低於回溯事務的成本的環境。
悲觀鎖,如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度(悲觀),因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依賴資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料存取的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改資料)
悲觀鎖定實際上是採取了「先取鎖在存取」的策略,為資料的處理安全提供了保證,但是在效率方面,由於額外的加鎖機制產生了額外的開銷,並且增加了死鎖的機會。並且降低了並發性;當一個事物所以一行資料的時候,其他事物必須等待該交易提交之後,才能操作這行資料。
在關聯式資料庫管理系統裡,樂觀並發控制(又稱為“樂觀鎖”,Optimistic Concurrency Control,縮寫“OCC”)是一種並發控制的方法。它假設多用戶並發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分資料。在提交資料更新之前,每個事務會先檢查在該事務讀取資料後,有沒有其他事務又修改了該資料。如果其他事務有更新的話,正在提交的交易會進行回滾。
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為資料一般情況下不會造成衝突,所以在資料提交更新的時候,才會正式對資料的衝突與否進行檢測,如果發現衝突了,則讓傳回使用者錯誤的訊息,讓使用者決定如何做。
相對於悲觀鎖,在對資料庫進行處理的時候,樂觀鎖並不會使用資料庫提供的鎖定機制。一般的實現樂觀鎖的方式就是記錄資料版本。
資料版本,為資料增加的一個版本標識。當讀取資料時,將版本標識的值一同讀出,資料每更新一次,同時對版本標識進行更新。當我們提交更新的時候,判斷資料庫表對應記錄的當前版本資訊與第一次取出的版本標識進行比對,如果資料庫表目前版本號與第一次取出的版本標識值相等,則予以更新,否則認為是過期數據。
樂觀並發控制相信事務之間的資料競爭(data race)的機率是比較小的,因此盡可能直接做下去,直到提交的時候才去鎖定,所以不會產生任何鎖和死鎖。但如果直接簡單這麼做,還是有可能會遇到不可預期的結果,例如兩個事務都讀取了資料庫的某一行,經過修改以後寫回資料庫,這時就遇到了問題。
由於表鎖定和行鎖定雖然鎖定範圍不同,但是會互相衝突。所以當你要加表鎖時,勢必要先遍歷該表的所有記錄,判斷是否加有排他鎖。這種遍歷檢查的方式顯然是一種低效率的方式,MySQL 引入了意向鎖,來偵測表鎖和行鎖的衝突。
意向鎖也是表格級鎖,也可分為讀取意向鎖(IS 鎖)和寫入意向鎖(IX 鎖)。當交易要在記錄上加上讀鎖或寫鎖時,要先在表上加上意向鎖。這樣判斷表中是否有記錄加鎖就很簡單了,只要看下表上是否有意向鎖就行了。
意向鎖之間是不會產生衝突的,也不和AUTO_INC 表鎖衝突,它只會阻塞表級讀鎖或表級寫鎖,另外,意向鎖也不會和行鎖衝突,行鎖只會和行鎖衝突。
意向鎖定是InnoDB自動加總的,不需要使用者乾預。
對於insert、update、delete,InnoDB會自動將涉及的資料加上排他鎖(X);
對於一般的Select語句,InnoDB不會加任何鎖,事務可以透過以下語句給予顯示加上共享鎖或排他鎖。
意向共享鎖(IS):表示交易準備好給資料行加入共享鎖,也就是說一個資料行加共享鎖前必須先取得表格的IS鎖
意向排他鎖(IX):類似上面,表示交易準備給資料行加入排他鎖,說明交易在一個資料行加排他鎖前必須先取得該表的IX鎖。
記錄鎖定是最簡單的行鎖,並沒有什麼好說的。上邊描述 InnoDB 加鎖原理中的鎖就是記錄鎖,只鎖住 id = 49 或 name = 'Tom' 這筆記錄。
當 SQL 語句無法使用索引時,會進行全表掃描,這個時候 MySQL 會為整張表的所有資料行加上記錄鎖,再由 MySQL Server 層進行篩選。但是,在 MySQL Server 層進行過濾的時候,如果發現不符合 WHERE 條件,會釋放對應記錄的鎖定。這樣做,保證了最後只會持有滿足條件記錄上的鎖,但是每筆記錄的加鎖操作還是不能省略的。
所以更新操作必須要根據索引進行操作,沒有索引時,不僅會消耗大量的鎖定資源,增加資料庫的開銷,還會極大的降低了資料庫的並發效能。
當我們使用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但不存在的記錄,InnoDB 也會對這個「間隙」加鎖,這種鎖定機制就是所謂的間隙鎖。
间隙锁是锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。
间隙锁在 InnoDB 的唯一作用就是防止其它事务的插入操作,以此来达到防止幻读的发生,所以间隙锁不分什么共享锁与排他锁。
要禁止间隙锁,可以把隔离级别降为读已提交,或者开启参数 innodb_locks_unsafe_for_binlog
show variables like 'innodb_locks_unsafe_for_binlog';复制代码
innodb_locks_unsafe_for_binlog
:默认
值为OFF,即启用间隙锁。因为此参数是只读模式,如果想要禁用间隙锁,需要修改 my.cnf(windows是my.ini) 重新启动才行。
# 在 my.cnf 里面的[mysqld]添加 [mysqld] innodb_locks_unsafe_for_binlog = 1复制代码
测试环境:
MySQL5.7,InnoDB,默认的隔离级别(RR)
示例表:
CREATE TABLE `my_gap` ( `id` int(1) NOT NULL AUTO_INCREMENT, `name` varchar(8) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO `my_gap` VALUES ('1', '张三');INSERT INTO `my_gap` VALUES ('5', '李四');INSERT INTO `my_gap` VALUES ('7', '王五');INSERT INTO `my_gap` VALUES ('11', '赵六');复制代码
在进行测试之前,我们先看看 my_gap 表中存在的隐藏间隙:
/* 开启事务1 */BEGIN;/* 查询 id = 5 的数据并加记录锁 */SELECT * FROM `my_gap` WHERE `id` = 5 FOR UPDATE;/* 延迟30秒执行,防止锁释放 */SELECT SLEEP(30); # 注意:以下的语句不是放在一个事务中执行,而是分开多次执行,每次事务中只有一条添加语句/* 事务2插入一条 name = '杰伦' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (4, '杰伦'); # 正常执行/* 事务3插入一条 name = '学友' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (8, '学友'); # 正常执行/* 提交事务1,释放事务1的锁 */COMMIT;复制代码
上述案例,由于主键是唯一索引,而且只使用一个索引查询,并且只锁定了一条记录,所以只会对 id = 5
的数据加上记录锁(行锁),而不会产生间隙锁。
恢复初始化的4条记录,继续在 id 唯一索引列上做以下测试:
/* 开启事务1 */BEGIN;/* 查询 id 在 7 - 11 范围的数据并加记录锁 */SELECT * FROM `my_gap` WHERE `id` BETWEEN 5 AND 7 FOR UPDATE;/* 延迟30秒执行,防止锁释放 */SELECT SLEEP(30); # 注意:以下的语句不是放在一个事务中执行,而是分开多次执行,每次事务中只有一条添加语句/* 事务2插入一条 id = 3,name = '思聪3' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (3, '思聪3'); # 正常执行/* 事务3插入一条 id = 4,name = '思聪4' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (4, '思聪4'); # 正常执行/* 事务4插入一条 id = 6,name = '思聪6' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (6, '思聪6'); # 阻塞/* 事务5插入一条 id = 8, name = '思聪8' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (8, '思聪8'); # 阻塞/* 事务6插入一条 id = 9, name = '思聪9' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (9, '思聪9'); # 阻塞/* 事务7插入一条 id = 11, name = '思聪11' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (11, '思聪11'); # 阻塞/* 事务8插入一条 id = 12, name = '思聪12' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (12, '思聪12'); # 正常执行/* 提交事务1,释放事务1的锁 */COMMIT;复制代码
从上面可以看到,(5,7]、(7,11] 这两个区间,都不可插入数据,其它区间,都可以正常插入数据。所以可以得出结论:当我们给(5,7] 这个区间加锁的时候,会锁住(5,7]、(7,11] 这两个区间。
恢复初始化的4条记录,我们再来测试如果锁住不存在的数据时,会如何?
/* 开启事务1 */BEGIN;/* 查询 id = 3 这一条不存在的数据并加记录锁 */SELECT * FROM `my_gap` WHERE `id` = 3 FOR UPDATE;/* 延迟30秒执行,防止锁释放 */SELECT SLEEP(30); # 注意:以下的语句不是放在一个事务中执行,而是分开多次执行,每次事务中只有一条添加语句/* 事务2插入一条 id = 3,name = '小张' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (2, '小张'); # 阻塞/* 事务3插入一条 id = 4,name = '小白' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (4, '小白'); # 阻塞/* 事务4插入一条 id = 6,name = '小东' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (6, '小东'); # 正常执行/* 事务5插入一条 id = 8, name = '大罗' 的数据 */INSERT INTO `my_gap` (`id`, `name`) VALUES (8, '大罗'); # 正常执行/* 提交事务1,释放事务1的锁 */COMMIT;复制代码
从上面可以看出,指定查询某一条记录时,如果这条记录不存在,会产生间隙锁。
示例表:id 是主键,在 number 上,建立了一个普通索引。
# 注意:number 不是唯一值CREATE TABLE `my_gap1` ( `id` int(1) NOT NULL AUTO_INCREMENT, `number` int(1) NOT NULL COMMENT '数字', PRIMARY KEY (`id`), KEY `number` (`number`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;INSERT INTO `my_gap1` VALUES (1, 1);INSERT INTO `my_gap1` VALUES (5, 3);INSERT INTO `my_gap1` VALUES (7, 8);INSERT INTO `my_gap1` VALUES (11, 12);复制代码
在进行测试之前,我们先来看看 my_gap1 表中 number 索引存在的隐藏间隙:
我们执行以下的事务(事务1最后提交),分别执行下面的语句:
/* 开启事务1 */BEGIN;/* 查询 number = 3 的数据并加记录锁 */SELECT * FROM `my_gap1` WHERE `number` = 3 FOR UPDATE;/* 延迟30秒执行,防止锁释放 */SELECT SLEEP(30); # 注意:以下的语句不是放在一个事务中执行,而是分开多次执行,每次事务中只有一条添加语句/* 事务2插入一条 number = 0 的数据 */INSERT INTO `my_gap1` (`number`) VALUES (0); # 正常执行/* 事务3插入一条 number = 1 的数据 */INSERT INTO `my_gap1` (`number`) VALUES (1); # 被阻塞/* 事务4插入一条 number = 2 的数据 */INSERT INTO `my_gap1` (`number`) VALUES (2); # 被阻塞/* 事务5插入一条 number = 4 的数据 */INSERT INTO `my_gap1` (`number`) VALUES (4); # 被阻塞/* 事务6插入一条 number = 8 的数据 */INSERT INTO `my_gap1` (`number`) VALUES (8); # 正常执行/* 事务7插入一条 number = 9 的数据 */INSERT INTO `my_gap1` (`number`) VALUES (9); # 正常执行/* 事务8插入一条 number = 10 的数据 */INSERT INTO `my_gap1` (`number`) VALUES (10); # 正常执行/* 提交事务1 */COMMIT;复制代码
我们会发现有些语句可以正常执行,有些语句被阻塞来。查看表中的数据:
这里可以看到,number(1,8) 的间隙中,插入语句都被阻塞来,而不在这个范围内的语句,正常执行,这就是因为有间隙锁的原因。
我们再进行以下测试,这里将数据还原成初始化那样
/* 开启事务1 */BEGIN;/* 查询 number = 3 的数据并加记录锁 */SELECT * FROM `my_gap1` WHERE `number` = 3 FOR UPDATE;/* 延迟30秒执行,防止锁释放 */SELECT SLEEP(30);/* 事务1插入一条 id = 2, number = 1 的数据 */INSERT INTO `my_gap1` (`id`, `number`) VALUES (2, 1); # 阻塞/* 事务2插入一条 id = 3, number = 2 的数据 */INSERT INTO `my_gap1` (`id`, `number`) VALUES (3, 2); # 阻塞/* 事务3插入一条 id = 6, number = 8 的数据 */INSERT INTO `my_gap1` (`id`, `number`) VALUES (6, 8); # 阻塞/* 事务4插入一条 id = 8, number = 8 的数据 */INSERT INTO `my_gap1` (`id`, `number`) VALUES (8, 8); # 正常执行/* 事务5插入一条 id = 9, number = 9 的数据 */INSERT INTO `my_gap1` (`id`, `number`) VALUES (9, 9); # 正常执行/* 事务6插入一条 id = 10, number = 12 的数据 */INSERT INTO `my_gap1` (`id`, `number`) VALUES (10, 12); # 正常执行/* 事务7修改 id = 11, number = 12 的数据 */UPDATE `my_gap1` SET `number` = 5 WHERE `id` = 11 AND `number` = 12; # 阻塞/* 提交事务1 */COMMIT;复制代码
查看表中的数据;
这里有一个奇怪的现象:
这是为什么?我们来看看下面的图:
从图中库看出,当 number 相同时,会根据主键 id 来排序
临键锁,是记录锁(行锁)与间隙锁的组合,它的锁范围,即包含索引记录,又包含索引区间。它指的是加在某条记录以及这条记录前面间隙上的锁。假设一个索引包含 15、18、20 ,30,49,50 这几个值,可能的 Next-key 锁如下:
(-∞, 15],(15, 18],(18, 20],(20, 30],(30, 49],(49, 50],(50, +∞)复制代码
通常我们都用这种左开右闭区间来表示 Next-key 锁,其中,圆括号表示不包含该记录,方括号表示包含该记录。前面四个都是 Next-key 锁,最后一个为间隙锁。和间隙锁一样,在 RC 隔离级别下没有 Next-key 锁,只有 RR 隔离级别才有。还是之前的例子,如果 id 不是主键,而是二级索引,且不是唯一索引,那么这个 SQL 在 RR 隔离级别下就会加如下的 Next-key 锁 (30, 49](49, 50)
此时如果插入一条 id = 31 的记录将会阻塞住。之所以要把 id = 49 前后的间隙都锁住,仍然是为了解决幻读问题,因为 id 是非唯一索引,所以 id = 49 可能会有多条记录,为了防止再插入一条 id = 49 的记录。
注意:临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务隔离级别降级为 RC,临键锁则也会失效。
插入意向锁是一种特殊的间隙锁(简称II GAP)表示插入的意向,只有在 INSERT 的时候才会有这个锁。注意,这个锁虽然也叫意向锁,但是和上面介绍的表级意向锁是两个完全不同的概念,不要搞混了。
插入意向锁和插入意向锁之间互不冲突,所以可以在同一个间隙中有多个事务同时插入不同索引的记录。譬如在例子中,id = 30 和 id = 49 之间如果有两个事务要同时分别插入 id = 32 和 id = 33 是没问题的,虽然两个事务都会在 id = 30 和 id = 50 之间加上插入意向锁,但是不会冲突。
插入意向锁只会和间隙锁或 Next-key 锁冲突,正如上面所说,间隙锁唯一的作用就是防止其他事务插入记录造成幻读,正是由于在执行 INSERT 语句时需要加插入意向锁,而插入意向锁和间隙锁冲突,从而阻止了插入操作的执行。
插入意向锁的作用:
AUTO_INC 锁又叫自增锁(一般简写成 AI 锁),是一种表锁,当表中有自增列(AUTO_INCREMENT)时出现。当插入表中有自增列时,数据库需要自动生成自增值,它会先为该表加 AUTO_INC 表锁,阻塞其他事务的插入操作,这样保证生成的自增值肯定是唯一的。AUTO_INC 锁具有如下特点:
使用AUTO_INCREMENT
函数实现自增操作,自增幅度通过 auto_increment_offset
和auto_increment_increment
这2个参数进行控制:
通过使用last_insert_id()函数可以获得最后一个插入的数字
select last_insert_id();复制代码
首先insert大致上可以分成三类:
如果存在自增字段,MySQL 会维护一个自增锁,和自增锁相关的一个参数为(5.1.22 版本后加入) innodb_autoinc_lock_mode
,可以设定 3 值:
MyISam引擎均为 traditonal,每次均会进行表锁。但是InnoDB引擎会视参数不同产生不同的锁,默认为 1:consecutive。
show variables like 'innodb_autoinc_lock_mode';复制代码
innodb_autoinc_lock_mode
为 0 时,也就是 traditional 级别。该自增锁时表锁级别,且必须等待当前 SQL 执行完毕后或者回滚才会释放,在高并发的情况下可想而知自增锁竞争时比较大的。
innodb_autoinc_lock_mode 为 1 时,也就是 consecutive 级别。这是如果是单一的 insert SQL,可以立即获得该锁,并立即释放,而不必等待当前SQL执行完成(除非在其它事务中已经有 session 获取了自增锁)。另外当SQL是一些批量 insert SQL 时,比如 insert into ... select ...
, load data
, replace ... select ...
时,这时还是表级锁,可以理解为退化为必须等待当前 SQL 执行完才释放。可以认为,该值为 1 时相对比较轻量级的锁,也不会对复制产生影响,唯一的缺陷是产生自增值不一定是完全连续的。
innodb_autoinc_lock_mode 为 2 时,也就是 interleaved 级别。所有 insert 种类的 SQL 都可以立马获得锁并释放,这时的效率最高。但是会引入一个新的问题:当 binlog_format 为 statement 时,这是复制没法保证安全,因为批量的 insert,比如 insert ... select ...
语句在这个情况下,也可以立马获取到一大批的自增 id 值,不必锁整个表, slave 在回放这个 SQL 时必然会产生错乱。
如果你的二进制文件格式是mixed | row 那么这三个值中的任何一个对于你来说都是复制安全的。
由于现在mysql已经推荐把二进制的格式设置成row,所以在binlog_format不是statement的情况下最好是innodb_autoinc_lock_mode=2 这样可能知道更好的性能。
鎖的模式有:讀取意向鎖,寫意向鎖,讀鎖,寫鎖和自增鎖(auto_inc)。
IS | IX | S | X | ||
---|---|---|---|---|---|
IS | 相容 | ||||
相容 |
相容 |
IX | |||
相容 |
相容 |
||||
# 相容 |
|
相容 |
|||
X |
意向鎖定之間互不衝突; |
S 鎖只和S/IS 鎖定相容,和其他鎖都衝突;X 鎖和其他所有鎖都衝突; | AI 鎖只和意向鎖定相容; | 鎖定的類型根據鎖定的粒度可以將鎖定細分為表格鎖定 | 和行鎖定,行鎖依場景的不同又可以進一步細分,依序為Next-Key Lock | ,
---|---|---|---|---|
插入意向GAP 鎖。 | 不同的鎖鎖定的位置是不同的,比如說記錄鎖只鎖住對應的記錄,而間隙鎖鎖住記錄和記錄之間的間隔,Next-Key Lock 則所屬記錄和記錄之前的間隙。不同型式鎖的鎖定範圍大致如下圖所示。不同類型鎖定的相容矩陣 | |||
GAP | #NEXT-KEY | II GAP | RECORD | |
#相容 |
||||
#相容 | #GAP |
其中,第一行表示已有的鎖,第一列表示要加的鎖。插入意向鎖較為特殊,所以我們先對插入意向鎖做個總結,如下:
其他類型的鎖的規則較為簡單:
#記錄鎖定和記錄鎖定衝突,Next-key 鎖定和Next-key 鎖定衝突,記錄鎖定和Next-key 鎖定衝突;
以上是今日的深入理解MySQL鎖定類型與加鎖原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!