一、长事务
1.1 定义
长事务,就是时间特别长的事务。
比如:SQL语句可能没有很多,但是中间过程比较长,commit的时间特别长。
1.2 危害
长事务的危害,体现在锁上。
因为大部分的锁,是无法手动释放的,都是需要等待别的事务提交之后,自动释放。
- (行锁)死锁:两个事务,互相拥有对方想拥有的锁。----------- 只两个事务互相锁住
- 最容易忽略的锁:MDL锁(表级锁的一种:元数据锁)。-------- 后面排队的事务,都锁死
1.3 危害1:行级锁长时间无法释放
1.4 解决:参数调整:减小锁阻塞等待的超时时间。本质:不解决根本问题,只是减弱影响
设置:减小锁阻塞等待的超时时间。
可以缩短为20秒。如果是经常性的等待20秒-40秒,前面事务还不退出,就说明前面事务(业务)写的有问题。
长事务,应该避免出现。
1.5 危害2:容易产生死锁
1.理论:
- 左边:对id为10的那行数据查询出来,加了写锁。select for update相当于update语句。
for update:
是当前读
1.6 解决:主动死锁检测,默认开启
解决:
当每次加锁时,会主动判断:这把锁加下去会不会死锁。
2.示例:
左边事务A:此时阻塞卡住了,因为该行记录,已经被右边的事务B之前锁住了
右边事务B:语句4回车的瞬间,就会主动报错:说明主动的死锁检测起作用了,即把代价较小的锁撤销掉,即右边事务B给回滚了:
同一瞬间,左边事务A,id为20的行记录,就立马拿到锁并加上锁了:弹出左边的表格结果
3.
凡是每个update的SQL,都会进行主动死锁检测,在大的事务并发时,会很影响性能。
所以,此时看一下主动死锁检测是否是开着的:如果开着,有可能是它的锅,就把它关掉。
关了就关了,不能死锁检测也没关系。将前面的锁等待时间调小为20秒,也能解决问题。
当然,治本还是:业务逻辑上,不要写长时间事务。
二、元数据锁:能导致数据库崩溃
2.1 理论:MDL锁等待
注意:
这里的事务访问数据,就是直接普通的select,不是之前的select for update,
2.2 示例(图)
一般,快照读,不加表锁、行锁。但是会加MDL元数据锁。
- Tx A:读取查询。--------------------------------------------加了MDL读锁
- Tx B:更新操作,有可能是加列,或加索引。-------即想加MDL写锁,根本加不上,阻塞等待。自动变成一个队列
- Tx C:读取查询。--------------------------------------------等着加MDL读锁
- Tx D:读取查询。--------------------------------------------等着加MDL读锁
2.3 代码示例
1.
开了四个MySQL客户端,分别代表四个不同的独立事务ABCD。
2.
事务A:当前读;------ 加了MDL读锁
事务B:加1列;------- 阻塞了。因为该事务是加MDL写锁,加不上,所以形成了队列,卡住了
事务C:当前读-------- 按理说,应该可以读。但是,前面加了个事务B的MDL写锁的原因,自己也只能卡在队列里
事务D:当前读-------- 跟事务C一样
3.解决:
根本原因是:事务A的MDL元数据读锁,一直没有释放。导致事务B的写锁阻塞等待,进行形成队列
事务A:commit提交一下;
事务C、D这两个MDL读锁的查询,瞬间已完成;
原因是:事务A提交了,锁已释放;队列中的事务B,瞬间拿到MDL写锁。加上1列,加完之后就释放锁。事务C、D相继完成。
大多数时候我们不希望在服务器上安装图形界面,但有时候有些程序需要图形界面。比如安装Oracle的时候。此时,可以配置让Linux使用远程的X Server进行图形界面显示。
2.4 解决
- 类似的事务B,不要在业务的高峰期做.
因为只要你阻塞等待,后面所有的事务,也会阻塞等待
- 如果非要进行类似的事务B,那么就要进行检查:是否有长事务未提交;
三、如何查看影响性能的锁
不只是MDL元数据锁,还包括以前学习的所有的锁:
3.1 MySQL :查系统表
3.2 MySQL 8.0
3.3 业务上的建议
不只是数据库上优化,最主要的是在业务上:
尤其是现在大家都没有手动的开关事务、手动写SQL,都是用的ORM框架,比如MyBatis之类的。这更会让人忽略事务开闭的时机
- 尽量不用事务,不用长事务;
- 如果必须有长事务,就用快照读(因为不加行锁,只加MDL元数据锁);数据更新操作,一次性的集中在最末尾
Comments | NOTHING