mysql学习笔记-锁

1、MySQL并发事务访问相同记录

1.1 、读-读情况
读-读 情况,即并发事务相继 读取相同的记录。读取操作本身不会对记录有任何影响,并不会引起什么问题,所以允许这种情况的发生。
1.2 、写-写情况
写-写 情况,即并发事务相继对相同的记录做出改动。 所有隔离级别都解决了这种最严重问题。
1.3、读-写或写-读情况
读-写或 写-读,即一个事务进行读取操作,另一个进行改动操作。这种情况下可能发生 脏读 、 不可重复读 、 幻读 的问题
1.4 并发问题的解决方案。
方案一:读操作利用多版本并发控制(MVCC,下章讲解),写操作进行加锁。
方案二:读、写操作都采用 加锁 的方式。

2、不同角度锁的分类

在这里插入图片描述

2.1、从数据操作的类型划分:读锁、写锁

锁定读:
对读取的记录加 S锁:别的事务可以继续获取这些记录的S锁
对读取的记录加 X锁:其他事务既不能读也不能写
写操作:
DELETE :定位记录、获取记录X锁、执行delete mark操作
UPDATE:
①情况1:未修改该记录的键值,并且被更新的列占用的存储空间在修改前后未发生变化
则先在 B+ 树中定位到这条记录的位置,然后再获取一下记录的 X锁,最后在原记录的位置进行修改操作。我们也可以把这个定位待修改记录在 B+ 树中位置的过程看成是一个获取 X锁 的 锁定读。
②未修改该记录的键值,并且至少有一个被更新的列占用的存储空间在修改前后发生变化。
则先在 B+ 树中定位到这条记录的位置,然后获取记录的 X锁,将该记录彻底删除掉(就是把记录彻底移入垃圾链表),最后再插入一条新记录。这个定位待修改记录在B+树中位置的过程看成是一个获取X锁 的 锁定读 ,新插入的记录由 INSERT 操作提供的 隐式锁 进行保护。
③情况3:修改了该记录的键值,则相当于在原记录上做 DELETE 操作之后再来一次 INSERT操作,加锁操作就需要按照 DELETE 和 INSERT的规则进行了。
INSERT :
一般情况下,新插入一条记录的操作并不加锁,通过一种称之为 隐式锁 的结构来保护这条新插入的记录在本事务提交前不被别的事务访问。

2.2、从数据操作的粒度划分:表级锁、页级锁、行锁

① 表级别的S锁、X锁
某个表执行一些诸如 ALTER TABLE、 DROP TABLE这类的 DDL 语句,其他事务对这个表并发执行诸如SELECT、INSERT、DELETE、UPDATE的语句会发生阻塞。
②意向锁
如果我们给某一行数据加上了排它锁,数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了。
如果事务想要获得数据表中某些记录的共享锁,就需要在数据表上 添加意向共享锁。
如果事务想要获得数据表中某些记录的排他锁,就需要在数据表上添加意向排他锁。
③自增锁
AUTO-INC锁是当向使用含有AUTO_INCREMENT列的表中插入数据时需要获取的一种特殊的表级锁
事务在持有AUTO-INC锁的过程中,其他事务的插入语句都要被阻塞。
当我们向一个有AUTO_INCREMENT关键字的主键插入值的时候,每条语句都要对这个表锁进行竞争
④元数据锁

2.3、从数据操作的粒度划分:行锁

InnoDB中的行锁:
MySQL 服务器层并没有实现行锁机制,行级锁只在存储引擎层实现。
优点:锁定力度小,发生 锁冲突概率低,可以实现的 并发度高。
缺点:对于 锁的开销比较大,加锁会比较慢,容易出现 死锁 情况。
① 记录锁(Record Locks):记录锁也就是仅仅把一条记录锁上,官方的类型名称为:LOCK_REC_NOT_GAP 。
② 间隙锁(Gap Locks):图中id值为8的记录加了gap锁,意味着 不允许别的事务在id值为8的记录前边的间隙插入新记录。gap锁的提出仅仅是为了防止插入幻影记录而提出的。
③ 临键锁(Next-Key Locks):有时候我们既想 锁住某条记录 ,又想 阻止 其他事务在该记录前边的 间隙插入新记录,所以InnoDB就提出了一种称之为 Next-Key Locks的锁,官方的类型名称为: LOCK_ORDINARY。
④ 插入意向锁(Insert Intention Locks):插入意向锁是在插入一条记录行前,由 INSERT 操作产生的一种间隙锁 。事实上插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁。

2.3、从数据操作的粒度划分:页锁

页锁的开销介于表锁和行锁之间,会出现死锁。锁定粒度介于表锁和行锁之间,并发度一般。

2.4、从对待锁的态度划分:悲观锁

共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。

2.5、从对待锁的态度划分:乐观锁

不采用数据库自身的锁机制,而是通过程序来实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
1.乐观锁的版本号机制
2.乐观锁的时间戳机制
乐观锁 适合 读操作多 的场景,悲观锁 适合 写操作多 的场景。

2.6、其它锁之:死锁

两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会释放自己的锁。
在这里插入图片d描述
如何解决?
方式1:关闭死锁检测,但意味着可能会出现大量的超时,会导致业务有损。
方式2:控制并发访问的数量。比如在中间件中实现对于相同行的更新,在进入引擎之前排队,这样在InnoDB内部就不会有大量的死锁检测工作。
如何避免死锁?
·合理设计索引,使业务 SOL尽可能通过索引定位更少的行,减少锁竞争。
.调整业务逻辑 SQL执行顺序,避免 update/delete 长时间持有锁的 SQL 在事务前面。
避免大事务,尽量将大事务拆成多个小事务来处理,小事务缩短锁定资源的时间,发生锁冲突的几率也更
小。
.在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。如select…for update 语句,如果是在事务里运行了 start transaction 或设置了autocommit 等于0,那么就会锁定所查找到的记录。
.降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值