0
点赞
收藏
分享

微信扫一扫

MYSQL的事务

MYSQL的事务_database

定义

数事务指的是一组数据操作,事务内的操作要么就是全部成功,要么就是全部失败,是一个不可分割的工作单位。

事务的特性及其实现原理

原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)四个特性,简称 ACID。

 Atomicity:

事务是一个最小的执行单位,包含在其中的对数据库的操作要么全部都执行,要么都不执行。例如一条语句从A里扣钱,另一条语句往B身上加钱,如果这两条语句不能全部执行,而是成功了一部分,那事务就没有存在的意义了。

是使用 undo log来实现的,如果事务执行过程中出错或者用户执行了rollback,系统通过undo log日志返回事务开始的状态。 

 Consistency:

指事务使得应用系统从一个正确的状态到另一个正确的状态。在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。

通过回滚、恢复,以及并发情况下的隔离性,从而实现一致性。

 Isolation:

多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。如果一个事务正在操作的数据被另一个事务修改或删除了,最后的执行结果可能无法达到预期。

通过锁以及MVCC,使事务相互隔离开。

 Durab ility:

意为事务完成了对数据的修改之后,修改的结果是永久性生。

使用 redo log来实现,只要redo log日志持久化了,当系统崩溃,即可通过redo log把数据恢复。

事务并发带来的问题有哪些?

Lost Update 更新丢失:事务A、B更新同一行数据时,双方都不知道对方的存在,就有可能覆盖对方的修改。 

Dirty Reads 脏读事务A读取到事务B未提交的数据。事务B在执行时修改了某条数据,事务A正好也读取了这条数据,并基于这条数据做了其他操作,因为事务B还没提交,如果基于修改后的数据进一步处理,就会产生无法挽回的损失。

Non-Repeatable Reads 不可重复读在一个事务范围内,两个相同的查询,读取同一条记录,却返回了不同的数据。同样是事务A、B在操作同一数据,如果在事务A开始时读了某数据,这时候事务B修改了这条数据,等事务A再去读这条数据的时候发现已经变了,这就是没办法重复读一条数据。

Phantom Read 幻读:不可重复读是指同一事物不同时刻读到的数据值可能不一致,而幻读则是一个事务按相同的查询条件查询之前检索过的数据,确发现检索出来的结果集条数变多或者减少(由其他事务插入、删除的)。例如:事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的。

可重复读仅能保证再次执行同样的查询时,先前返回【过】的结果一定跟之前一样,而不保证会不会多出别的记录。如果返回了之前没有出现过的记录,就是幻读。

不可重复读重点在于update(原有数据),而幻读的重点在于insert和delete(数量)

为解决事务并发带来的问题,使用了什么手段?【四大隔离级别】

更新丢失,可以完全避免,应用对访问的数据加锁即可。事务A执行 update 操作, update 的时候要对所修改的行加行锁,这个行锁会在提交之后才释放。而在事务A提交之前,事务B也想 update 这行数据,于是申请行锁,但是由于已经被事务A占有,事务B是申请不到的,此时,事务B就会一直处于等待状态,直到事务A提交。

加锁的过程要分有索引和无索引两种情况,比如下面这条语句



update user set age=11 where id = 1



id 是这张表的主键,是有索引的情况,那么 MySQL 直接就在索引中找到了这行数据,然后干净利落的加上行锁就可以了。

而下面这条语句



update user set age=11 where age=10



表中并没有为 age 字段设置索引,所以, MySQL 无法直接定位到这行数据。那怎么办呢,当然也不是加表锁了。MySQL 会为这张表中所有行加行锁,没错,是所有行。但是呢,在加上行锁后,MySQL 会进行一遍过滤,发现不满足的行就释放锁,最终只留下符合条件的行。虽然最终只为符合条件的行加了锁,但是这一锁一释放的过程对性能也是影响极大的。所以,如果是大表的话,建议合理设计索引,如果真的出现这种情况,那很难保证并发度。

mysql的锁类型请参考:​​MYSQL的锁_李歘歘的博客-CSDN博客​​

脏读、不可重复读、幻读的解决使用了事务的隔离性,共有四大隔离级别。

  • 读未提交(Read Uncommitted)其实就是事务没提交就可以读,相当于裸奔。
  • 读已提交(Read Committed)一个事务只能读到其他事务已经提交过的数据,即事务提交后才能读,可以解决脏读。
  • 可重复读(Repeatable Read)该事务执行期间,不允许其他事务对该事务数据进行操作,保证该事务中多次对数据的查询结果一致,可以解决脏读和可重复读。
  • 串行化(Serializable)所有的事务串起来一个个执行,因为没有并发的场景出现了,解决了脏读、可重复读、幻读的问题,性能很差,一般不会使用。

从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。

四个隔离级别解决隔离性能如下:

隔离级别

脏读

不可重复读

幻读

读未提交

可能

可能

可能

读已提交

不可能

可能

可能

可重复读

不可能

不可能

可能

串行化

不可能

不可能

不可能

关于隔离级别的实现原理:

读未提交,它是性能最好,也可以说它是最野蛮的方式,因为它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。

再来说串行化。读的时候加共享锁,也就是其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读。

为了解决不可重复读,实现可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。快照,学名叫做一致性视图,是可重复读和读已提交的关键读已提交则是每次执行语句的时候都重新生成一次快照,可重复读是在事务开始的时候生成一个当前事务全局性的快照,。

读未提交和串行化基本上是不需要考虑的隔离级别,前者不加锁限制,后者相当于单线程执行,效率太差。读已提交解决了脏读问题,行锁解决了并发更新的问题。并且 MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的。

参考:

​​一文讲清楚MySQL事务隔离级别和实现原理,开发人员必备知识点 - 风的姿态 - 博客园​​

​​MySQL的事务隔离级别是什么? - 掘金​​

​​一文彻底读懂MySQL事务的四大隔离级别 - 掘金​​

MYSQL的事务_数据_02


举报

相关推荐

0 条评论