mysql事务隔离

2021-12-08  本文已影响0人  中2庸

事务是什么

   事务比较常见数据库操作,例如银行金融业务,涉及多个操作,并保证操作要么全部成功要么全部失败,这样保证数据的一致性。事务支持是在引擎层实现的,事务存在以下特性:原子性、一致性、隔离性、持久性;

事务隔离级别有哪些

读未提交

Read uncommmited:读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
● 事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
● 分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
● 问题:这种事务隔离级别可导致脏读,使用读提交,能解决脏读问题。

读已提交

Read committed:读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据
● 事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他买单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候,程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
● 分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读
● 问题:这种隔离级别可导致不可重复读;那怎么解决可能的不可重复读问题?Repeatable read

可重复读

Repeatable read:重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
● 事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他买单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
● 分析:可重复读隔离级别可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
● 问题:导致幻读问题;什么时候会出现幻读?事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

串行

Serializable 序列化:是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。“写”会加“写锁”,“读”会加“读锁”,当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行,这种事务隔离级别效率低下,比较耗数据库性能,一般不使用

【注意】大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read

事务隔离之可重复读如何实现

  这里主要说下可重复读的实现,首先可重复读是利用undo log来记录,不同时刻启动的事务会有不同的 read-view,Undo log是InnoDB MVCC事务特性的重要组成部分。当我们对记录做了变更操作时就会产生undo记录,Undo记录默认被记录到系统表空间(ibdata)中,但从5.6开始,也可以使用独立的Undo 表空间。
案例分析:事务 A 第一次读完之后,事务 B 要修改这行数据了。undo log 会为所有写操作生成日志,所以就会生成一条 undo log 日志,并且它的 roll_pointer 会指向上一条 undo log 日志。


image

紧接着,事务 A 第二次去读这行数据了,情况如下图所示:


image
第一次读的时候,开启事务 A 的时候就生成了一个 ReadView-R,此时事务 A 第二次去查询的时候,先查到的是 trx_id = 18 的那条数据,它会发现 18 比最小的事务编号 10 大。那就说明事务编号为 18 的事务,有可能它是读不到的。
接着就要去 m_ids 里确认是否有 18 这条数据了。发现有 18,那就说明在事务 A 开启事务的时候,这个事务是没有提交的,它修改的数据就不应该被读到。事务 A 就会顺着 roll_pointer 指针继续往下找,找到了 trx_id = 8 这条日志,发现这条能读,读到的值任然是 x,与第一次读到的结果一致。成功实现可重复读!

常见问题

1.可重复读的使用场景举例? 对账的时候应该很有用?
  对于已经开启的事务,读视图已经建立,这时对账过程中出现更新不影响对账;

2.并发版本控制(MCVV)的概念是什么, 是怎么实现的?
  是通过保存数据在某个时间点的快照来实现并发控制的。也就是说,不管事务执行多长时间,事务内部看到的数据是不受其它事务影响的,根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的;
可以认为 多版本并发控制(MVCC) 是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行;

3.使用长事务的弊病? 为什么使用常事务可能拖垮整个库?
  长事务意味着系统里面会存在很老的事务视图,由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就导致了大量占用内存。在MySQL5.5以前的版本,回滚日志是和数据字典一起放在ibdata文件里的,即使长事务提交,回滚段被清理,文件也不会变小,最终往往为了清理回滚段而重建整个库。除了影响回滚段,长事务还会占用锁资源,也有可能拖垮整个库。

4.commit work and chain的语法是做什么用的?
  提交当前事务,并开始下一个事务

5.怎么查询各个表中的长事务?
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60;

7.如何避免长事务的出现?
在一个事务里面, 避免一次处理太多数据
在一个事务里面,尽量避免不必要的查询
在一个事务里面, 避免耗时太多的操作,造成事务超时。一些非DB的操作,比如rpc调用,消息队列的操作尽量放到事务之外操作

参考文献

https://www.cnblogs.com/cnbk/p/13043872.html
http://mysql.taobao.org/monthly/2015/04/01/
https://time.geekbang.org/column/article/68963
https://blog.csdn.net/itworld123/article/details/115207304
https://segmentfault.com/a/1190000037557620
https://www.cnblogs.com/trunks2008/p/15213918.html
https://segmentfault.com/a/1190000037557620
https://segmentfault.com/a/1190000023273980

上一篇 下一篇

猜你喜欢

热点阅读