Mysql学习 —— 锁机制
Posted Johnny*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql学习 —— 锁机制相关的知识,希望对你有一定的参考价值。
锁机制
解决因资源共享而造成的并发问题
分类
按操作类型
- 读锁(共享锁): 对同一个数据,多个读操作可以 同时进行,互不干扰。
- 写锁(排他锁): 如果当前写操作 没有完毕,则无法进行其他的读、写操作。
操作范围
- 表锁
- 行锁
代码实操
创建表
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处于阻塞状态
结论:
- 如果会话x对某条数据a进行 DML操作,则其他会话必须等待会话x结束事务(commit/rollback)后 才能对数据a进行操作(增删改查)。
- 表锁 是通过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优化]