SQL优化之锁机制

本文深入探讨了MySQL的锁机制,包括表锁和行锁。分析了读锁和写锁的特性,指出在没有索引的情况下行锁可能升级为表锁,并详细阐述了行锁中的间隙锁情况。通过对不同会话的操作模拟,展示了锁如何影响并发事务的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

锁机制

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

示例:某网购平台平台仅剩一件衣服X
两个Women :WA、 WB
WA : X 抢购 X加锁===>下单===>付款===>卖家收到订单===>抢购完成
WB : X 抢购 X已经加锁===>等待阻塞中===>WA付款之后===>提示已经售空

锁分类

按照操作类型分

1. 读锁(共享锁)===>对于同一个数据(衣服X),多个操作可以同时进行,互不干扰。
2. 写锁(互斥锁)===>若当亲操作没有完成(WA准备对X下手),WB不能进行读写操作。(甚至不能看了)

按照操作范围分

1. 表锁(一次操作锁一张表)MyISAM存储引擎用的是表锁
	1.1. 优势分析: 开销小,加锁快,无死锁.
	1.2. 不足分析:粒度粗,容易放生锁的冲突,并发度低
2. 行锁(一次操作锁一条数据)InnoDB存储引擎使用行锁(MySQL默认)
	2.1. 优势分析:锁的使用粒度细,不容易放生锁冲突,并发度高
	2.2.不足分析:开销大,加锁慢,易死锁。
	2.3 小概率问题:很小概率 发生高并发问题:脏读、幻读、不可重复度、丢失更新等问题
3. 页锁

数据准备

表锁

读锁特性


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');
commit;
-- 增加所的语法
lock table1 read/write ,2 read/write;

--查看加锁的表
show open tables;

	lock table tablelock read;-- 加锁

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

看看我们加了一个读锁,还能不能增删改了?

在这里插入图片描述

会话0(当前本地会话)

		lock table  tablelock read ;
		select * from tablelock; --读(查),可以
		delete from tablelock where id =1 ; --写(增删改),不可以
		select * from emp ; --读,不可以
		delete from emp where eid = 1; --写,不可以

结论1:

		1.如果这个会话0 对A表加了read锁,则该会话 可以对A表进行读操作、不能进行写操作; (出了对自己查之外,其他操作一律不行)
		2.且该会话不能对其他表进行读、写操作。(对其他表甚至不能查了)

其他会话

		select * from tablelock;   --读(查),可以
		delete from tablelock where id =1 ; --写,会“等待”会话0将锁释放
		select * from emp ;  --读(查),可以
		delete from emp where eno = 1; --写,可以	

结论2

	若本地会话(会话0)给A表加了读锁,其他会话想要访问的话会得到如下结论:
	1.这个会话(指代其他会话)可以对其他表进行读写操作(会话0不可以操作其他表)
	2. 对于当前加锁的表A
		读===> 可以读。(跟会话0一样)
		写===> 也可以,但是需要会话0释放锁
	unlock tables ;-- 释放锁

写锁特性

会话0(目前会话)

	lock table tablelock write;-- 加写锁指令

结论

	1. 当前会话(会话0)可以对加了写锁的表进行CRUD操作,但不可对其他表进行操作
	2. 其他会话可以进行增删改查,前提是:会话0释放锁。

MySQL表级锁的锁模式

  • MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁。
  • 在执行更新操作(DML)前,会自动给设计的表加写锁
  • 对MyISAM表的读操作(加读锁),不会阻塞其他进程(会话)对同一表的读请求,
    但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
  • 对MyISAM表的写操作(加写锁),会阻塞其他进程(会话)对同一表的读和写操作,
    只有当写锁释放后,才会执行其它进程的读写操作。

表锁分析

  • List item
	--查看哪些表加了锁:   
	show open tables ; --  1代表被加了锁
	--分析表锁定的严重程度: 
	show status like 'table%' ;
	Table_locks_immediate -- 即可能获取到的锁数
	Table_locks_waited  -- 需要等待的表锁数(如果该值越大,说明存在越大的锁竞争)
	引擎选用建议:
	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')  ;
-- 因为MySQL自动提交 为了研究行锁需要关闭自动提交
set autocommit =0 ;

会话0(当前会话)==>写数据

	 insert into linelock  values(7,'shu');--  写入数据到数据库 ID是7

在这里插入图片描述

会话1(其他会话)===>操作数据

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

等待一段时间就超时了

在这里插入图片描述

	那么我们将这个数据提交一下,再看看

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

	显然这个操作成功了
上面我们操作的是同一条数据,那么操作不用数据呢?
行锁,操作不同数据:
	
	会话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 = 'aiX' where name = '4' ;
	

	
	会话0: 写操作
		update linelock set name = 'ai' where name = 3 ;
		
	会话1: 写操作, 不同的数据
		update linelock set name = 'aiX' where name = 4 ;


结论

  • 可以发现,数据被阻塞了(加锁)
  • 原因:如果索引类 发生了类型转换,则索引失效。 因此 此次操作,会从行锁 转为表锁。

行锁特殊情况(间隙锁)(本该在范围,实际不在)

 --即在此where范围中,没有id=7的数据,则id=7的数据成为间隙。
update linelock set name ='x' where id >1 and id<9 ;  
	间隙:Mysql会自动给 间隙 加索 ->间隙锁。即 本题 会自动给id=7的数据加 间隙锁(行锁)。
	行锁:如果有where,则实际加索的范围 就是where后面的范围(不是实际的值)
	
	--如何仅仅是查询数据,能否加锁? 可以通过
	   for update 
	
	研究学习时,将自动提交关闭:
		set autocommit =0 ;
		start transaction ;
		begin ;
	 select * from linelock where id =2 for update ;

	-- 通过for update对query语句进行加锁。

行锁结论

InnoDB默认采用行锁
缺点:相比表锁性能损耗大,因为粒度更细
有点:并发能力强,效率高
建议:高并发使用InnoDB,否则用MyISAM

行锁分析

	show status like '%innodb_row_lock%' ;-- 查看行锁
		 Innodb_row_lock_current_waits :当前正在等待锁的数量  
		 Innodb_row_lock_time:等待总时长。从系统启到现在 一共等待的时间
		 Innodb_row_lock_time_avg  :平均等待时长。从系统启到现在平均等待的时间
		 Innodb_row_lock_time_max  :最大等待时长。从系统启到现在最大一次等待的时间
		 Innodb_row_lock_waits :	等待次数。从系统启到现在一共等待的次数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值