Mysql中的锁分类:
数据库使用锁是为了支持对共享资源的并发访问,同时保证了数据的完整性和一致性,
其中mysq在sserver层和innoDB引擎设计了多种类型的锁机制,主要用于实现不同场景下的并发控制,
锁的分类:
1,按照锁的作用范围进行划分:
1,全局锁(FTWRL)Flush tables with read lock
2.表级锁:
1,元数据锁MDL(meta data lock)
2,表锁:
3,意向锁:
4,AUTO-INC Locks (自增列锁)
3,行级锁:
1,record locks(记录锁)
2,Gap locks(间隙锁)
3,Next-key lock(锁行记录+间隙锁)
4,insert intention locks
2,按照权限划分:
1,共享锁:
1,意向共享锁IS
2.表共享锁
3,行共享锁
2,排它锁:
1,意向排它锁IX
2.表排他锁
3,行排它锁
悲观锁,乐观锁.
全局锁:
全局锁就是对整个数据库加锁,目的就是为了保证数据读取时的一致性,加锁的全局命令: flush tales with read Lock
,加上全局锁之后,整个库就是只读状态了,其他线程对表的任何更新修改操作都会被阻塞掉.
典型的使用场景就是全库的逻辑备份,在备份过程中就是使用的全局锁.但这种会对数据库上有很大的影响,所以可以有其他的一个办法
就是采用事务的可重复读的隔离级别下,来对数据一致性进行保持.这就是一种快照读,来避免对整个数据库的影响.
还有就是不建议的方式,就是设置只读方式:set global readonly=true.
Mysql为了解决表并发,数据安全的等问题,按照锁的粒度把数据库锁分为表级锁和行级锁。
表级锁:
Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单 ,资源消耗也比较少,加锁快,不会出现死锁 。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
命令: lock tables ...read/write 加锁 \\ 释放锁: unlock tables ,客户端断开连接时也会自动释放.
但表锁造成的影响较大,一般只有对整个表的结构进行修改的时候,才会使用该种方式,但也可以通过 online DDL方式,实现了可以不锁整个表进行数据的修改.提高了数据并发的能力.
元数据锁MDL(meta data Lock):
MDL就是指的是对表的数据结构进行变更的操作,mdl是为了在一个表在做增删改查的时候,防止因为表结构改变导致查询结果异常,
在5之后加入了mdl,当一个表做增删改的操作的时候,加上mdl读锁,当改变结构的时候加上mdl写锁.
不需要显示指定,在对应的访问时会自动加上,读之间不会发生互斥.写锁互斥/
在这种机制下,会不会存在一些问题?虽然MDL系统会默认加锁,但是不免还是会在使用的时候出现一些问题。例:线程A启动后,开启了一个事务,在事务中访问表T,给表T加了MDL读锁,此时线程B启动后需要更改表结构,给表T加写锁,但是由于线程A的读锁还未释放(事务提交后才会释放MDL锁),所以线程B会阻塞,而申请MDL锁的操作形成的等待队列中,写锁的优先级高于读锁,所以只要有写锁等待,那么后续所有的MDL加锁操作都会被阻塞(详情参考mysql MDL读写锁阻塞,以及online ddl造成的“插队”现象_花落的速度的博客-CSDN博客_mdl读写),也就是说现在这个表已经不能读写了。
解决方式: 1.考虑申请写锁前关闭掉长事务,因为长事务会一直占用着锁,2,就是利用超时机制,设置锁的等待时间.
行级锁: 是mysql中锁粒度最小的,大大减少了数据库操作的冲突,加锁的粒度最下,并发度最高,但加锁的开销比较大,加锁慢,会出现死锁,innoDB支持的行级锁,支持如下算法:
意向锁: 简单理解就是有加锁的意向,意向是用来很好的解决表锁和行锁之间的冲突和共存.
意向锁是表级锁,但是却表示事物正在读或写某一行记录,而不是这个表,主要是加上他之后,为了表在后续被加上x锁或者S锁,能快速的判断出表记录之前是否被加锁,从而避免了通过遍历的方式一个个个去检测行锁的存在.
意向锁主要分为:
1,意向共享锁IS: 当事务准备给表记录加上S锁的时候,需要先对表加上IS锁.
2,意向排它锁IX: 当事务准备给表记录加上X锁的时候,需要先对表加上IX锁
image.png
其中,IS锁和IX锁、IS锁和IS锁、IX锁和IX锁之间都是兼容的。这个如何理解呢?
这就意味着,不管有事务对表记录中加了S锁,还是加了X锁,只需要加上对应的IS锁和IX锁就好了,不需要关心其他事务加的是IS锁还是IX锁。
也就是说,IS锁和IX锁只是为了后续对表的家S锁或X锁才能起作用.
自增列锁(AUTO-INC Locks);
自增列锁是一种特殊的表级锁,当表使用了AUTO_INCREMNT列时,插入数据时需要获取AUTO-INC锁,
他的作用范围也是语句级别的.当一个插入语句执行完成后,哪怕整个事务还没结束,可以是通过auto-INC锁也会释放,因此
来保证自增值是连续的.这种方式下,可能会因为大量的插入请求导致后面的语句阻塞较多,导致整个表操作的效率下降,
解决方式: 可以采用一种轻量级的锁,在插入之前获取到该锁,然后修改加锁的判断的值,然后就释放掉.
行级锁:
1,record Locks(记录锁):
但需要注意,record locks锁的都是索引的记录,主要是作用于聚簇索引或者二级索引上的,即是一个表没有定义索引,innoDB也会创建一个隐藏的的聚簇索引并使用该索引进行记录锁定,所以,也被称为索引记录锁.
其中的记录锁也分为共享记录锁和排他记录锁,同样遵循读共享,读写互斥/
select * from user where user_id = 7 for update;
锁定了索引对应的数据行的记录,这个词句有保证user表上user_id=7的记录被锁定,在当事务没提交前,其他事务对该记录的当前读操作会被阻塞.
2,Gap Locks:(间隙锁)
这个锁的主要作用就是用来补充行锁的不足,作为记录锁的补充,主要为了解决幻读的问题,(幻读:指的是一个事务在前后两次查询相同范围内的数据返回的结果不一致)
间隙锁,所以就是对相邻行之间的间隙进行加锁,这样此时行之间的记录就会被插入新的行.所以就是用来限制在锁住几行的间隙中,也不允许此时的行间的插入操作,
间隙锁之间是不冲突的,两个事物可以同时添加间隙所,但可能会引起死锁.
3,Next-key Locks:
Next-key lock 即是记录锁和间隙锁的的整合版.他是锁住一个范围区间,这个区间内的行和行之间的间隙都是进行了上锁,并且这是添加的一个左开右闭的区间范围锁.
在rr隔离级别下能够很好的解决幻读的问题,但同时由于锁更大的范围,会影响一定的并发下效果.
mysql默认的RR隔离级别下,会出现幻读,但是很多线上的业务采用RC 级别,rc允许幻读,所以一般不采用间隙所方式.
4,insert intention locks(插入意向锁):
这个主要是用来在间隙所之后,添加一个插入意向锁,表示即将对这里进行数据插入操作,
然后就开始等待间隙所的释放,并且将意向所的is_waiting=false, 唤醒两个插入的事务,且这两个事务之间是不阻塞的.
注意: 插入意向锁是insert操作的时候,一种特殊的间隙锁,注意它并不属于意向锁而是属于间隙锁.
插入意向锁之间互不排斥,当多个事务在同一个区间插入记录时,只要记录本身(主键索引,唯一索引)不发生冲突,那么事务之间也不会发生阻塞等待.
乐观锁:
认为假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式的对数据的冲突进行检测,
1,通过version版本控制识别,就是给当前数据添加一个版本标识,如果比对版本一致了则进行修改并且会对version加1,如果不一致,则修改提交失败.
2.乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp),
和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
悲观锁(Pessimistic Lock):
当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【Pessimistic Concurrency Control,缩写“PCC”,又名“悲观锁”】。
悲观锁,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,
否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。