当前位置:Gxlcms > 数据库问题 > MySQL8-事务

MySQL8-事务

时间:2021-07-01 10:21:17 帮助过:35人阅读

1、什么是一致读:在事务A中的查询,看不到事务B所做的修改;即使B已经提交,A的查询仍然是基于之前某个时间点的数据库快照的结果。时间点的如何选取呢?对于可重复读,时间点选取第一次读的时间;对于读已提交,时间点是每次consistent read的时间【也就是说同一事务中读出来并不一致,印证了一致读的核心是不加锁,而不是什么一致】。如果查询的数据已经被其它事务更改,则原始数据基于undo日志的内容进行重建。 2、什么时候一致读:Innodb在读已提交和可重复读隔离级别下,执行select的默认模式便是consistent read。 3、一致读有什么好处:因为consistent read不会在它读取的表上加任何锁,当consistent read在表上执行时,其它事务可以自由地修改这些表。这项技术可以避免一些锁定问题,而这些锁定问题由于强制事务等待其它事务完成而降低了并发量。 4、本质:一致性读与其他读取方式最大的区别在于是否加锁;一致读不加任何锁。与一致性读相对应的是Current Read,而当前读,是指update, delete, select for update, select in share mode等等语句进行的读,它们读取的是数据库中的最新的数据,并且会锁住读取的行和gap(RR隔离时)。 5、补充说明
  • 不一致:查询可以看到在快照时间点之前提交的事务所做的更改,看不到快照时间点之后提交或未提交的事务所做的更改。这条法则的例外是查询可以看到本次事务内,前面的语句所做的更改。这个例外会导致以下不一致:如果在一个表中更新了一些行,select语句可以看到这些更新行的最新数据,却也能看到其他行的旧数据。如果其他sessions同时更改了同一张表,那么这个不一致意味着你可能看到了一个从来没有出现在数据库中的状态。【因此有时需要locking reads】
  • 数据库快照应用于事务内的select语句,但是对DML语句并不是必须的。
  • 如果你insert或update了一些行,然后提交这个事务A;则另一个事务B的delete或update语句可以影响那些已经提交的行,即使B不能查询他们。也就是说虽然B中有快照不能看到A的更改,但是B却可以对这些行进行更改。
  • 如果一个事务A update或delete了不同事务B提交的行,则B对这些行所做的更改在A中可见。
  locking reads:共享锁读取和排它锁读取 1、为什么需要:如果你查询数据,然后在同一个事务内插入或更改相关联的数据(可以理解为这些操作依赖于之前查询的结果),那么普通的查询语句不能给予足够的保护。Innodb支持两种类型的locking reads来提供额外的安全性,即共享锁读取和排它锁读取。 2、什么区别
  • 共享锁读取,即select...lock in share mode,会对读取的行加共享锁。如果加锁成功,则其它事务只能读取行,在你的事务提交或回滚前不能修改行。同样的,如果共享锁读取时,其它事务已经修改行但是并没有提交,则只能等待这些事务提交或回滚。
  • 排它锁读取,即select...for update,会对查询行(即任何相关索引项?不理解)加锁,与在这些行上执行update语句的效果是相同的。当其它事务执行以下操作时会被阻塞:更新那些行,执行select...lock in share mode,或在某些事务隔离级别下执行查询。但是consistent read会忽略在读视图下加的任何锁。【For index records the search encounters, SELECT ... FOR UPDATE locks the rows and any associated index entries, the same as if you issued an UPDATE statement for those rows.】【关于行锁还是表锁:如果是按照明确的索引进行查询的,则是行锁;如果不是索引,或者不明确(如<等),则是表锁,性能会非常差】
  • 这两种读锁在事务提交或回滚时会释放。
3、使用场景
  • 【共享锁读取举例】假设存在两个表parent和child,只有parent中有某个值时,child才能对应的插入;因此需要先从parent中查询,再向child中插入。假设使用一致读,查询到了某个parent(记做a),而在插入之前,其它事物删除掉了a,那么插入后,数据库的状态就出现了问题:某个child没有对应parent。为了避免这种情况,可以采用共享锁读取:其它行只能读不能改。
  • 【排它锁读取举例1】假设表t中一栏表示vip过期时间,当用户购买30天vip时,新的过期时间如下计算:如果已经过期,则在现在时间上加30天;如果还没有过期,则在原过期时间上加30天。假设用户vip已经过期;现在有两个事务同时请求:先查询过期时间,在应用内计算新的过期时间后,更新过期时间。如果使用一致读,那么两个事务看到的都看到过期,计算的新过期时间都是30天后;这样就导致了其中的一次更新被淹没了。如果使用共享锁读,那么两个事务读的时候都加了锁,因此都不能更新,可能进入死锁状态。
  • 总结:当一个事务(A)中,先读后改(改泛指增删改),且改的操作依赖于读的结果(无论是不是在一张表中),那么应该使用locking reads进行保护,防止其它事务(B)在之间进行更改。如果B只是更改,而不是像A一样先查后改且改的操作依赖于A的结果,那么一般使用共享锁读;如果B也像A一样,则一般使用排它锁读。
  扩展:select+update处理并发问题解决方案/select...for update替代方案 【参考:http://www.jb51.net/article/50103.htm】 方法1:讲select和update合并成一个语句,用case/when/then/else/end等;当select和update之间操作复杂,或需要在应用中做其他工作时,这种方法不能使用;局限性较大。 方法2:select...for update 方法3:乐观锁机制。以上述为例,首先执行select获取状态,然后在update时将select获得的状态作为一个条件传入,这样如果查询到的数据被其他事务更改,则update不会更改;而且可以根据update的返回结果知道update是否成功,从而进行进一步的操作(重试或者放弃,根据业务情形决定)。       八、乐观锁与悲观锁 参考http://blog.csdn.net/hongchangfirst/article/details/26004335 1、悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 2、乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。 3、两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。    

MySQL8-事务

标签:hose   line   冲突   ring   arc   art   个数   ued   替代   

人气教程排行