MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类
全局锁
MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。
当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。
风险:
1.如果在主库备份,在备份期间不能更新,业务停摆
2.如果在从库备份,备份期间不能执行主库同步的binlog,导致主从延迟
可重复读的隔离级别,可以让同一个事务拿到一致性视图,这个过程由MVCC支持 ,(MyISAM 不支持此事务所以需要FTWRL
表级锁
表锁:lock tables … read/write
元数据锁(MDL meta date lock): MDL 不需要显式使用,在访问一个表的时候会被自动加上
MDL 锁是系统默认会加的,给一个表加字段,或者修改字段,或者加索引,需要扫描全表的数据。而且事务中的 MDL 锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放
MDL 保证读写的正确性
当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。
行级锁
两阶段协议:
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
针对两阶段协议做的优化
顾客在电影院买票的大概流程:
1、顾客账户余额中扣除票钱
2、给影院增加账户余额
3、记录一条交易日志
可见,多名客户购买,需要修改的都会集中在步骤2,所以调整顺序 3、1、2,最大程度减少事务中停留的时间
死锁、死锁检查
诱因:不同线程循环资源依赖
事务 A 在等待事务 B 释放 id=2 的行锁,而事务 B 在等待事务 A 释放 id=1 的行锁。 事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。当出现死锁以后,有两种策略:
1、一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。InnoDB默认50s
2、另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
注: 死锁检查耗费大量的CPU资源。
死锁检查:
每当一个事务被锁的时候,就要看看它所依赖的线程有没有被别人锁住,如此循环,最后判断是否出现了循环等待,也就是死锁。
总结
mysql的行锁触发的死锁与死锁检测
死锁策略:1、设置超时时间,默认50s 2、死锁检查(高并发CPU大量消耗)
热点行更新性能问题:
1、临时关闭死锁检查
2、应用层阻塞队列、数据库层阻塞队列
3、逻辑上多行减少锁冲突(业务复杂解决锁冲突)
4、调整事务中逻辑顺序