Mysql学习 —— 锁机制

Posted Johnny*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql学习 —— 锁机制相关的知识,希望对你有一定的参考价值。

锁机制

解决因资源共享而造成的并发问题

分类

按操作类型

  1. 读锁(共享锁): 对同一个数据,多个读操作可以 同时进行,互不干扰。
  2. 写锁(排他锁): 如果当前写操作 没有完毕,则无法进行其他的读、写操作。

操作范围

  1. 表锁
  2. 行锁

代码实操

创建表

create table tablelock
(
id int primary key auto_increment , 
name varchar(20)
)engine myisam;


insert into tablelock(name) values('a1');
insert into tablelock(name) values('a2');
insert into tablelock(name) values('a3');
insert into tablelock(name) values('a4');
insert into tablelock(name) values('a5');

表锁

已经预先使用下面SQL语句在sqlopt数据库下创建了student表。


drop table if exists student;
create table student(id int primary key auto_increment , name char(16) ) engine myisam;
insert into student(name) values ('a');
insert into student(name) values ('b');

增加读锁

会话0


lock table tablelock read;    #  给tablelock增加读锁
select * from tablelock;  # 会话0可以读取tablelock内容
select * from student;  #  会话0无法进行读取其他表
delete from tablelock where id =1 ;  #  会话0无法对tablelock进行写操作

在这里插入图片描述

unlock tables; # 会话0释放掉自己加在tablelock的读锁
select * from student;  # 释放掉锁之后,会话0才可以读取其他表

在这里插入图片描述

结论:

如果一个会话给A表增加了读锁,那么该会话只能对该表进行读操作,不能进行写操作。且该会话不能对其他表进行读、写操作。

会话1

select * from tablelock;  # 会话1可以读取被会话0加了读锁的tablelock
select * from student;	# 会话1也可以读取其他表

在这里插入图片描述
此时会话0还仍持有对tablelock的表锁。

delete from tablelock where id =2 ;  # 会话1不能对被 会话0加了读锁 的tablelock进行dml操作,必须等会话0释放掉锁

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

结论:
会话0给A表加了读锁之后,其他会话可以对A表进行读操作,但是写操作会被阻塞,需等待会话0释放锁后才能执行。
其他会话也可以对除A表的其他进行读、写操作。

增加写锁

会话0

lock table tablelock write;    # 给tablelock 增加写锁
select * from tablelock;  #  会话0可以对加了表锁的tablelock进行增删改查
insert into tablelock(name) values('6');

在这里插入图片描述

会话1

select * from tablelock;  #  会话1不能对有了表锁的tablelock进行增删改查
insert into tablelock(name) values('6');

在这里插入图片描述

分析表锁定

show open tables;

在这里插入图片描述
分析表锁定的严重程度;

mysql> show status like 'table%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Table_locks_immediate      | 122   |   #  立即能获取到的表
| Table_locks_waited         | 0     |	 #  需要等待的表锁数,该值越大,说明存在越大的锁竞争
| Table_open_cache_hits      | 12    |
| Table_open_cache_misses    | 2     |
| Table_open_cache_overflows | 0     |
+----------------------------+-------+
5 rows in set (0.00 sec)

一般建议:
Table_locks_immediate / Table_locks_waited > 5000,建议采用InnoDB否则使用MyISAM。

行锁

create table linelock(
id int(5) primary key auto_increment,
name varchar(20)
)engine=innodb ;
insert into linelock(name) values('1')  ;
insert into linelock(name) values('2')  ;
insert into linelock(name) values('3')  ;
insert into linelock(name) values('4')  ;
insert into linelock(name) values('5')  ;

msql 默认自动commit,为研究行锁,暂时 将自动commit关闭,通过如下设置:

set autocommit =0;

不同会话对同一行数据进行增删改

会话0,写操作:

insert into linelock(name) values('a7');  # 当前会话对加了写锁的表可以进行任何操作(增删改查),但是不能操作其他表
select * from linelock where id=7;   #  即使上面语句没有commit, select也会查询出上面记录

会话1,写操作

update linelock set name='ax7' where id=7; #  由于会话0还没有commit/rollback事务,所以会话1处于阻塞状态

在这里插入图片描述

结论:

  1. 如果会话x对某条数据a进行 DML操作,则其他会话必须等待会话x结束事务(commit/rollback)后 才能对数据a进行操作(增删改查)。
  2. 表锁 是通过unlock tables,也可以通过事务解锁 ; 行锁 是通过事务解锁,也就是commit和rollback。

不同会话对不同数据进行增删改

会话0插入id=8的数据

insert into linelock values(8,'a8');

会话1更新id=5的记录

update linelock set name='ax' where id=5;

在这里插入图片描述

结论:

如果操作不同记录,互不影响。

行锁的注意事项

如果某个字段没有建立索引或者索引失效,则会从行锁升级为表锁

show index from linelock;	# 查找linelock的索引
alter table linelock add index idx_linelock_name(name); # 对name字段增加索引

在这里插入图片描述
会话0

update linelock set name='johnny'  where name='3';

会话1

update linelock set name = 'hahaa' where name = '4' ;

操作不同记录此时互不影响。但是下面索引失效时候,行锁转换为表锁。

会话0

update linelock set name='jj1'  where name=3;

会话1

update linelock set name = 'w123' where name =4;

在这里插入图片描述

行锁还存在一种特殊的情况,间隙锁。间隙锁就是 值在范围内,但却该值所对应的记录却不存在表内。如此时在linelock中,id范围在(1,10)之间的记录没有9。那么当更新这个范围的值时,Mysql会自动给id=9的数据加上间隙锁。

update linelock set name ='x'  where id >1 and id <10;
insert into linelock(id,name) values(9,'kk');

在这里插入图片描述

在这里插入图片描述
结论

行锁,如果where后面有范围,则加锁范围为where所写的范围。也就是说如果表中没有的记录在where范围中,那么也会被加行锁,这种行锁称为间隙锁。

行锁分析

show status like '%innodb_row_lock%';
+-------------------------------+--------+
| Variable_name                 | Value  |
+-------------------------------+--------+
| Innodb_row_lock_current_waits | 0      |  # 当前正在等待锁的数量
| Innodb_row_lock_time          | 146329 |	# 等待的总时长。 从系统启动到现在,一共等待的时间
| Innodb_row_lock_time_avg      | 36582  |	# 平均等待时长。 从系统启动到现在,平均等待的时间
| Innodb_row_lock_time_max      | 51026  |  # 最大等待时长。 从系统启动到现在,最大等待的时间
| Innodb_row_lock_waits         | 4      |  # 等待次数。 从系统启动到现在,一共等待的次数。
+-------------------------------+--------+
5 rows in set (0.00 sec)

以上是关于Mysql学习 —— 锁机制的主要内容,如果未能解决你的问题,请参考以下文章

重点知识学习(9.4)--[浅入MySQL数据库锁机制以及SQL优化]

Java学习---Java的锁和Mysql的锁机制

学习笔记MySQL数据库高级版 - 索引优化慢查询锁机制等

MySQL的锁机制:MyISAM 表锁InnoDB行锁

重新学习MySQL数据库7:详解MyIsam与InnoDB引擎的锁实现

MySQL学习笔记之四:并发控制和事务机制