MySQL的表锁

目录

共享锁与排它锁(读锁和写锁)

1、锁定读

2、写操作

1、表锁

表级别的读锁和写锁

 意向锁(IS、Ik)

自增锁(TUTO-INC锁)

元数据锁(MDL锁)


共享锁与排它锁(读锁和写锁)

读锁(共享锁):针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,互相不阻塞

写锁(排它锁):当前写操作没有完成前,他会阻断其他的写锁和读锁。这样就保证了在一定时间里,只有一个事务能执行写入,并防止其他用户读取正在写入的统一资源

对于innoDB来说,读锁可以加在表上,也可以加在行上。

(行级读写锁)如果一个事务T1已经获得了某行R的读锁,那么此时另一个的事务T2是可以去获得这个行R的读锁的,因为读取的操作并没有改变行而的数据;但是,如果某个事务T3想获得行R的写锁,他必须等T1,T2释放R上的读才可以。

1、锁定读

在采用加锁方式解决脏读、不可重复读、 幻读这些问题时,读取一条记录时需要获取该记录的S锁,其实是不严谨的,有时候需要在读取记录时就获取记录的X锁,来禁止别的事务读写该记录,为此MySQL提出 了两种比较特殊的SELECT语句格式:

对读取的记录加读锁

mysql在普通的SELECT语句后边加LOCK IN SHARE MODE ,如果当前事务执行了该语句,那么就会为读到的记录加读锁,这样允许别的事务继续获取这些记录的读锁(比方说别的事务也使用SELECT ... LOCK IN SHARE MODE语句来读取这些记录),但是不能获取这些记录的X锁(比如使用 SELECT ... FOR UPDATE 语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的X锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的S锁释放掉。

对读取记录加写锁

 在普通的SELECT语句后边加FOR UPDATE ,如果当前事务执行了该语句,那么它会为读取到的记录加写锁,这样既不允许别的事务获取这些记录的写锁(比方说别的事务使用SELECT LOCK IN SHARE MODE 语句来读取这些记录),也不允许获取这些记录的X锁(比如使用SELECT   FOR UPDATE 语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的S锁或者写锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的写锁释放掉。

MySQL8.0新特性:
在5.7及之前的版本,SELECT .. FOR UPDATE,如果获取不到锁,会一直等待,直到
innodb_ .lock_ wait_ timeout 超时。在8.0版本中,SELECT .. FOR UPDATE, SELECT ... FOR SHARE添加NOWAIT、SKIP LOCKED 语法,跳过锁等待,或者跳过锁定。


通过添加NOWAIT、SKIP LOCKED语法,能够立即返回。如果查询的行已经加锁: 
        那么NOWAIT会立即报错返回
        而SKIP LOCKED也会立即返回,只是返回的结果中不包含被锁定的行。

2、写操作

        平常所用到的写操作无非是DELETE、UPDATE 、INSERT 这三种:
●DELETE :
对一条记录做DELETE操作的过程其实是先在B+树中定位到这条记录的位置,然后获取这条记录的X锁,再执行delete mark 操作。我们也可以把这个定位待删除记录在B+树中位置的过程看成是一个获取X锁的锁定
●UPDATE :在对一条记录做UPDATE操作时分为三种情况:


情况1:未修改该记录的键值,并且被更新的列占用的存储空间在修改前后未发生变化。
则先在B+树中定位到这条记录的位置,然后再获取一下记录的 写锁,最后在原记录的位置进行修改操作。我们也可以把这个定位待修改记录在B+树中位置的过程看成是一个获取写锁的锁定读。


情况2:未修改该记录的键值,并且至少有一一个被更新的列占用的存储空间在修改前后发生变化。则先在B+树中定位到这条记录的位置,然后获取一 下记录的X锁,将该记录彻底删除掉(就是把记录彻底移入垃圾链表),最后再插入一条新记录。这个定位待修改记录在B+树中位置的过程看成是获取个获取X锁的锁定读,新插入的记录由INSERT操作提供的隐式锁进行保护。


情况3:修改了该记录的键值,则相当于在原记录上做DEL ETE操作之后再来一次INSERT操作,加锁操作就需要按照DELETE和INSERT的规则进行了。

●INSERT :
一般情况下,新插入-条记录的操作并不加锁,通过一种称之为 隐式锁的结构来保护这条新插入的记录在本事务提交前不被别的事务访问。
 

操作力度划分:表级锁、页级锁、行锁

为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据的方案会得到最大的并发度,但是管理锁是很耗资源的事情(涉及获取、检查、释放锁等动作)。因此数据库系统需要在高并发响应和系统性能两方面进行平衡,这样就产生了“锁粒度(Lock granularity) ”的概念。

1、表锁

不依赖于储存引擎(不管你是MySQL的什么存储引擎,对于表锁的策略都是一样的),开销小(因为力度大)。表锁不会产生死锁问题。但是表锁会影响并发率

表级别的读锁和写锁

在对某个表执行SELECT、INSERT、 DELETE、 UPDATE语句时,InnoDB存储引擎是不会为这个表添加表级别的读锁或者写锁的。在对某个表执行一些诸如ALTER TABLE、DROP TABLE 这类的DDL语句时,其他事务对这个表并发执行诸如SELECT、INSERT、 DELETE、 UPDATE的语句会发生阻塞。同理,某个事务中对某个表执行SELECT、INSERT、DELETE、 UPDATE语句时,在其他会话中对这个表执行DDL语句也会发生阻塞。这个过程其实是通过在server层使用一种称之为元数据锁(英文名: Metadata Locks ,简称MDL )结构来实现的。

一般情况下, 不会使用InnoDB存储引擎提供的表级别的读锁和写锁。只会在一些特殊情况下,比方说崩溃恢复过程中用到。比如,在系统变量autocommit=0,innodb_ table_ locks = 1 时,手动获取InnoDB存储弓|擎提供的表t的S锁或者X锁可以这么写:

不过尽量避免在使用InnoDB存储弓|擎的表上使用LOCK TABLES 这样的手动锁表语句,它们并不会提供什么额外的保护,只是会降低并发能力而已。InnoDB的厉害之处还是实现了更细粒度的行锁,关于InnoDB表级别的读锁和写锁大家了解一下就可以了。

总结:
MyISAM在执行查询语句(SELECT) 前,会给涉及的所有表加读锁,在执行增删改操作前,会给涉及的表加写锁。InnoDB存储弓|擎是不会为这个表添加表级别的读锁或者写锁的。MySQL的表级锁有两种模式: (以MyISAM表进行操作的演示)
 

 意向锁(IS、Ik)

innoDB支持多粒度锁,他允许行级锁和表锁共存

1、意向锁的存在是为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存。
2、意向锁是一种不与行级锁冲突表级锁,这一点非常重要。
3、表明“某个事务正在某些行持有了锁或该事务准备去持有锁”
意向锁分为两种:
●意向共享锁(intention shared lock, IS) :事务有意向对表中的某些行加共享锁(S锁)
●意向排他锁(intention exclusive lock, IX) :事务有意向对表中的某些行加排他锁(X锁)

 即:意向锁是由储存引擎自己维护的,用户无法手动操作意向锁,在为数据加共享/排它锁之前,InnDB会先获取数据行所在数据表的对应意向锁。

假如说我对这个表的数据上表锁,我首先要确定我有没有对其中一行上行级锁,这时候如果没有行级锁你就需要一条一条的判断了,而当你有行级锁时,会自动加意向锁,这时候加表锁的时候,就可以直接判断了。

从上面的案例可以得到如下结论:
1. InnoDB 支持多粒度锁,特定场景下,行级锁可以与表级锁共存。
2.意向锁之间互不排斥,但除了IS与S兼容外,意向锁会与共享锁/排他锁互斥。
3. IX, IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,s发生冲突。
4.意向锁在保证并发性的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。

自增锁(TUTO-INC锁)

了解即可

主要是针对有自增主键的表,当要插入新的主键时,会对这个表上锁,当一个事务持有锁时,其他的事务阻塞。

三种锁定模式传统模式,连续锁定模式,交错锁定模式
 

元数据锁(MDL锁)

 MySQL5.5引入了meta data lock,简称MDL锁,属于表锁范畴。MDL的作用是,保证读写的正确性。比如,如果一个查询正在遍历一个表中的数据, 而执行期间另-个线程对这个表结构做变更,增加了一列, 那么查询线程拿到的结果跟表结构对不上,肯定是不行的。因此,当对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL写锁。读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。读写锁之间、写锁之间是互斥的,用来保证变
更表结构操作的安全性,解决了DML和DDL操作之间的一致性问题。不需要 显式使用,在访问一一个表的时候会被自动加上。

你可能感兴趣的:(mysql,数据库,sql)