数据库复习笔记7——MySQL事务

MySQL事务

什么是事务 

事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态事务的一致性)。事务是逻辑上的一组操作,要么都执行,要么都不执行

有一个经典的例子就是转账,比如小明要给小方转100块钱,如果成功了,那么小明的账户余额信息和小方的账户余额信息都需要更新;但是如果转账过程中突然银行系统崩溃,导致小明的余额少了,小方的余额却不变,这就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。

一个完整的业务需要批量的DML(数据库管理语言,如insert、update、delete)语句共同联合完成。

MySQL语句的分类:

1、DQL(数据查询语言):含有SELECT的语句都是数据查询语言。

2、DDL(数据定义语言):例如:CREATE,DROP,ALTER

3、DML(数据操作语言):例如:INSERT,UPDATE,DELETE

4、DCL(数据控制语言):例如:GRANT,REVOKE对权限的操作

5、TCL(事务控制语言):例如:COMMIT,ROLLBACK等对事务进行操作

事务的四大特性ACID

  • Atomicity原子性:事务中包括的所有操作要么都做,要么都不做
    • 实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。需要使用undo log回滚日志实现。
  • Consistency一致性:事务必须使数据库从一个一致性状态变到另一个一致性状态。(例如上面的银行转账例子,总钱数在事务执行前后发生了变化,说明事务完成后,不符合逻辑运算,状态不一致了)其实也就是说:保证事务只能把数据库从一个有效(正确)的状态“转移”到另一个有效(正确)的状态,什么是正确的状态,其实是用户自己指定的,所以说一致性其实是一个用户层的概念。
    • 事务的一致性决定了一个系统设计和实现的复杂度,也导致了事务的不同隔离级别
    • 一致性是事务的最终目的,原子性、隔离性、持久性都是为了实现一致性。(AID是为了C)
  • 隔离性 Isolation:隔离性是指多个事务并发执行的时候,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰
  • durable持久性:根据定义,持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
    • 事务的持久性是通过redo log恢复日志实现的。Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。为了防止上述故障造成数据不一致,我们使用redo log日志记录下每一条数据库修改操作,从而保证数据库宕机重启的时候能够选择是否将数据恢复。

事务日志

我们在前面已经介绍了两种日志:

  • undo log 记录某数据被修改前的值。类似于Word中的撤销操作,用于事务的回滚,为了保证原子性。

  • redo log 记录某 数据块 被修改  的值,可以用来恢复未写入 data file 的已成功事务更新的数据。类似于Word中的恢复操作。用于事务的重做,为了保证持久性。

  • redo log和undo log是InnoDB事务机制中的日志,与MySQL级别的日志不同(如binlog):

    • redo/undo 是 innodb 引擎层维护的。而 binlog 是 mysql server 层维护的,跟采用何种引擎没有关系,记录的是所有引擎的更新操作的日志记录
    • redo/undo 记录的是 每个页/每个数据 的修改情况,属于物理日志+逻辑日志结合的方式(redo log 是物理日志,undo log 是逻辑日志)。binlog 记录的都是事务操作内容,属于逻辑日志
      • redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置))。
    • undo用来回滚行记录到某个版本。undo log一般是逻辑日志符合SQL的逻辑特性,根据每行记录进行记录
    • redo/undo 在 事务执行过程中 会不断的写入,而 binlog 是在 事务最终提交前 写入的。binlog 什么时候刷新到磁盘跟参数 sync_binlog 相关

事务日志的详细内容可以参考:https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html

并发事务带来的问题

我们知道,数据库显然是一个常常需要高并发的场景,例如:淘宝网的秒杀活动,需要支持多个用户购买后同时修改商品的库存量,如果数据库不支持并行,只支持串行,那么同一时刻只能有一个用户进行购买,这将导致用户的阻塞,显然是不好。

所以,我们需要让数据库使用并发技术。但是,我们知道,并发技术往往会带来数据的不一致性的问题,这点在多线程、多进程编程中是非常常见。

并发事务不加以控制可能导致的问题主要有:

  • 脏读(Drity Read):某个事务读取的数据正在被另一个未提交事务所处理,而另一个事务可能会回滚,导致该事务读取的数据是错误的。
  • 不可重复读:在同一个事务内,读取数据后,该数据被另一个事务所修改,导致无法重现前一次读取结果。(不可重复读:指的是一个事务连续两次读取的结果不一致)
  • 丢失修改:多个事务读入同一个数据并修改,一个事务的修改结果被另一个事务的提交所覆盖
  • 幻读同一事务两次读取之间,其他事务插入或删除一条数据。例如:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。(幻读解决了不可重复读的问题,但是幻读对于一个事务已提交的事务,另一个事务也是不可见的)

幻读和不可重复读很容易混淆,实际上,幻读侧重于数据的增添和删除,而不可重复读侧重于数据的修改

解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

事务的隔离级别

考虑到上述的并发事务问题,最简单粗暴的解决方案是让所有事务都串行化处理,不允许并发。这将导致数据库的效率大大降低。为了平衡数据库的效率和隔离性Isolation,我们提出了几种事务的隔离级别,也就是并发事务之间相互影响的程度。

隔离级别从低到高

  • READ Uncommitted未提交读:事务中的修改,即使未提交,对其他事务也都是可见的;本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也会导致脏读(Dirty Read)
  • Read Committed提交读:一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的;大多数数据库默认采用此级别;也就是说:一个事务只能看见已经提交事务所做的改变。但是,该级别未解决不可重复读的问题,因为同一事务的多次读数据之间,别的事务对该数据的修改也可能进行提交,导致数据不一致。
  • Repeatable Read可重复读MYSQL默认):解决了脏读问题和不可重复读问题;该级别保证了在同一事务中多次读取同样记录的结果是一致的MySQL默认为此级别;但是,此级别没有解决幻读问题,如果其它事务插入或者删除了记录。(MySQL的InnoDB引擎使用快照读的机制使得可重复读隔离级别也解决了幻读)
  • Serializable可串行化最高的隔离级别。该级别会在读取的每一行数据上都加锁,导致大量的超时和锁争用的问题。但用的比较少,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑此级别。它解决了幻读问题

√: 可能出现 ×: 不会出现

  脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

我们需要平衡数据库的安全性和效率,选择当前场景最优的事务隔离级别。

MySQL默认:可重复读Repeatable read

事务的隔离级别由低到高:

read uncommitted > read committed > repeatable read > serializable

事务的隔离级别越高,性能越差。

参考资料:

1、https://hillzhang1999.gitee.io/2020/05/29/shu-ju-ku-fu-xi-ji-yu-mysql/#toc-heading-98

2、https://www.bilibili.com/video/BV1Vt411z7wy?p=64

猜你喜欢

转载自blog.csdn.net/ProQianXiao/article/details/108352921