事务

事务

      事务由单独单元的一个或多个 SQL 语句组成,在这个单元中,每个 MySQL 语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条 SQL 语句一旦执行失败或产生错误,整个单元将会回滚。所有受到影响的数据将返回到事物开始以前的状态;如果单元中的所有 SQL 语句均执行成功,则事物被顺利执行。

InnoDB的事务

      严格上来说,事务必须同时满足 4 个特性,即通常所说事务的 ACID 特性。虽然理论上定义了严格的事务要求,但是数据库厂商出于各种目的并没有严格满足事务的 ACID 标准。例如,对于 MYSQL 的 NDB Cluster 引擎,虽然支持事务,但是不满足 D 的要求,即持久性的要求。对于 Oracle 数据库来说,其默认的事务隔离级别为 READ COMMITTED,不满足 I 的要求,即隔离性的要求。对于 InnoDB 存储引擎而言,默认的事务隔离级别是 READ REPRATABLE,完全遵循和满足事务的 ACID 特性

事务的ACID

  1. A(atomicity) 原子性。一个事务的执行被视为一个不可分割的最小单元。事务里面的操作,要么全部成功执行,要么全部失败回滚,不可以只执行其中的一部分。
  2. C(consistency) 一致性。一个事务的执行不应该破坏数据库的完整性约束。如果上述例子中第 2 个操作执行后系统崩溃,保证 A 和 B 的金钱总计是不会变的。
  3. I(isolation) 隔离性。通常来说,事务之间的行为不应该互相影响。然而实际情况中,事务相互影响的程度受到隔离级别的影响。文章后面会详述。
  4. D(durability) 持久性。事务提交之后,需要将提交的事务持久化到磁盘。即使系统崩溃,提交的数据也不应该丢失。

事务不隔离产生的问题

      1)第一类丢失更新:在没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了。

例如:
      张三的工资为 5000,事务 A 中获取工资为 5000,事务 B 获取工资为 5000,汇入 100,并提交数据库,工资变为 5100。
      随后事务 A 发生异常,回滚了,恢复张三的工资为 5000,这样就导致事务 B 的更新丢失了。

      2)脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

例如:
  张三的工资为 5000,事务 A 中把他的工资改为 8000,但事务 A 尚未提交。
  与此同时,事务 B 正在读取张三的工资,读取到张三的工资为 8000。
  随后,事务 A 发生异常,而回滚了事务。张三的工资又回滚为 5000。
  最后,事务 B 读取到的张三工资为 8000 的数据即为脏数据,事务 B 做了一次脏读。

      3)不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。

例如:
  在事务 A 中,读取到张三的工资为 5000,操作没有完成,事务还没提交。
  与此同时,事务 B 把张三的工资改为 8000,并提交了事务。
  随后,在事务 A 中,再次读取张三的工资,此时工资变为 8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。

      4)第二类丢失更新:不可重复读的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。

例如:
       在事务 A 中,读取到张三的存款为 5000,操作没有完成,事务还没提交。
  与此同时,事务 B,存储 1000,把张三的存款改为 6000,并提交了事务。
  随后,在事务 A 中,存储 500,把张三的存款改为 5500,并提交了事务,这样事务 A 的更新覆盖了事务 B 的更新。

      5)幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

例如:
  目前工资为 5000 的员工有 10 人,事务 A 读取所有工资为 5000 的人数为 10 人。
  此时,事务 B 插入一条工资也为 5000 的记录。
  这时,事务 A 再次读取工资为 5000 的员工,记录为 11 人。此时产生了幻读。

提醒:
      不可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样

隔离级别

  1. READ UNCOMMITTED(未提交读)。在 RU 的隔离级别下,事务 A 对数据做的修改,即使没有提交,对于事务 B 来说也是可见的,这种问题叫脏读。这是隔离程度较低的一种隔离级别,在实际运用中会引起很多问题,因此一般不常用。

  2. READ COMMITTED(提交读)。在 RC 的隔离级别下,不会出现脏读的问题。事务 A 对数据做的修改,提交之后会对事务 B 可见,举例,事务 B 开启时读到数据 1,接下来事务 A 开启,把这个数据改成 2,提交,B 再次读取这个数据,会读到最新的数据 2。在 RC 的隔离级别下,会出现不可重复读的问题。这个隔离级别是许多数据库的默认隔离级别。

  3. REPEATABLE READ(可重复读)。在 RR 的隔离级别下,不会出现不可重复读的问题。事务 A 对数据做的修改,提交之后,对于先于事务 A 开启的事务是不可见的。举例,事务 B 开启时读到数据 1,接下来事务 A 开启,把这个数据改成 2,提交,B 再次读取这个数据,仍然只能读到 1。在 RR 的隔离级别下,会出现幻读的问题。幻读的意思是,当某个事务在读取某个范围内的值的时候,另外一个事务在这个范围内插入了新记录,那么之前的事务再次读取这个范围的值,会读取到新插入的数据。Mysql 默认的隔离级别是 RR,然而 mysql 的 innoDB 引擎间隙锁成功解决了幻读的问题。

  4. SERIALIZABLE(可串行化)。可串行化是最高的隔离级别。这种隔离级别强制要求所有事物串行执行,在这种隔离级别下,读取的每行数据都加锁,会导致大量的锁征用问题,性能最差。
    查询默认的隔离级别 :select @@tx_isolation;
    在这里插入图片描述
          第一个小人,可能读到 1-20 之间的任何一个。因为未提交读的隔离级别下,其他事务对数据的修改也是对当前事务可见的。第二个小人可能读到 1,10 和 20,他只能读到其他事务已经提交了的数据。第三个小人读到的数据去决于自身事务开启的时间点。在事务开启时,读到的是多少,那么在事务提交之前读到的值就是多少。第四个小人,只有在 A end 到 B start 之间开启,才有可能读到数据,而在事务 A 和事务 B 执行的期间是读不到数据的。因为第四小人读数据是需要加锁的,事务 A 和 B 执行期间,会占用数据的写锁,导致第四个小人等待锁。

隔离级别脏读不可重复读幻读
读未提交(Read uncommitted)VVV
读已提交( Read committed )XVV
可重复读(Repeatable read)XXV
可串行化(Serializable )XXX

      事务的实现是基于数据库的存储引擎。不同的存储引擎对事务的支持程度不一样。mysql 中支持事务的存储引擎有 innoDB 和 NDB。innoDB 是 mysql 默认的存储引擎,默认的隔离级别是 RR,并且在 RR 的隔离级别下更进一步,通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。因此 innoDB 的 RR 3 隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。
      事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值