【MySQL】鎖之MyISAM

紙上得來終覺淺,絕知此事要躬行。mysql

鎖的分類

從對數據操做的粒度分 :sql

  1. 表鎖:操做時,會鎖定整個表。
  2. 行鎖:操做時,會鎖定當前操做行。

從對數據操做的類型分:數據庫

  1. 讀鎖(共享鎖):針對同一份數據,多個讀操做能夠同時進行而不會互相影響。
  2. 寫鎖(排它鎖):當前操做沒有完成以前,它會阻斷其餘寫鎖和讀鎖。

MySQL鎖

MySQL的鎖機制比較簡單,其最顯著的特色是不一樣的存儲引擎支持不一樣的鎖機制。下表中羅列出了各存儲引擎對鎖的支持狀況:併發

存儲引擎 表級鎖 行級鎖 頁面鎖
MyISAM 支持 不支持 不支持
InnoDB 支持 支持 不支持
MEMORY 支持 不支持 不支持
BDB 支持 不支持 支持

特性:性能

鎖類型 特色
表級鎖 偏向MyISAM 存儲引擎,開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
行級鎖 偏向InnoDB 存儲引擎,開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
頁面鎖 開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。

MyISAM表鎖

如何加表鎖

MyISAM 在執行查詢語句SELECT前,會自動給涉及的全部表加讀鎖,在執行更新操做UPDATE、DELETE、INSERT前,會自動給涉及的表加寫鎖,這個過程並不須要用戶干預,所以,用戶通常不須要直接用 LOCK TABLE 命令給 MyISAM 表顯式加鎖。測試

顯示加表鎖語法:線程

加讀鎖 : lock table table_name read;

加寫鎖 : lock table table_name write;

雖然MyIASM默認加了表鎖,可是咱們仍然能夠手動加鎖,具體經過案例來了解一下加鎖以後,咱們操做數據庫會有什麼影響。code

讀寫鎖案例

在進行測試以前咱們須要準備測試環境和相關數據庫以及表,而且把MySQL的存儲引擎指定爲MyISAM,以下先建立一個數據庫並切換到數據庫:blog

create database demo2 default charset=utf8;

接着建立表,本次咱們測試的是MyISAM存儲引擎,因此要建立表時要指定存儲引擎(說明存儲引擎是做用於表,而不是做用於數據庫,也就是說一個數據庫有多個表,而多個表可使用不一樣的存儲引擎)rem

