MySQL的行锁、表锁、间隙锁详解(下)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: MySQL的行锁、表锁、间隙锁详解(下)

4.7 when 使用表锁

对于InnoDB,在绝大部分情况下都应该使用行锁

因为事务,行锁往往是我们选择InnoDB的理由

但在个别特殊事务中,也可以考虑使用表锁


事务需要更新大部分数据,表又较大

若使用默认的行锁,不仅该事务执行效率低(因为需要对较多行加锁,加锁是需要耗时的);

而且可能造成其他事务长时间锁等待和锁冲突;

这种情况下可以考虑使用表锁来提高该事务的执行速度

事务涉及多个表,较复杂,很可能引起死锁,造成大量事务回滚

这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销

当然,应用中这两种事务不能太多,否则,就应该考虑使用MyISAM


在InnoDB下 ,使用表锁要注意


使用LOCK TALBES虽然可以给InnoDB加表锁

表锁不是由InnoDB引擎层管理的,而是由其上一层MySQL Server负责;

仅当autocommit=0、innodb_table_lock=1(默认设置),InnoDB 引擎层才知道MySQL加的表锁,MySQL Server才能感知InnoDB加的行锁;

这种情况下,InnoDB才能自动识别涉及表锁的死锁

否则,InnoDB将无法自动检测并处理这种死锁

在用LOCK TALBES对InnoDB锁时要注意,要将autocommit设为0,否则MySQL不会给表加锁

事务结束前,不要用UNLOCK TALBES释放表锁,因为它会隐式地提交事务

COMMIT或ROLLBACK不能释放用LOCK TALBES加的表锁,必须用UNLOCK TABLES释放表锁,正确的方式见如下语句

需要写表t1并从表t读

image.png

5 死锁

MyISAM表锁是deadlock free的,这是因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁

但在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了InnoDB发生死锁是可能的


发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并退回,另一个事务获得锁,继续完成事务


但在涉及外部锁,或涉及锁的情况下,InnoDB并不能完全自动检测到死锁

这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决

需要说明的是,这个参数并不是只用来解决死锁问题,在并发访问比较高的情况下,如果大量事务因无法立即获取所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖垮数据库

我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。

通常来说,死锁都是应用设计的问题,通过调整业务流程、数据库对象设计、事务大小、以及访问数据库的SQL语句,绝大部分都可以避免


下面就通过实例来介绍几种死锁的常用方法。


应用中,不同的程序会并发存取多个表

尽量约定以相同的顺序访问表

程序批处理数据时

事先对数据排序,保证每个线程按固定的顺序来处理记录

在事务中,要更新记录

应直接申请排他锁,而不应该先申请共享锁

在可重复读下,如果两个线程同时对相同条件记录用SELECT...ROR UPDATE加排他写锁

在没有符合该记录情况下,两个线程都会加锁成功

程序发现记录尚不存在,就试图插入一条新记录,如果两个线程都这么做,就会出现死锁

这种情况下,将隔离级别改成READ COMMITTED,就可以避免问题

当隔离级别为READ COMMITED时,如果两个线程都先执行SELECT...FOR UPDATE

判断是否存在符合条件的记录,没有 -> 插入记录;

此时,只有一个线程能插入成功,另一个线程会出现锁等待.

当第1个线程提交后,第2个线程会因主键重出错,但虽然这个线程出错了,却会获得一个排他锁!这时如果有第3个线程又来申请排他锁,也会出现死锁.

对于这种情况,可以直接做插入操作,然后再捕获主键重异常,或者在遇到主键重错误时,总是执行ROLLBACK释放获得的排他锁

如果出现死锁,可以用SHOW INNODB STATUS命令来确定最后一个死锁产生的原因和改进措施。

6 总结

6.1 MyISAM的表锁

共享读锁之间是兼容的,但共享读锁和排他写锁之间,以及排他写锁之间互斥,即读写串行

