事务隔离:为什么你改了我还看不见

161 阅读4分钟

事务特性

一致性、隔离性、持久性、原子性

执行事务会出现的问题

脏读、幻读、不可重复读等问题

事务的隔离级别

1.读未提交,一个事务还没有提交时,它做的变更就能被别的事务看到。(脏读) 2.读提交,一个事务提交之后,它做的变更才会被其他事务看到。(可能幻读) 3.可重复读,一个事务执行过程中看到的数据,总跟这个事务在启动时看到的数据是一致的。当然这个重复读隔离级别下,未提交变更对其他的事务也不可见。(事务在执行期间看到的数据前后必须一直) 4.串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能执行。

查看事务的隔离级别

show variables like "transaction_isolation";

什么情况下,使用“可重复读”的场景呢?

需要在事务开始到结束期间,查询前和查询后的数据要保持一直。

事务隔离的实现

1.同一条记录有多个版本,就是数据库的多版本并发控制(MVVC)

不要使用长事务的原因

长事务意味着系统里面会存很老的事务试图。由于这些事务随时可能会访问数据库里面任何数据,所以这个事务提交前,数据库里面他可能用到的回滚记录都必须保存,这就会导致大量占用存储空间。 对于长事务,还占用了锁资源,也可能拖垮整个库。

查询持续时间超60秒的事务

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

事务的启动方式

1.显示启动事务语句,begin和start transaction。配套语句是 commit,回滚语句是rollback 2.set autocommit = 0,这命令会将这个行程的自动提交关闭。意味着如果你只执行一个select语句。这个事务就启动了,而且并不会自动提交。这个事务持续存在知道你主动执行commit或rollback。 有些客户端链接框架会默认链接成功后先执行一个set autocommit=0的命令。这就导致链接下来的查询都在事务中,如果是长链接,就导致了意外的长事务。 因此我建议使用set autocommit = 1通过显示的语句的方式来启动事务。

如何避免长事务对业务的映像

首先从开发端来看: 1.确定是否使用set autocommit = 0。这个确认工作可以在测试环境中开展,把MYSQL的general_log开起来,然后随便跑一个业务逻辑,通过log的日志确认。一般框架如果设置这个值,就会提供参数来控制行为,你的目标就是把它改为1。 2.确认是否不必要的只读事务。 3.避免单个语句执行的时间太长,通过set max_execution_time命令

从数据库端 1.监控information_schema.innodb_trx表,设置长事务阈值,超过机报警或者KILL 2.Percona的pt-kill这个工具不错 3.在业务功能测试阶段要求输出所有的general_log,分析日志行为提前发现问题。 4.如果使用mysql56或者更新的版本,把innodb_undo_tablespaces设置成2(或者更大的值)。如果真的出现大失误导致回滚段过大,这样设置后续清理更方便。

视图

1:一个view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view,他的查询方法和表的一样 2:innodb在实现MVCC的时候用的一致性读视图,即consistent read view,用于支持 RC和RR隔离级别实现。

快照和mVCC里是怎么工作的?

快照的实现: innodb里面每个事务有一个唯一的事务id,叫做transaction id。它是在事务开始的时候向innodb申请的,是按照申请顺序严格递增的。而每行数据也都有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且transaction id赋值给这个版本的事务id,并记录row trx_id。也就是说,数据表中的一行记录,其实有多个版本row,每个版本都有自己的row trx_id

共享锁、排它锁

一个session加了共享锁,其他也正常也加正常读,但是修改会失败。 一个session加了排它锁,其他排它锁都得阻塞。