數據庫之MyISAM與InnoDB鎖方面的區別

一、數據庫鎖的分類

  • 按鎖的粒度劃分,可分爲頁級鎖、表級鎖、行級鎖
  • 按鎖級別劃分,可分爲共享鎖(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適合的場景

  • 數據增刪改查都相當頻繁
  • 可靠性要求比較高,要求支持事務
  • 高併發場景
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章