在一定条件下,MyISAM允许查询/插入并发,可利用这一点来解决应用中对同一表查询/插入的锁争用问题

MyISAM默认的锁调度机制是写优先,这并不一定适合所有应用,用户可以通过设置LOW_PRIPORITY_UPDATES参数或在INSERT、UPDATE、DELETE语句中指定LOW_PRIORITY选项来调节读写锁的争用

由于表锁的锁定粒度大,读写又是串行的,因此如果更新操作较多,MyISAM表可能会出现严重的锁等待,可以考虑采用InnoDB表来减少锁冲突

6.2 对于InnoDB表

  • 行锁基于索引实现
    如果不通过索引访问数据,InnoDB会使用表锁
  • 间隙锁机制及使用间隙锁的原因
  • 不同的隔离级别下,InnoDB的锁机制和一致性读策略不同
  • MySQL的恢复和复制对InnoDB锁机制和一致性读策略也有较大影响
  • 锁冲突甚至死锁很难完全避免

7 索引与锁

在了解InnoDB的锁特性后,用户可以通过设计和SQL调整等措施减少锁冲突和死锁

  • 尽量使用较低的隔离级别
  • 精心设计索引,并尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会。

利用索引优化锁


索引可以减少锁定的行数

索引可以加快处理速度,同时也加快了锁的释放

选择合理的事务大小,小事务发生锁冲突的几率也更小

给记录集显式加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁。

不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大减少死锁的机会。

尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响。

不要申请超过实际需要的锁级别;除非必须,查询时不要显示加锁。

对于一些特定的事务,可以使用表锁来提高处理速度或减少死锁的可能

索引的维护和优化

删除重复和冗余的索引

primary key(id) ,unique key (id) ,index(id)

主键索引、唯一索引、单列索引

  • 注意加粗的联合索引
    Index(a),index(a,b)
    primary key(id),index(a,id)
  • 删除重复和冗余的索引
  • image.png
  • 查找未被使用过的索引
  • image.png
  • 更新索引统计信息及减少索引碎片

analyze table table_ name

optimize table table_ name

使用不当会导致锁表


参考

https://www.cnblogs.com/chenqionghe/p/4845693.html

https://developer.aliyun.com/article/3780


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1天前
|
存储 算法 关系型数据库
MySQL事务与锁,看这一篇就够了!
MySQL事务与锁,看这一篇就够了!
|
6天前
|
存储 关系型数据库 MySQL
MySQL的锁机制
MySQL的锁机制主要用于管理并发事务对数据的一致性和完整性的访问控制
23 4
|
11天前
|
存储 SQL 关系型数据库
|
15天前
|
关系型数据库 MySQL 数据库
MySQL锁解密:读锁与写锁
【4月更文挑战第20天】
24 1
|
15天前
|
关系型数据库 MySQL 数据库
|
15天前
|
算法 关系型数据库 MySQL
|
15天前
|
SQL 关系型数据库 MySQL
MySQL锁:解析隐式锁与显式锁
【4月更文挑战第20天】
36 0
|
15天前
|
关系型数据库 MySQL 数据库
|
15天前
|
SQL 关系型数据库 MySQL
MySQL 锁
MySQL 锁
17 1
|
18天前
|
关系型数据库 MySQL 中间件
【MySQL实战笔记】07 | 行锁功过:怎么减少行锁对性能的影响?-02 死锁和死锁检测
【4月更文挑战第19天】在高并发环境下,死锁发生在多个线程间循环等待资源时,导致无限期等待。MySQL中,死锁可通过`innodb_lock_wait_timeout`参数设置超时或`innodb_deadlock_detect`开启死锁检测来解决。默认的50s超时可能不适用于在线服务,而频繁检测会消耗大量CPU。应对热点行更新引发的性能问题,可以暂时关闭死锁检测(风险是产生大量超时),控制并发度,或通过分散记录减少锁冲突,例如将数据分拆到多行以降低死锁概率。
24 1