Files
rikako-note/mysql/mysql底层/mysql加锁.md
2023-01-10 21:59:13 +08:00

7.0 KiB
Raw Blame History

mysql锁

  • msyql中锁的分类

    • 从数据库操作的类型划分mysql中的锁可以分为读锁/共享锁和写锁/排他锁
      • 读锁S锁Share
      • 写锁X锁Exclusive
      • 写锁和读锁示例
      # 为语句加上写锁
      select * from table_name for udpate
      
      # 为查询语句加上读锁
      select * from table_name lock in share mode
      # or
      select * from table_name for share(mysql 8.0新增语法)
      
      • 在获取读锁或者写锁之后只有当事务结束之后获取到的读锁或者写锁才会被释放。在innodb中for update或者for share语句只会为查询匹配的行数据上锁并且当事务结束或者回滚之后会释放为数据行所加的排他锁或者共享锁
      • 在对mysql中数据进行写操作UPDATE、INSERT、DELETE)时,会通过如下方式加锁:
        • DELETE操作对mysql表中数据执行DELETE操作需要为要删除数据添加排他锁X锁
        • UPDATE操作对mysql表中数据执行UPDATE操作分如下情况
          • update操作不改变记录的键值且更新的列在更新前后占用的空间未发生变化
            • 此时会在B+数中定位到该条记录并且为该条记录添加X锁在修改完成之后释放X锁
          • update操作没有修改键值但是更新前后该条记录的某一列占用空间发生了变化
            • 此时会先对原先的记录加上X锁并进行DELETE操作并且在DELETE操作后通过INSERT操作并添加隐式锁来插入修改后的数据
          • update操作修改了键值
            • 同样也会通过先DELETE再INSERT的操作来更新数据
        • INSERT操作通过添加隐式锁的操作来保证mysql中多事务的并发安全
    • 从mysql数据库的粒度来对锁进行划分
      • 表锁:
        • 表级锁会锁住mysql中的整张表其粒度较大受其影响并发性能较低
        • 表级锁是mysql中最基本的加锁策略并不依赖于存储引擎不管是innodb还是myisam都支持表级锁
        • 表级锁的使用场景:
          • 通过的select或者update、delete、insert操作innodb并不会加表级锁但是对于alter table、drop table这类ddl语句对表的结构进行修改时需要对其表的元数据进行加锁MDL对元数据加锁的过程中想要对该表中数据进行select、delete、update、insert的操作会被阻塞
        • 对表锁进行加锁的语句:
        # 对表锁进行加锁
        # 在对表进行上锁操作后,无法再去读取其他未上锁的表
        lock tables table_name read/write
        
        # 对上锁的表进行释放操作
        unlock tables;
        
        • 对于myisam存储引擎读操作之前会为涉及到的表加上读锁并且在读操作执行完成之后释放上锁的表而对于写操作myisam存储引擎会在写操作执行之前为涉及到的表加上写锁。反之innodb存储引擎在对数据进行查找和修改时不会在表级别加锁而是会在更细的粒度上为行数据进行加锁以此来提高程序的并发性能。
        • 元数据锁MDL对于表结构的修改DDL操作会对其元数据锁进行加锁而对于该表的增删改操作则是会默认加上元数据的读锁。故而在对表结构进行修改时想要对表中数据进行増删改操作需要获取其元数据的读锁此时对表数据的増删改操作会被阻塞。
        • 意向锁意向锁通常用来简化行级锁和表级锁是否兼容的判断操作。如果为一个表中某条数据加上行级锁那么innodb存储引擎会自动的为该行记录所在的页和表加上页级和表级的意向锁那么当接下来有事务想要对该表进行表级加锁操作时就无需查看该表中所有数据来判断是否存在表中数据的锁和表级锁冲突。只需判断该表的意向锁和想要加上的表级锁是否冲突即可。
      • 行锁
        • 相对与表锁,行锁的粒度更小,故而其并发度更高,并发性能更好。但是行锁可能会造成死锁问题,加锁的开销也更大。
        • 是否支持行锁取决与存储引擎比如myisam不支持行锁但是innodb却支持行锁
        • 行锁种类:
          • 记录锁记录锁针对于单条记录在一个事物中如果存在update、delete等修改操作其默认会获得想要修改的那条记录的记录锁并且在执行修改操作之后才会被释放。若一个事务中对某条记录调用update操作那么直到当前事务提交或者回滚前若其他事务想要修改同一行记录会进入阻塞状态知道持有记录写锁的退出才能继续执行。
          • 间隙锁间隙锁对记录的范围进行加锁。间隙锁是不冲突的多个事务可以同时持有某一个范围的间隙锁。用间隙锁或者mvcc机制都能够解决死锁问题。但是间隙锁可能会导致死锁问题如果多个事务各自持有自己范围的间隙锁并同时向对方持有间隙锁的范围插入数据此时两个事务都会等待对方释放间隙锁在这种情况下会发生死锁问题。
          • 临键锁next-key lock在锁住某条记录的同时也锁定某个范围内的数据使之无法被其他事务插入数据
      • 页锁:在页面的粒度上进行上锁
      • 在粒度上不同层级的所的数量是有上限的例如innodb其优先会选用行锁来进行加锁。当行锁数量过多占用空间超过表空间上限时会进行锁升级的行为。行锁会升级为页面锁此时锁的粒度会增加锁花费的开销也会变小但是粒度变大后并发性能也会变低。
    • 通过对待锁的方式进行划分
      • 乐观锁:乐观锁假设每次对数据进行修改时,其他事务不会访问数据,故而不会对数据正真的加锁,只是会在修改时检查数据是否被其他事务修改过。
        • 乐观锁的实现方式:
          • cas
          • 版本号机制
      • 悲观锁:
        • 悲观锁假设一个事务在对数据进行修改时其他事务也会对数据进行修改故而在每次修改数据时都会对要修改的数据进行加锁悲观锁是通过mysql内部提供的锁机制来实现的
    • 通过加锁方式进行划分:
      • 隐式锁隐式锁通常用于插入操作。在某一个事务在对所记录进行插入操作时如果其他事务想要访问该条记录会查看最后修改该条记录的事务id是否是活跃的事务如果是活跃事务那么其会帮助插入事务创建一个X锁并且让自己进入阻塞状态。
        • 隐式锁是一种延迟加锁的机制,只有当有其他事务想要访问未提交事务插入的记录时,隐式锁才会被创建。该机制能够有效减少创建锁的数量。
      • 显示锁可以通过命令查看到的锁通常会用for update、lock in share mode 或者 update、delete操作会生成显示锁