数据库为什么需要锁
锁机制:解决因资源共享而造成的并发问题。
事务在并发执行时,对同一张表(或同一条数据)进行操作而引发的问题
示例:买最后一件衣服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表进行读操作。
会话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%' ;
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;
小结:
如果会话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;
MysqL行级锁的锁模式
InnoDB默认采用行锁;
缺点:比表锁性能损耗大。
优点:并发能力强,效率高。
因此建议,高并发用InnoDB,否则用MyISAM.
加锁机制规律总结
表锁:
会话(事务) 对那张表加锁,那当前会话 就只能操作那张表,如果是读锁,那就只能读,如果是写锁,那就可以增删改查。
其他会话 对该表操作: 如果是读锁,那就可以读,写的话需要等待锁释放;如果是写锁的话,读与写 都需要等待锁释放。
行锁:上面的表换成行
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。