CREATE TABLE `tb_book` (
  `id` INT(11) auto_increment,
  `name` VARCHAR(50) DEFAULT NULL,
  `publish_time` DATE DEFAULT NULL,
  `status` CHAR(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=myisam DEFAULT CHARSET=utf8 ;

# 插入數據

INSERT INTO tb_book (id, name, publish_time, status) VALUES(NULL,'高性能MySQL','2088-08-01','1');
INSERT INTO tb_book (id, name, publish_time, status) VALUES(NULL,'MySQL技術內幕','2088-08-08','0');

CREATE TABLE `tb_user` (
  `id` INT(11) auto_increment,
  `name` VARCHAR(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=myisam DEFAULT CHARSET=utf8 ;

# 插入數據

INSERT INTO tb_user (id, name) VALUES(NULL,'張三');
INSERT INTO tb_user (id, name) VALUES(NULL,'李四');

讀鎖

數據準備完成接下來咱們進行測試,使用兩個客戶端鏈接數據庫。

  • 查詢數據

客戶端一:

  1. 獲取tb_book表的讀鎖
lock table tb_book read;
  1. 執行查詢語句
select * from tb_book;

mysql> select * from tb_book;
+----+-------------------+--------------+--------+
| id | name              | publish_time | status |
+----+-------------------+--------------+--------+
|  1 | 高性能MySQL       | 2088-08-01   | 1      |
|  2 | MySQL技術內幕     | 2088-08-08   | 0      |
+----+-------------------+--------------+--------+
2 rows in set (0.00 sec)

能夠正常執行,查詢出數據。

客戶端二:

  1. 執行查詢操做
select * from tb_book;

mysql> select * from tb_book;
+----+-------------------+--------------+--------+
| id | name              | publish_time | status |
+----+-------------------+--------------+--------+
|  1 | 高性能MySQL       | 2088-08-01   | 1      |
|  2 | MySQL技術內幕     | 2088-08-08   | 0      |
+----+-------------------+--------------+--------+
2 rows in set (0.00 sec)

也能夠正常查詢數據

客戶端一:

  1. 查詢未鎖定的表
select * from tb_user;

mysql> select * from tb_user;
ERROR 1100 (HY000): Table 'tb_user' was not locked with LOCK TABLES

提示tb_user沒有加鎖,沒法獲取數據

客戶端二:

  1. 查詢未鎖定的表
select * from tb_user;

mysql> select * from tb_user;
+----+--------+
| id | name   |
+----+--------+
|  1 | 張三   |
|  2 | 李四   |
+----+--------+
2 rows in set (0.00 sec)

能夠正常查詢出未鎖定的表

  • 插入數據

客戶端一:

  1. 執行插入數據
insert into tb_book values(null,'Mysql高級','2088-01-01','1');

mysql> insert into tb_book values(null,'Mysql高級','2088-01-01','1');
ERROR 1099 (HY000): Table 'tb_book' was locked with a READ lock and can't be updated

執行插入, 直接報錯,因爲當前tb_book 得到的是讀鎖,不能執行更新操做。

客戶端二:

  1. 執行插入數據
insert into tb_book values(null,'Mysql高級','2088-01-01','1');

執行語句阻塞,等待中。

當在客戶端一中釋放鎖指令unlock tables後 , 客戶端二中的 inesrt 語句當即執行。

寫鎖

  • 查詢數據

客戶端一:

  1. 獲取tb_book表的寫鎖
lock table tb_book write;
  1. 執行查詢數據
select * from tb_book;

mysql> select * from tb_book;
+----+-------------------+--------------+--------+
| id | name              | publish_time | status |
+----+-------------------+--------------+--------+
|  1 | 高性能MySQL       | 2088-08-01   | 1      |
|  2 | MySQL技術內幕     | 2088-08-08   | 0      |
|  3 | Mysql高級         | 2088-01-01   | 1      |
+----+-------------------+--------------+--------+
3 rows in set (0.00 sec)
  1. 執行update操做
mysql> update tb_book set name = 'MySQL' where id = 3;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0
  1. 執行insert操做
mysql> insert into tb_book values(null,'Python','2088-01-01','1');
Query OK, 1 row affected (0.00 sec)
  1. 執行delete操做
mysql> delete from tb_book where id = 4;
Query OK, 1 row affected (0.00 sec)

當前客戶端處於write lock,其增刪改查都正常執行

客戶端二:

  1. 執行查詢操做
select * from tb_book;

此時查詢操做處於阻塞狀態。

客戶端三:

  1. 執行更新操做
update tb_book set name = 'Java' where id = 3;

也處於阻塞狀態。

當咱們把客戶端一的表解鎖,則客戶端二的查詢操做和客戶端三的更新操做馬上執行。

鎖上加鎖

  • 不一樣客戶端加鎖

客戶端一:

  1. 獲取tb_book表的讀鎖
lock table tb_book read;

客戶端二:

  1. 獲取tb_book表的讀鎖
lock table tb_book read;

此時客戶端一,只能讀取加鎖的表,客戶端二一樣只能讀取加鎖的表,都沒法更新表。

  1. 獲取tb_book表的寫鎖
lock table tb_book wtite;

因爲客戶端一已經上了讀鎖,此時客戶端二上寫鎖會阻塞。

  • 同一客戶端屢次加鎖

客戶端一:

  1. 執行tb_book表的讀鎖,而後繼續執行寫鎖
lock table tb_book read;
lock table tb_book write;

此時客戶端一能夠對加鎖的表執行增刪改查,客戶端二對加鎖的表增刪改查阻塞。

總結

  • MyISAM表的讀操做,不會阻塞其餘用戶對同一表的讀請求,但會阻塞對同一表的寫請求
  • MyISAM表的寫操做,則會阻塞其餘用戶對同一表的讀和寫操做

​簡而言之,就是讀鎖會阻塞寫,可是不會阻塞讀。而寫鎖,則既會阻塞讀,又會阻塞寫。

此外,MyISAM 的讀寫鎖調度是寫優先,這也是MyISAM不適合作寫爲主的表的存儲引擎的緣由。由於寫鎖後,其餘線程不能作任何操做,大量的更新會使查詢很可貴到鎖,從而形成永遠阻塞。

查詢鎖的爭用狀況

show open tables;

In_user:表當前被查詢使用的次數。若是該數爲零,則表是打開的,可是當前沒有被使用。
Name_locked:表名稱是否被鎖定。名稱鎖定用於取消表或對錶進行重命名等操做。

show status like 'Table_locks%';


mysql> show status like 'Table_locks%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Table_locks_immediate | 139   |
| Table_locks_waited    | 0     |
+-----------------------+-------+
2 rows in set (0.00 sec)

Table_locks_immediate:指的是可以當即得到表級鎖的次數,每當即獲取鎖,值加1。
Table_locks_waited:指的是不能當即獲取表級鎖而須要等待的次數,每等待一次,該值加1,此值高說明存在着較爲嚴重的表級鎖爭用狀況。

相關文章
相關標籤/搜索