纸上得来终觉浅,绝知此事要躬行。mysql
从对数据操做的粒度分 :sql
从对数据操做的类型分:数据库
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,'李四');
读锁
数据准备完成接下来咱们进行测试,使用两个客户端链接数据库。
客户端一:
tb_book
表的读锁lock table tb_book read;
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)
能够正常执行,查询出数据。
客户端二:
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)
也能够正常查询数据
客户端一:
select * from tb_user; mysql> select * from tb_user; ERROR 1100 (HY000): Table 'tb_user' was not locked with LOCK TABLES
提示tb_user
没有加锁,没法获取数据
客户端二:
select * from tb_user; mysql> select * from tb_user; +----+--------+ | id | name | +----+--------+ | 1 | 张三 | | 2 | 李四 | +----+--------+ 2 rows in set (0.00 sec)
能够正常查询出未锁定的表
客户端一:
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 得到的是读锁,不能执行更新操做。
客户端二:
insert into tb_book values(null,'Mysql高级','2088-01-01','1');
执行语句阻塞,等待中。
当在客户端一中释放锁指令unlock tables
后 , 客户端二中的 inesrt
语句当即执行。
写锁
客户端一:
lock table tb_book write;
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)
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
insert
操做mysql> insert into tb_book values(null,'Python','2088-01-01','1'); Query OK, 1 row affected (0.00 sec)
delete
操做mysql> delete from tb_book where id = 4; Query OK, 1 row affected (0.00 sec)
当前客户端处于write lock
,其增删改查都正常执行
客户端二:
select * from tb_book;
此时查询操做处于阻塞状态。
客户端三:
update tb_book set name = 'Java' where id = 3;
也处于阻塞状态。
当咱们把客户端一的表解锁,则客户端二的查询操做和客户端三的更新操做马上执行。
客户端一:
tb_book
表的读锁lock table tb_book read;
客户端二:
tb_book
表的读锁lock table tb_book read;
此时客户端一,只能读取加锁的表,客户端二一样只能读取加锁的表,都没法更新表。
tb_book
表的写锁lock table tb_book wtite;
因为客户端一已经上了读锁,此时客户端二上写锁会阻塞。
客户端一:
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,此值高说明存在着较为严重的表级锁争用状况。