前情提要
当多个事务同时更新一条数据的时候,如何防止脏写的问题
锁机制的引入
依靠锁机制让多个事务更新同一行数据的时候串行化,避免同时更新。
锁机制下的更新步骤
- 事务A要更新一条数据,先判断当前数据是否有锁。没锁则当前事务创建一个锁,其中包含了自己的trx_id和等待状态,然后把锁和数据关联起来。
- 此时数据和锁都是在内存中。
- 事务B也要更新这条数据,检查数据是否有锁时发现存在着事务A创建的锁。则创建了属于自己的一个锁,其中等待状态为true。表示正在等待
- 事务A更新完后,释放锁,然后寻找到事务B对这条数据加锁了。此时就会把事务B的锁中的等待状态修改为false,然后唤醒事务B。
行锁
独占锁
简介
又叫X锁,Exclude锁。当有一个事务加了独占锁后,其他事务再来更新当前数据,都是要加独占锁的,但是只能在独占锁后面等待。
备注说明
当多事务更新同一行数据时,其他事物是能够直接读取这行数据的,并不需要加锁。因为默认开启mvcc机制可以基于ReadView去undo log版本链中找到一个能够读取的版本。
共享锁
简介
又叫S锁。语法是:SELECT * FROM TABLE LOCK IN SHARE MODE;意思是在查询的时候对一行数据加共享锁。
备注说明
当一行数据加了X锁后,S锁是无法添加的,因为两者互斥。
当一行数据加了S锁后,其他事物也能添加S锁,因为S锁不互斥。
其他情况
查询操作通过LOCK IN SHARE 添加共享锁;
查询操作通过FOR UPDATE 添加互斥锁;
表锁
语法
LOCK TABLES xxx READ;加表级共享锁(很少用)
LOCK TABLES xxx WRITE;加表级独占锁(很少用)
其他加锁的操作
- 如果有事务在表里执行增删改操作,就会在行级加独占锁。还会在表级加一个意向独占锁。
- 如果事务在表里执行查询操作,那么会在表级添加一个意向共享锁。
备注说明
- 意向独占锁和意向共享锁不互斥
互斥关系
锁类型 | 独占锁 | 意向独占锁 | 共享锁 | 意向共享锁 |
---|---|---|---|---|
独占锁 | 互斥 | 互斥 | 互斥 | 互斥 |
意向独占锁 | 互斥 | 不互斥 | 互斥 | 不互斥 |
共享锁 | 互斥 | 互斥 | 不互斥 | 不互斥 |
意向共享锁 | 互斥 | 不互斥 | 不互斥 | 不互斥 |
- 独占锁与其他锁都互斥
- 意向XX锁与意向XX锁之间不互斥
- 共享锁,意向共享锁之间不互斥
- 意向共享锁只与独占锁互斥
不确定性的性能抖动
脏页刷盘
原因分析
当一个查询语句,加载了大量的数据到缓存页中,导致内存中大量的缓存页被淘汰然后刷回磁盘。
解决方案
减少缓存页刷盘的频率:增加buffer pool分配的内存空间
Redolog刷盘
原因分析
redolog不断的写入,当日志文件写满了,就会对第一个日志文件进行覆盖写入,此时如果
第一个日志文件中的一些redolog
对应的内存里的缓存页的数据
如果还没有被刷回磁盘的话。也会触发脏页回盘的过程。
解决方案
提升缓存页刷盘的速度
- 因为刷盘是典型的随机IO,所以要提升随机IO的性能,使用SSD固态硬盘。
- 配置参数innodb_io_capacity:采用多大的IO速率刷盘(每秒随机IO的次数)
- 配置参数innodb_flush_neighbors:刷盘时把缓存页临近的其他缓存页也刷盘,但是这样刷回的缓存页就会变多,如果是SSD,把这个设置为0就行。