无论是Oracle、MySQL、PG,锁可能都是一个非常重要的特性,它提供了数据库的很多功能,但如果使用不当,就会到来风险。
关于锁的历史文章如下,
《如何定位锁定用户的元凶?》
《MySQL快速定位全局锁的途径》
《MySQL锁等待超时的解决路径》
《MySQL的MDL锁解惑》
《InnoDB快速定位行锁争用会话的过程和操作》
《小白学习MySQL - 查询会锁表?》
徐老师写的这篇文章《MySQL的锁》介绍一些MySQL数据库中锁的知识,值得学习借鉴。
MySQL的锁包括服务器级别的锁,存储引擎级别的锁,及互斥锁。服务器级别的锁包括表锁和元数据锁,存储引擎的锁是行级别的锁,由InnoDB引擎控制。互斥锁是低级别的锁,适用于内部的资源,用于同步低级别代码的操作,确保一次只有一个线程能够访问,例如,日志文件、自增列的计数器,及InnoDB buffer pool的互斥。
如何识别锁争用?
用户首先需要了解InnoDB的锁,之后可以通过如下方法识别不同的锁争用:
- 通过SHOW PROCESSLIST 或查询Performance Schema的threads表识别长时运行或阻塞的查询
- 通过查询Performance Schema中同步的指标(/Wait/synch/mutex/*)来识别互斥争用
- 通过查询Performance Schema和Information Schema中的视图,识别阻塞和等待的事务
- 通过查询Performance Schema中的data_locks表,识别当前的锁
- 通过查询Performance Schema中的metadata_locks表,识别当前的元数据锁
InnoDB表锁
InnoDB的表锁包括共享锁S、排他锁X、意向共享锁IS,及意向排他锁。
共享锁S:对表加锁用于读取
排它锁X:对表加锁用于写入
意向共享锁IS:对表加锁,以允许执行行级别的共享锁
意向排它锁IX:对表加锁,以允许执行行级别的排它锁
当一个事务对表加锁时,如果遇到另一个事务的锁,需要两个事务的锁类型兼容才能够获得锁。表锁类型的兼容矩阵如下:
X | IX | S | IS | |
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容 | 冲突 | 兼容 |
S | 冲突 | 冲突 | 兼容 | 兼容 |
IS | 冲突 | 兼容 | 兼容 | 兼容 |
InnoDB的行锁
一个事务在获得行锁之前必须获得表的意向锁(IS或IX)
- 共享锁S:允许事务读取一行,并允许其他事务获得该行的共享锁
- 排它锁X:允许事务读取并写入一行,但不允许其他事务对该行加锁
假设,事务A对一行具有排他锁,事务B请求对该行加共享锁,由于两个类型的锁冲突,事务B被阻塞执行,等待获得行锁。假设,事务C对一行具有共享锁,事务D同样对该行加共享锁,两个事务的锁不发生冲突,则事务D可以获得锁。
MySQL提供了非锁定读取功能。一个正常的SELECT语句不使用“FOR SHARE”或“FOR UPDATE”时,读取InnoDB的数据不需要获取任何锁,如果使用“FOR SHARE”则要求一个共享锁,使用“FOR UPDATE”将要求一个排他锁。
使用SHOW PROCESSLIST获取锁信息
使用“SHOW PROCESSLIST”在输出的“state”列中显示锁的相关信息:
“State: Waiting for table metadata lock”表示表具有冲突的锁
“State: update”或“State: Searching rows for update ”表示具有InnoDB锁(表锁或行锁)
注意,使用“SHOW PROCESSLIST”无法获得锁的内部信息,用户也可以查询Performance Schema中的threads表获得同样的信息。推荐用户使用Performance Schema,可以减少对服务器性能的影响。
使用Performance Schema和Information Schema监视锁
Performance Schema和Information Schema中的视图包含与事务关联的锁信息,包括正在加锁和被锁的语句:
- INFORMATION_SCHEMA.INNODB_TRX: 事务和锁的通用信息。信息包括事务ID、线程ID、事务开始时间、当前执行的查询、事务状态,及锁的状态
- performance_schema.data_locks: 每个锁和被锁资源的信息。信息包括引擎、引擎锁ID、引擎的内部事务ID、线程ID、事件ID、对象模式、对象名称、索引名称、锁在内存中的地址、锁类型、锁模式、锁状态,及锁数据
- performance_schema.data_lock_waits: 被锁的事务,及被该事务锁的事务。包括存储引擎要求的锁ID、锁请求的引擎内部事务ID、锁请求会话的线程ID、阻塞的锁ID、具有阻塞锁的事务的引擎内部ID、具有阻塞锁会话的线程ID,及会话中阻塞锁的Performance Schema事件ID
- sys.innodb_lock_waits:方便查询加锁和等待锁的语句的视图,组合了上述三个视图的内容
- performance_schema_metadata_locks:当一个事务访问一个表时,InnoDB通过元数据锁阻止在该表上进行的DDL操作。通过该视图的“LOCK_STATUS”列,识别每个锁的状态。当一个元数据锁请求立即获得时,MySQL会插入一个新的行,状态使用“GRANTED”,当请求一个无需立刻获得的元数据锁时,MySQL会插入一个状态为“PENDING”的新行。当元数据锁释放后,会删除该行。
如果您认为这篇文章有些帮助,还请不吝点下文章末尾的"点赞"