mysql之加锁

锁分类

全局锁

针对整个数据库实例加锁,加锁后数据库状态为只读。数据增删改,修改表,创建表等变更都会被阻塞。
命令:Flush tables with read lock (FTWRL)
使用场景:做全库逻辑备份
缺陷:在备份期间业务停止。
其他方式:官方自带的逻辑备份工具是 mysqldump。当 mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。(隐含条件:支持事务的引擎,可重复读的隔离级别)
不使用 set global readonly=true的缺点:
一:有些系统 readonly 的值会被用来做其他逻辑,比如用来判断一个库是主库还是备库。
二是:客户端发生异常断开情况下,FTWRL 会自动释放,readonly会一直保持当前状态。

表级锁

表锁

作用:针对某张表的读写锁定。
命令:lock tables … read/write。 主动释放或者链接断开释放。
限制:当前链接线程只能严格根据上锁的语句操作,如read A,write B,就只能 读A,写B。其他操作都不允许。写包含(读写) 读只包含(读),所以不能写A,只能读A。

元数据锁

作用:保证写的正确性。
MDL分为读写锁: 表中数据进行增删改查时会加MDL读锁, 对表结构修改变化时会加:MDL写锁。读锁之间不冲突,但是读与写,写于写之间互斥,当有MDL写锁等待时,会阻塞该表后面所有操作,整个表属于不可用状态。直至写锁的事务释放。
解决办法:加上获取写锁的超时时间,拿不到后尽快放弃获取写锁,让业务语句执行,后面通过重试这个过程来解决表结构修改问题。

行锁

针对数据表中的行数据进行锁定。获取行锁后 释放时机为:事务提交才会释放。
如果一个事务中需要修锁定多行数据,尽最大程度把锁争抢最强烈的行锁放在最后。因为行锁是在使用时才回去获取。

死锁和死锁检测

死锁:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会导致这几个线程都进入无限等待的状态,称为死锁。
案例
事务A 需要操作两条数据首先操作数据X,然后操作数据Z,
事务B同时也需要操作两条数据,首先操作数据Z,然后操作数据X。
当事务A 操作完数据X, 事务B操作完数据Z时, 事务A需要获取Z的行锁, 事务B需要获取X的行数进行操作,产生互相持有 互相等待释放。成为死锁。
解决方案

  • 超时策略:设置innodb_lock_wait_timeout (默认时间50S)
  • 设置innodb_deadlock_detect=no 开启死锁检测(死锁检测为:主动检测是否有死锁会发生,如有死锁则主动回滚某一事物,让其他事务继续执行)
    两种选择方案,因超时时间不可预估,且不友好故选择开启死锁检测。
    死锁检测
    死锁检测在并发度很高的情况下时间复杂度越高O(n) 的操作。因为并发度越高也就是并发线程越多,各个线程间互相检测,耗费大量CPU资源。
    1保证业务不会出现死锁,关掉死锁检测。(可能会有大量超时)
    2控制并发度,在Mysql中间件做控制,或者在mysql的server层做判断,对于同行的更新在进入引擎前排队。
    3从设计优化上解决。例如:将并发度高的某条数据分割成多份,当修改时随机修改一份,使用时获取多份的总额。(类似于分段汇总)
    死锁查看
    show engine innodb status
    LATESTDETECTED DEADLOCK记录最后一个死锁的信息。

备份时碰到DDL语句

加粗样式备库用–single-transaction 做逻辑备份,主库做了DDL的表结构修改,此时备份是个什么样的状态?
解析:做逻辑备份需要执行多个语句,设置级别 开启事务 设置保存点 获取表结构 等。
关键点:修改表结构需要获取MDL写锁,(写锁与写锁,读锁之间冲突)备份真正执行时需要获取读锁,所以判断备份当前状态主要看DDL语句来临时备份进度属于那个阶段。
答: 3种可能:
1备份正常执行,DDL正常执行。(说明DDL来临时,备份还没有获取读锁,准备阶段)
2备份正常执行,DDL阻塞。(DDL阻塞,说明备份已经获取读锁,DDL阻塞等待备份完成)
3备份停止,DDL执行。(备份准备阶段完成,真正执行数据查询前,DDL执行,导致准备阶段的表结构与数据查询获得字段有差别,事务停止,备份停止)

间隙锁

innodb引入间隙锁为了解决在RR级别下解决幻读的问题。
例如三条数据:id,a–(5,5) (7,7) (10,10)
假设id为主键,且为int。则三条数据的间隙就是:(-&,5)(5,7)(7,10)(10,&+)
加锁规则:
1加锁的基本单位是 next-key lock。next-key lock 是前开后闭区间。
2查找过程中访问到的对象才会加锁。
3索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
4索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
5唯一索引上的范围查询会访问到不满足条件的第一个值为止。(按照2规则,5.XX版本会加锁,8版本不会加锁)

范围查询规则:例如:id<8 ;
加锁步骤:首先找到id=8的数据,如果有根据8进行向左遍历操作直到最后一条数据(所有遍历过的数据都需要加锁)。如果没有则找到的是(7,10)的间隙,则将当前间隙锁加上(7,10),继续向左遍历依次加锁。

锁蔓延:当间隙锁所有在前后数据记录行被修改或删除时,
例如间隙锁(7,10)删除或修改了7(7修改为比7小或者比10大)这条数据向左 蔓延间隙锁变成(5,10)
例如间隙锁(7,10)删除或修改了10(10修改为比7小)这条数据向右 蔓延间隙锁变成(7,&+)

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