什么是大事务?
简单来说就是那些运行时间比较长,操作的数据比较多的事务
如何查询大事务?
以查询执行时间超过10秒的事务为例:
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>10
当系统中不同事务之间出现循环资源依赖,涉及的事务都在等待别的事务释放资源时,就会导致这几个事务都进入无限等待的状态,比如下面这个场景:
这时候,事务A在等待事务B释放id=2的行锁,而事务B在等待事务A释放id=1的行锁。 事务A和事务B在互相等待对方的资源释放,就是进入了死锁状态
首先我们知道,有两种策略可以处理死锁:
但是死锁检测可能会存在一个问题:
假如所有事务都要更新同一行的时候。每个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是 O(n)的操作。假设有1000个并发线程要同时更新同一行,那么死锁检测操作就是100万这个量级 的。虽然最终检测的结果是没有死锁,但是这期间要消耗大量的CPU资源。因此,你就会看到 CPU利用率很高,但是每秒却执行不了几个事务。
在MySQL中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。
假设一个值从1被按顺序改成了2、3、4,在回滚日志里面就会有类似下面的记录。
当前值是4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的read-view。如图中看到的,在视图A、B、C里面,这一个记录的值分别是1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于read-view A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。
同时你会发现,即使现在有另外一个事务正在将4改成5,这个事务跟read-view A、B、C对应的 事务是不会冲突的。
你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。 也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。
什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的read-view的时候,换一种说法就是在这些事物提交之后。
因为主库上必须等事务执行完成才会写入binlog,再传给备库。所以,如果一个主库上的语句执行10分钟,那这个事务很可能就会导致从库延迟10分钟
两阶段锁协议是什么?
在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放
基于两阶段锁协议我们可以做这样的优化:
如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放
假设你负责实现一个电影票在线交易业务,顾客A要在影院B购买电影票。我们简化一点,这个 业务需要涉及到以下操作:
想要解决死锁检测的问题那么就只能控制O(n)的数量,当同一行并发数小的时候死锁检测的成本就会很低了
不过这个并发数还挺不好控制:
这个方案看上去是无损的,但其实这类方案需要根据业务逻辑做详细设计。如果账户余额可能会减少,比如退票逻辑,那么这时候就需要考虑当一部分行记录变成0的时候,代码要有特殊处理。
我们知道MySQL的事务隔离级别默认是可重复读,在这个隔离级别下写数据的时候会有这些问题:
但是如果把隔离级别改为读提交就不存在这两个问题了,每次写数据只会锁一行
但同时,你要解决可能出现的数据和日志不一致问题,需要把binlog格式设置 为row
关于为什么要设置binlog可以参考这篇文章为什么要把MySQL的binlog格式修改为row
原文:https://www.cnblogs.com/zhixiang-org-cn/p/12454275.html