文章目錄
一、數據庫鎖的分類
- 按鎖的粒度劃分,可分爲頁級鎖、表級鎖、行級鎖
- 按鎖級別劃分,可分爲共享鎖(S)、排他鎖(X)
- 按加鎖方式劃分,可分爲自動鎖、顯式鎖(for update,lock in share mode)
- 按操作劃分,可分爲DML、DDL鎖
- 按使用方式劃分,可分爲樂觀鎖、悲觀鎖
二、MyISAM與InnoDB鎖方面的區別
- InnoDb支持行鎖和表鎖,MyISAM只支持表鎖
- MyISAM的寫請求的優先級比讀請求的優先級高
- MyISAM總是一次性獲得所需的全部鎖,要麼全部滿足,要麼等待,因此不會出現死鎖。
- 但是在InnoDB中,除單個SQL組成的事務外,鎖是逐步獲得的,這就決定了InnoDB發生死鎖是可能的。
我們直接上測試
測試準備:
- 一張基於MyISAM引擎的50萬數據量的表person_info_myisam
- 一張基於InnoDB引擎的50萬數據量的表person_info
- Navicat for MySQL
- mysql 8.x
注:測試數據生成可參考:該博客的中間部分,也可以自行百度。
有朋友可能問,爲什麼要這麼多數據。查詢這些數據需要些時間,在這個時間段內,執行另外一個會話中的sql語句來模擬併發
(一)MyISAM測試:
1、表的設計:
sql腳本:
/*
Navicat MySQL Data Transfer
Source Server : test
Source Server Version : 80014
Source Host : localhost:3306
Source Database : mysql_study
Target Server Type : MYSQL
Target Server Version : 80014
File Encoding : 65001
Date: 2020-04-23 19:25:19
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for person_info_myisam
-- ----------------------------
DROP TABLE IF EXISTS `person_info_myisam`;
CREATE TABLE `person_info_myisam` (
`id` int(7) NOT NULL AUTO_INCREMENT,
`account` varchar(10) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
`area` varchar(20) DEFAULT NULL,
`title` varchar(20) DEFAULT NULL,
`motto` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_account` (`account`) USING BTREE,
KEY `index_area_title` (`area`,`title`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
2、開始測試
- 2.1、測試上了讀鎖之後,能否繼續寫表
我們先測試更新語句速度:update person_info_myisam set account = account where id = 500001;
可以看到很快,那我們先根據主鍵進行查詢
select * from person_info_myisam where id between 1 and 500000;
在查詢期間再來執行上面的更新語句
可以看到被阻塞了,稍作等待,等待查詢一下結束,然後更新語句出現結果
花了2.+秒,和之前的比相差甚遠
2.2、我們再來測試該期間能否select,判斷是否是共享鎖
在會話2中加入select * from person_info_myisam where id = 1;
按照之前的步驟,先執行會話1的select,讀的過程中,MyiSam引擎會給表上讀鎖,然後我們執行會話2的選中的語句,結果如下
時間爲0.000s,幾乎沒用什麼時間,說明MyiSam引擎下的讀鎖爲共享鎖,不阻塞讀操作,其他都阻塞。以上的加鎖都是mysql自動給我們加的,我們可以用以下語句手動加讀鎖和取消它。
-- 添加讀鎖
LOCK TABLES person_info_myisam read;
-- 釋放讀鎖
UNLOCK TABLES;
2.3、測試加寫鎖後,能否讀和寫
會話1語句先執行
-- select語句上排他鎖
select * from person_info_myisam where id between 1 and 500000 FOR UPDATE;
先測試讀:執行會話1的同時執行以下語句
select * from person_info_myisam where id = 1;
我們會發現,本條幾乎不需要時間的語句花了1.8秒,中間被阻塞了
然後測試寫
按照以上步驟,同時執行
update person_info_myisam set account = account where id = 500001;
結果也在預料之中,被阻塞了
以上的測試可以看出,MyiSam引擎加的是表鎖(讀取會話1之外的數據都被阻塞)其共享鎖可讀不可寫,寫鎖(即排它鎖),不可讀也不可寫。
(二)InnoDB測試
1、設計基於InnoDB引擎的person_info表
/*
Navicat MySQL Data Transfer
Source Server : test
Source Server Version : 80014
Source Host : localhost:3306
Source Database : mysql_study1
Target Server Type : MYSQL
Target Server Version : 80014
File Encoding : 65001
Date: 2020-04-23 20:50:27
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for person_info
-- ----------------------------
DROP TABLE IF EXISTS `person_info`;
CREATE TABLE `person_info` (
`id` int(7) NOT NULL AUTO_INCREMENT,
`account` varchar(10) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
`area` varchar(20) DEFAULT NULL,
`title` varchar(20) DEFAULT NULL,
`motto` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3、
同樣我們採用會話1和會話2的方式進行併發測試,注意,InnoDB引擎下不會自動給select上共享鎖,需要我們手動添加lock in share mode
,另外我們需要將自動提交關閉:set autocommit = 0;
可以用show VARIABLES like 'autocommit';
查詢該變量是否爲OFF
3.1、先測試開啓讀鎖後能否讀其他的行(檢測是否爲行鎖)
select * from person_info where id = 3 lock in share mode;
在會話1事務沒提交時,我們查詢id爲5 的數據
select * from person_info where id = 5 lock in share mode;
3.2、說明InnoDB的是行鎖,接下來測試在同一個記錄上能否同時加兩個共享鎖,即多個事務讀
select * from person_info where id = 3 lock in share mode;
兩個會話都執行該sql,結果顯示都能執行,即InnoDB的共享鎖支持其他事務讀
那支不支持其他事務寫呢?
會話1先執行
select * from person_info where id = 3 lock in share mode;
會話2執行
update person_info set title = 'test1' where id = 3;
結果如下:
被阻塞了,然後超時,只有會話1commit才能執行
3.3、排他鎖測試
會話1執行
update person_info set title = 'test3' where id = 5;
會話2執行
select * from person_info where id = 5 lock in share mode;
讀操作被阻塞
update person_info set title = 'test1' where id = 5;
寫操作被阻塞
3.4、我們測試不走索引的情況下會加什麼鎖(行鎖or表鎖?)
爲了方便測試,我將字段name的索引刪除了,
然後將之前兩個會話執行commit
,提交之前的事務,然後會話1執行
update person_info set motto = 'test2' where name = 'BtomubiKI8qv8Xe7KdII';
執行成功,會話2查詢另外一行記錄
select account from person_info where name = 'kKBDcWVDe7KeM0wqoyrl' lock in share mode;
其他行都沒法查詢,說明表被鎖了,也就是說,不走索引,行鎖會升級爲表鎖。
總結
- InnoDB 支持表鎖和行鎖,使用索引作爲檢索條件修改數據時採用行鎖,否則採用表鎖
- InnoDB 自動給修改操作加鎖,給查詢操作不自動加鎖
- 行鎖可能因爲未使用索引而升級爲表鎖,所以除了檢查索引是否創建的同時,也需要通過explain執行計劃查詢索引是否被實際使用。
- 行鎖相對於表鎖來說開銷要大,但優勢在於高併發場景下表現更突出
兩個引擎下的共享鎖和排斥鎖兼容性如下:
myisam適合的場景
- 頻繁執行全表count語句,存有變量記錄行數
- 對數據進行增刪改的頻率不高,查詢非常頻繁,(共享鎖可用)
- 沒有事務
myisam引擎中的數據和文件時分離的,是非聚集索引,索引保存的是數據文件的指針,主鍵索引和輔助索引是獨立的,因此,myisam引擎在純檢索系統中或者增刪改很少的系統中,性能要好於InnoDB引擎。
InnoDB適合的場景
- 數據增刪改查都相當頻繁
- 可靠性要求比較高,要求支持事務
- 高併發場景