MYSQL(03)-锁

作者: 小亮__ | 来源:发表于2019-07-20 12:34 被阅读22次

MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类

全局锁

全局锁就是对整个数据库实例加锁,mysql提供了命令FTWRL(Flush tables with read lock)来开启全局锁,当开启全局锁的时候,整个数据库只能够进行读操作,数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等),和更新类事务的语句都不能提交

应用场景:
  • 全局锁一般应用在数据库备份的时候,这样只有读操作可以进行
疑问
  • 为什么使用(set global readonly=true),来设置数据库只读,他和全局锁有什么区别,因为如果使用FTWRL在客户端链接出现错误的时候,全局锁会自动释放,而(set global readonly=true)不会释放,导致只读时间长
  • 为什么不使用mysqldump参数single-transation,在导出数据之前开启事务,通过MVCC机制保证读取的时候一致性?因为Mysql不是所有的存储引擎都支持事务,例如MYISAM

表级锁

表级锁分两类,表锁和元数据锁(MDL-meta data-lock)

  • 表锁:可以使用lock tableName ...read/write 进行上锁,客户端断开链接的时候自动释放,也可以通过 unlock tables 手动释放
  • MDL:不需要显示使用,访问表的时候会自动带上,主要是DDL语句和DML查询更新语句之间进行加锁,保证表结构更新时,DML等语句堵塞
应用场景
  • 在DDL更新表的时候,因为需要便利所有的表数据,所以一般我们执行的时候都比较小心,或者干脆交给DBA来执行,但是在DDL小表的时候,我们可能会掉以轻心,如果DDL的表比较小,但是访问量很大,那么使用不当一样会出现问题,在DDL的时候,会增加MDL锁这时,所有的查询请求都会堵塞,如果客户端有重试机制,那么请求超时马上会在创建一个session,这时会很快占用满数据库的连接,这时应该给DDL语句增加一个超时时间,如果执行的时间比较长则直接超时断开,不要影响后面的请求

行级锁

MYSQL的行锁是引擎实现的,就是对数据的行进行加锁,MYISAM不支持行锁,这也是为什么MyIsam会被Innodb替代的一个主要原因。行锁在 InnoDB 中是基于索引实现的,所以一旦某个加锁操作没有使用索引,那么该锁就会退化为表锁。下面的介绍都是基于Innodb引擎来实现的

行锁(Record Locks)

行锁也叫记录锁,就是为某行记录加锁,它封锁该行的索引记录。(SELECT * FROM table WHERE id = 1 FOR UPDATE)时,id 为 1 的记录行会被锁住。需要注意的是:id 列必须为唯一索引列或主键列,否则上述语句,中就会从行锁退化成间隙锁。

间隙锁(Gap Locks)

间隙锁基于非唯一索引,它锁定一段范围内的索引记录。当更新语句更新一定范围的数值的时候,例如update user set del_flag = 1 where age > 100 (逻辑删除年龄大于100岁的人),这时如果事务的隔离级别设置为\color{red}{可重复读}的时候,则会增加间隙锁,其他DDL语句无法执行,当然如果事务的隔离级别如果是读提交的话,则可以成功。
详细原理:https://www.jianshu.com/writer#/notebooks/38437209/notes/51011877/preview

二阶段锁:

二阶段锁就是只锁操作分为两个阶段:加锁阶段与解锁阶段,innodb数据库的行锁的生成时机是在需要的时候增加锁,例如执行(update)更新语句执行的时候,而锁的释放是在commit的时候。举个案例,用户A消费买电影票,并记录消费日志。这时用户B也买电影表。这时用户A:

  • 1.用户A金额表数值减少
  • 2.影院金额表增加
  • 3.消费日志表增加记录
    用户B的执行操作
  • 1.用户B金额表数值减少
  • 2.影院金额表增加
  • 3.消费日志表增加记录

他们都会对影院金额表的同一行进行操作,这时,把影院金额表的更新操作放在最后执行(1,3,2)这样的顺序执行,影院金额这行的行锁就锁的时间最少,语句的执行就会更高。总结一下就是:如果一个事务中需要锁多个行,那么尽量把最可能造成锁冲突,最可能影响并发度的锁放的申请时机放在最后

死锁和死锁检测

行解锁,当一个事务中,已经获取到了行A的锁,去获取行B的锁,而另一个事务中,已经获取到了行B的锁,等待获取行A的锁。这时就会造成死锁,如下图。解决死锁的方式有两种一种是增加锁的超时时间,另一种是增加死锁检测

死锁的超时时间

死锁的超时时间可以通过innodb_lock_wait_timeout来设置的,默认是50S。但是如果并发更新量很大,那么这么长的等待时间业务肯定是不能接受的,而如果设置的时间过短,又会影响正常的锁等待。所以对于死锁的操作还有如下的解决方案

死锁检测

死锁检测可以通过innodb_deadlock_detect=on来设置,默认是开启的。他是通过每次开启事务,都检测一下,开启的事务是否是被别的线程等待,如果出现循环等待也就是死锁的时候,直接将其中一个事务进行回滚,释放持有的一个锁。但是这种操作会占用大量的CPU资源,所以如果这种情况很多会造成数据库的CUP使用率很高,但是却没有执行多少个事务。我们可以通过以下方式解决

  • 将操作通过中间件(redis分布式锁)。进行顺序操作,从而减少并发事务
  • 将多个事务操作进行业务上的合并,例如影院金额行增加金额,可以在业务中对5分钟之内的数据取和然后操作数据,从而减少锁冲突的概率

相关文章

  • MYSQL(03)-锁

    MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类 全局锁 全局锁就是对整个数据库实例加锁,mysql提供了...

  • Mysql的锁

    MySql锁的分类 Mysql里的锁大致可以分为全局锁、表级锁和行锁三类。 全局锁 Mysql 增加全局锁的方法:...

  • MySQL二进制日志

    MySQL-day10 MySQL存储引擎-锁 1)什么是“锁”? 2)“锁”的作用是什么? 3)MySQL中的锁...

  • MySQL的锁机制

    mysql的锁机制 1、MySQL锁的基本介绍 MyISAM:MySQL的表级锁有两种模式:表共享读锁(Table...

  • MS汇总

    数据库相关[MS-关于锁(乐观锁,悲观锁,行锁、表锁,共享锁,排他锁)Mysql索引优化Mysql查询优化Mysq...

  • Mysql 之 锁表与解表

    Mysql 之 锁表与解表 Mysql 查看锁表语句 mysql>show open tables where i...

  • rails中乐观锁和悲观锁的使用

    MySQL乐观锁和悲观锁的介绍可以参考之前的一篇文章MySQL中的锁(行锁,表锁,乐观锁,悲观锁,共享锁,排他锁)...

  • 秒杀随笔

    方法: mysql悲观锁 mysql乐观锁 PHP+redis分布式锁 PHP+redis乐观锁(redis wa...

  • 共享 + 排他锁

    mysql锁机制分为表级锁和行级锁 ,mysql中行级锁中的共享锁与排他锁进行分享交流。 测试语法 begin; ...

  • (4)头条mysql

    1、MySQL有哪些锁,乐观锁和悲观锁实现 如果避免、减少锁等待、团队中如何监控MySQL的锁等待的情况 锁监控:...

网友评论

    本文标题:MYSQL(03)-锁

    本文链接:https://www.haomeiwen.com/subject/lbuylctx.html