Mysql锁

文章目录

  • 1. MySQL并发事务访问相同记录
  • 2. 锁的分类
    • 2.1 共享锁和排他锁
    • 2.2 表锁、行锁、页锁
      • 2.2.1 表锁(Table Lock)
      • 2.2.2 行锁
    • 2.3 乐观锁、悲观锁

事务的隔离性由锁来实现

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

读-读: ok
写-写: 脏写问题,一个事务已经commit了但是发现没写进去。—>排队执行is_waitingMysql锁_第1张图片

读-写、写-读问题:这种情况下可能发生 脏读不可重复读幻读 的问题。

并发问题的解决方案:

  • 方案一:操作利用多版本并发控制( MVCC),操作进行 加锁
    普通的SELECT语句在READ COMMITTEDREPEATABLE READ隔离级别下会使用到MVCC读取记录。
    在 READ COMMITTED 隔离级别下,一个事务在执行过程中每次执行SELECT操作时都会生成一个ReadView,ReadView的存在本身就保证了 事务不可以读取到未提交的事务所做的更改 ,也就 是避免了脏读现象;
    REPEATABLE READ 隔离级别下,一个事务在执行过程中只有 第一次执行SELECT操作 才会生成一个ReadView,之后的SELECT操作都 复用 这个ReadView,这样也就避免了不可重复读 和幻读的问题

  • 方案二:读、写操作都采用加锁的方式
    问题:可以解决脏读、不可重复读,幻读的话—不知道

2. 锁的分类

2.1 共享锁和排他锁

共享锁:S锁。读操作可以同时进行而不会互相影响,相互不阻塞的。SELECT … FOR share
排他锁:X锁。当前写操作没有完成前,它会阻断其他写锁和读锁。SELECT … FOR UPDATE

对于InnoDB引擎来说,读锁和写锁可以加在表上,也可以加在行上。


写操作:

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

2.2 表锁、行锁、页锁

2.2.1 表锁(Table Lock)

会锁定整张表,是开销最小的策略(因为粒度比较大)。可以很好的避免死锁问题。并发性能差。
MyIsam默认是表锁,InnoDB默认是行锁

  1. 表级 S、X锁在这里插入图片描述

  2. 意向锁 :它允许 行级锁 与 表级锁 共存,即意向锁和行级锁兼容(我认为是一种行级锁标志

    • 意向锁是一种不与行级锁冲突表级锁,这一点非常重要
    • 表明“某个事务正在某些行持有了锁或该事务准备去持有锁”

如果我们给某一行加上了排它锁,InnoDB会向表级别加上意向锁,告诉其他人这个表已经有人上过排它锁了,这样当其他人想要获取表级别锁的时候,只需要了解是否有人已经获取了这个表的意向排他锁即可。原来需要一条记录一条查,看看行有没有X锁,现在不用了,在表的意向排他锁看一下就ok

口述:意向锁其实就像一种行级锁的标志,当我们要加表锁时,如果表中有行的X锁的话,那其实是不能让这个事务获得表锁的,这样的话如果没有意向锁的话就要去一条一条记录去查看看有没有X锁,这样的话就性能低,意向锁就是在表级别上的这样一个锁,它描述的就是表中记录的X锁啊S锁啊这种,其实更类似于一种行锁的标记吧,也是为了快速判断能不能加表锁。

Mysql锁_第2张图片

  1. 自增锁(AUTO-INC锁):id字段声明了AUTO_INCREMENT,意味着在书写插入语句时不需要为其赋值
  2. 元数据锁(MDL锁):为了保证DDL与DML语句的正确性的,比如在DML时候,会加MDL读锁,在DDL时,会加MDL写锁,读写是互斥的,保证了数据正确性。比如:先查出数据,其次对表结构更改了,然后再查出数据,这肯定是不可以的,表结构都改了查出来也肯定不一样。

2.2.2 行锁

  1. 记录锁:S锁和X锁

  2. 间隙锁:以锁的方式解决了幻读,事务在第一次执行读取操作时,那些幻影记录尚不存在,我们无法给这些 幻影记录 加上 记录锁 ,间隙锁可以。

  3. 临键锁:innodb默认的锁就是临键锁,它其实是记录锁和间隙锁的一种结合】Mysql锁_第3张图片
    在这里插入图片描述
    在3-8之间加间隙锁,id为8的记录加记录X锁。

  4. 插入意向锁:插入意向锁是一种 特殊的Gap锁 ,在insert操作时产生。
    插入意向锁其实是在插入时,被间隙锁阻塞,这时候事务会持有这个插入意向锁,锁结构里面is_waiting为true,等待持有间隙锁的事务释放了锁才能insert。

2.3 乐观锁、悲观锁

  • 乐观锁:update version字段,条件是version
  • 悲观锁:会通过数据库自身的锁机制来实现,从而保证数据操作的排它性。Java中 synchronized 和 ReentrantLock 等独占锁就是悲观锁思想的实现。
    select … for update语句执行过程中所有扫描的行都会被锁上,因此在MySQL中用悲观锁必须确定使用了索引,而不是全表扫描,否则将会把整个表锁住

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