mysql中的锁

126 阅读7分钟

事务并发问题解决方案

  1. 读操作MVCC,写操作进行加锁
  2. 读、写操作都采用加锁的方式

锁定读

读取当前版本,读取数据加锁,阻塞其他的事务同时修改相同记录,避免安全问题。

对读取的记录加S锁

select ...lock in share mode;

select...for share(8.0新增语法)

对读取的记录加X锁

selec...for update;

如果获取不到锁,会一直等待,知道innodb_lock_wait_timeout超时。在8.0版本中可以添加nowait、skip Locked语法,跳过锁等待,或者跳过锁定。

共享锁

多个事务可以共同持有同一把锁,即共享锁。s锁和s锁时兼容的

独占锁

独占锁时排他的,x锁和x锁是不兼容的。x锁和s锁同样不兼容

增删改查分析

select:读操作 delete:获取要删除数据X锁,delete记录。定位删除记录的B+数的过程,可以看作获取锁定都。 insert:不需要加锁 update;三种情况

  1. 未修改该记录的键值,并且被更新的列占用的存储空间在修改前后未发生变化。则先在B+树中定位到这条记录的位置,然后再获取一下记录的X锁,最后再原记录的位置进行修改操作。我们也可以把这个定位待修改记录再B+树中的位置的过程看成是一个获取X锁的锁定读。
  2. 未修改改记录的键值,并且至少有一个被更新的列占用的存储空间再修改前后发生变化。则现在B+树中定位到这条记录的位置,这个定位待修改记录再B+树中位置的过程看成是一个获取X锁的锁定读,新插入的记录由insert操作提供的隐式锁进行保护。
  3. 修改了该记录的键值,则相当于再原记录上做delete操作之后再来一次insert操作,加锁操作就需要按照delete和insert的规则进行了

行锁和表锁

image.png

意向锁

判断当前表中有没有锁。意向锁没有行锁

image.png

表级锁、行锁和页级锁

表锁

该锁会锁定整张表,他是mysql中最基本的锁策略,并不依赖于存储引擎,并且表锁是开销最小的策略。由于表级锁一次会将整个表锁定。所以可以很好的避免死锁问题,但会导致并发效率低。

表级锁的S锁和X锁

在对某个表执行select、insert、delete和update语句时,innoDB存储引擎时不会未这个表添加表级的S锁或者X锁的,在对某个表执行一些诸如alter table/drop table这类DDL语句时,其他事务对这个表并发执行诸如select、insert、delete和update的语句会发生阻塞。同理某个事务对某个表执行select、insert、delete和update语句时,在其他会话中对这个表执行DDL语句也会发生阻塞,这个过程其实时通过在server层使用一种称之为元数据锁(MDL)结构实现的.

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

lock table t read:InnoDB存储引擎会对表t加表级别的S锁

lock table t write:InnoDB存储引擎会对t加表级别的X锁。

image.png

意向锁

意向锁允许行级锁和表级锁共存。

  1. 意向锁的存在时为了协调行级锁和表级锁的关系,支持多粒度的锁并存;
  2. 意向锁时一种不与行级锁冲突表级锁。
  3. 表明"某个事务正在某些行持有了锁或该事务准备取持有锁"

如果我们给某一行数据加上了排他锁,数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据也或数据表已经有人上过排他锁了

  1. 如果事务想获得数据表中某些记录的共享锁,就需要在数据表上添加意向共享锁
  2. 如果事务想要获得数据表中某些记录的排他锁,就需要在数据表上添加意向排他锁

都是InnoDB自动添加的

意向共享锁

事务有意向对表中的某些行加共享锁

select column from table ... lock in share mode;

意向排他锁

事务有意向对表中的某些行加排他锁

select column from table ... for update;

自增锁

AUTO-INC锁是当向使用含有AUTO-INCRMENT列的表插入数据时需要获取的一种特殊的表级锁,在执行插入语句时就在表级别加一个AUTO-INC锁,然后未每条待插入记录的AUTO-INCRMENT修饰的列分配递增的值,在该语句执行结束后,再把AUTO-INC锁释放调。一个事务在持有AUTO-INC锁的过程中,其他事务的插入语句都要被阻塞,可以保证一个语句中分配的递增值时连续的,也正因为此,其并发性显然不高,当我们向一个有AUTO-INCRMENT关键字的主键插入值的时候,每条语句都要对这个表锁进行竞争,这样的并发潜力很低下,InnoDB通过innodb_autoinc_lock_mode的不同取值来提供不同的锁定机制,来提高性能

innodb_autoinc_lock_mode=0(传统锁定模式)机制如上所述,相互竞争表锁。

innodb_autoinc_lock_mode=1(连续锁定模式)

innodb_autoinc_lock_mode=2(交错锁定模式)

不使用AUTO-INC锁,可以同时执行多个语局

元数据锁

当对一个表做增删改查操作的时候加MDL读锁,当要对表做结构变更操作的时候,加MDL写锁

行锁

行锁会出现死 锁。

记录锁

把一条记录锁上

间隙锁

防止插入数据,避免幻读

临键锁

兼具记录锁和间隙锁功能

插入意向锁

页锁

悲观锁

乐观锁

版本号机制

在表中设计一个版本字段version,第一次读的时候,会获取version字段的值。然后对数据进行更新或删除操作时,会执行update ... set version = version+1 where version = version。此时如果已经有事务对这个条数据进行了更改,修改不会成功。

时间戳机制

当前时间戳和之前时间戳不一致侧表示修改了。

死锁

两个或两个以上进程竞争资源,阻塞的现象。

锁的内存结构

image.png

  1. 锁所在的事务信息:不论是表锁还是行锁,都是在事务执行过程中生成的,哪个事务生成了这个锁结构,这里就记录这个事务的信息,此锁所在的事务信息在内存结构中只是一个指针,通过指针可以找到内存中关于该事务的更多信息,比方说事务id等。

  2. 索引信息:对于行锁来说,需要记录一下加锁的记录是属于哪个索引的,也是一个指针。

  3. 表锁/行锁信息:

  4. 表锁:记载着是哪个表加的锁,还有其他的一些信息

  5. 行锁:记录所在表空间,记录所在页号,一条记录对应着一个比特位,一个页面中包含很多记录,用不同的比特位来区分到底是那一条记录加了锁,为此在行锁结构的末尾放置了一堆比特位,这个n_bits属性代表使用了多少比特位。

  6. type_mode