数据库事务的并发-锁机制-mysql

数据库为什么需要锁

锁机制:解决因资源共享而造成的并发问题。

事务在并发执行时,对同一张表(或同一条数据)进行操作而引发的问题

示例:买最后一件衣服X

A: X 买:X加锁->试衣服...下单..付款..打包->X解锁

B: X 买:发现X己被加锁,等待X解锁, X己出售

分类

操作类型

a. 读锁(共享锁):对同一个数据(衣服),多个读操作可以同时进行,互不干扰。

b. 写锁(互斥锁):如果当前写操作没有完毕(买衣服的一系列操作),则无法进行其他的读操作、写操作

操作范围

a. 表锁
一次性对一张表加锁
MyISAM存储引擎认使用表锁
开销小,加锁快,无死锁,但锁的范围大,容易发生锁冲突,并发度低

b. 行锁
一次性对一条数据加锁
InnoDB存储引擎认使用行锁
开销大,加锁慢,容易出现死锁,锁的范围较小,不容易发生锁冲突,并发度高
(很小概率发生高并发问题:脏读、幻读、不可重复读、丢失更新等问题)

c. 页锁

建议:高并发采用InnoDB,否则采用MyISAM

(1)表锁(MyISAM)

锁相关命令

增加锁:
locak table 表1 read/write,表2 read/write,…

査看加锁的表:
show open tables ;

释放锁:
unlock tables ;

会话 session :每一个访问数据的dos命令行、数据库客户端工具都是一个会话

准备数据

create table tablelock
(
id int primary key auto_increment, --自增操作MysqL/sqlSERVER支持;oracle需要借助于序列来实现自增
name varchar(20)
)engine myisam;

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

===加读锁

会话0:

lock table tablelock read ;  --对tablelock加读锁

-- 测试对tablelock的读与写
select * from tablelock; --读(査),可以
delete from tablelock where id =1 ; --写(增刪改),不可以

-- 测试对其他表的读与写
select * from emp ; --读,不可以
delete from tablelock where eno = 1; --写,不可以

小结:
如果某一个会话对A表加了read锁,则该会话可以对A表进行读操作、不能进行写操作;且该会话不能对其他表进行读、写操作。
--即如果给A表加了读锁,则当前会话只能对A表进行读操作。

image

会话1 (其他会话):

select * from tablelock; --读(査),可以
delete from tablelock where id =1 ; --写,需要"等待"会话0将锁释放

select * from emp ; --读(査),可以
delete from emp where eno = 1; -- 写,可以

--总结:
会话0给A表加了锁,则其他会话:
a.可以对其他表(A表以外的表)进行读、写操作
b.对A表:读-可以;写-需要等待释放锁。

===加写锁:

会话0:
lock table tablelock write ;

结论和读锁大致一样:
当前会话(会话0)可以对加了写锁的表进行任何操作(增刪改査);但是不能操作其他表

其他会话:
对会话0中加写锁的表可以进行增删改查的前提是:等待会话0释放写锁

进行了两次加锁,Navicat死了
刚试了貌似只有读锁可以反复加,写锁只能一个会话加,这也符合上一节课的逻辑

MysqL表级锁的锁模式

MyISAM在执行査询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(DML)前,会自动给涉表加写锁。所以对MyISAM表进行操作,会有以下情况:
a、 对MyISAM表的读操作(加读锁),不会阻塞其他进程(会话)对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
b、对MyISAM表的写操作(加写锁).会阻塞其他进程(会话)对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。

分析表锁定

查看哪些表加了锁:
show open tables ; -- 1代表被加了锁

分析表锁定的严重程度:
show status like 'table%' ;

image

Table_locks_immediate: 即可能获取到的锁数
Table_locks_waited: 需要等待的表锁数(如果该值越大,说明存在越大的锁竞争)

建议:
Table_locks_immediate/Table_locks_waited > 5000,建议采用InnoDB引擎,否则MyISAM引擎

如果需要等待的表锁数大了,这个比例越低,表示锁冲突越严重,为什么要选择MyISAM,不应该是才用InnoDB

(2)行锁(InnoDB)

行锁相关命令

表锁是通过unlock tables,也可以通过事务解锁;行锁是通过事务解锁。

关闭自动commit

--MysqL自动commit; oracle认不会自动commit ;
为了研究行锁,暂时将自动commit关闭,以后需要通过commit

自动提交关闭: 共有三种方式

-- 关闭自动commit;

 set autocommit =0 ;
 start transaction ;
 begin;

准备数据

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');

commit ;

写锁

两个会话update或delete同一条数据(即便是第一个会话刚插入还没有来得及提交的数据)

操作同一条数据
会话0:写操作
insert into linelock values ('a6');

会话1:写操作同样的数据
update linelock set name='ax' where id = 6;

image

image

小结:
如果会话x对某条数据a进行DML操作(研究时:关闭自动commit的情况下),则其他会话必须等待会话x结束事务(commit/rollback)后才能对数据a进行操作。

操作不同数据

会话0:写操作
insert into linelock values(8, 'a8');

会话1:写操作,不同的数据
update linelock set name='ax' where id = 5;

小结:
行锁,一次锁一行数据;因此如果操作的是不同数据,则不干扰。

行锁会转为表锁

如果没有索引,则行锁会转为表锁

show index from linelock ;
alter table linelock add index idx_linelock_name(name);

-- 索引未失效
会话0:写操作
update linelock set name = 'ai' where name = '3';
会话1: 写操作,不同的数据
update linelock set name = 'diX' where name = '4' ;

-- 索引失效
会话0:写操作
update linelock set name = 'ai' where name = 3 ;
会话1:写操作,不同的数据
update linelock set name = 'aiX' where name = 4;
-- 可以发现数据被阻塞了(加锁)

原因: 如果索引类 发生了类型转换,则索引失效。因此此次操作,会从行锁转为表锁。

间隙锁

行锁的一种特殊情况:间隙锁:值在范围内,但却不存在

update linelock set name = 'x' where id >1 and id<9 ; -- 此时linelock表中没有id=7的数据

在此where范围中,没有 id=7的数据,则id=7的数据成为间隙。MysqL自动给间隙加锁->间隙锁。即本题会自动给id=7的数据加间隙锁(行锁)。

行锁:如果有where,则实际加锁的范围就是where后面的范围(不是实际的值)

读锁

如果仅仅是査询数据,能否加锁?

可以,通过for update对query语句进行加锁。
select * from linelock where id=2 for update;

image

MysqL行级锁的锁模式

InnoDB认采用行锁;

缺点:比表锁性能损耗大。

优点:并发能力强,效率高。

因此建议,高并发用InnoDB,否则用MyISAM.

加锁机制规律总结

表锁:
会话(事务) 对那张表加锁,那当前会话 就只能操作那张表,如果是读锁,那就只能读,如果是写锁,那就可以增删改查。

其他会话 对该表操作: 如果是读锁,那就可以读,写的话需要等待锁释放;如果是写锁的话,读与写 都需要等待锁释放。

行锁:上面的表换成行

相关文章

MySQL 死锁 是指两个或多个事务互相等待对方持有的锁,从而导...
在MySQL中,InnoDB引擎通过Next-Key Locking技术来解决幻读问...
在数据库事务管理中,Undo Log 和 Redo Log 是两种关键日志,...
case when概述 sql语句中的case语句与高级语言中的switch语句...
其实很简单,只是为了忘记,做个记录,用的时候方便。 不管是...
1.进入服务,找到mysql服务,在属性里找到mysql的安装路径 2...