全局锁
命令 Flush tables with read lock (FTWRL)
- 阻塞
当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。 - 适用场景 :做全库逻辑备份
- 不加锁的话,备份系统备份的得到的库不是一个逻辑时间点,这个视图是逻辑不一致的。
- 官方自带的逻辑备份工具是 mysqldump。当 mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。
- 对于 MyISAM 这种不支持事务的引擎,必须全局锁
另一种全局锁 不推荐使用
set global readonly=true 的方式
- readonly 的值会被用来做其他逻辑,比如用来判断一个库是主库还是备库
- 执行异常断开后不会释放全局锁
表级锁
一种是表锁, lock tables … read/write
- 表锁的语法是线程会造成阻塞,在还没有出现更细粒度的锁的时候,表锁是最常用的处理并发的方式
表级锁 meta data lock,MDL
一种是元数据锁(meta data lock,MDL)。
- 读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
- 读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。同一个表会顺序加字段
- 事务中的 MDL 锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放。MDL读锁与MDL 写锁(ALter改变表结构的),读锁没释放,写锁被阻塞导致,下一个读锁依赖写锁被释放,导致表读写不可用, 使用NOWAIT/WAIT 等待执行,不成功可以重复操作
行锁
- 两阶段锁:在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
- 如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。
死锁
两个事务互相等待对方先提交会造成死锁
- 一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置,InnoDB引擎默认值是50s。
- 另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
- 死锁检查会消耗大量CPU,如何解决
热点数据性能问题
- 如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关闭掉。一般不建议采用
- 控制并发度,对应相同行的更新,在进入引擎之前排队。这样在InnoDB内部就不会有大量的死锁检测工作了。
- 将热更新的行数据拆分成逻辑上的多行来减少锁冲突,但是业务复杂度可能会大大提高。
检查死锁
show engine innodb status
事务的启动方式
begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。
- 第一种启动方式,一致性视图是在执行第一个快照读语句时创建的;
- 第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的。
事务id
开启事务会获得一个事务id(自增),数据版本是按照提交时间来排序的
image.png
所以 A看到的还是1,一致性原则。
事务id的排序是 A<B<C
数据版本的排序是C早于B
B如果再自己update之前查询,查到的还是1,因为一致性
但是自己update之后查到的就是3,原因是当前读 再最新的数据版本上执行更新。
更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。
事务是如何实现的MVCC呢?
- 每个事务都有一个事务ID,叫做transaction id(严格递增)
- 事务在启动时,找到已提交的最大事务ID记为up_limit_id。
- 事务在更新一条语句时,比如id=1改为了id=2.会把id=1和该行之前的row trx_id写到undo log里,
并且在数据页上把id的值改为2,并且把修改这条语句的transaction id记在该行行头 - 再定一个规矩,一个事务要查看一条数据时,必须先用该事务的up_limit_id与该行的transaction id做比对,
如果up_limit_id>=transaction id,那么可以看.如果up_limit_id<transaction id,则只能去undo log里去取。去undo log查找数据的时候,也需要做比对,必须up_limit_id>transaction id,才返回数据
网友评论