您的位置:永利集团登录网址 > 永利集团登录网址 > mysql事务

mysql事务

2019-10-03 19:32

以下内容出自《高性能MySQL》第三版,了解事务的ACID及四种隔离级有助于我们更好的理解事务运作。

最近买了《高性能MySQL》这本书回来看,从中收益颇多!我来一吐为快!

  1. 事务并不专属于mysql

  2. 事务的ACID特性

下面举一个银行应用是解释事务必要性的一个经典例子。假如一个银行的数据库有两张表:支票表(checking)和储蓄表(savings)。现在要从用户Jane的支票账户转移200美元到她的储蓄账户,那么至少需要三个步骤:

我们都知道事务,那么在什么情况下我们需要使用事务呢?

  1)原子性(atomicity)  

1、检查支票账户的余额高于或者等于200美元。

银行应用是解释事务的一个经典例子。假设一个银行的数据库有两张表:支票(checking)和储蓄(savings)表。现在johnson要从支票账户中转移200块大洋到储蓄表中,那么至少需要三个步骤:

  一个事务必须被视为一个不可分割的最小工作单元,整个事务中得所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来讲,不可能只执行其中的一部分

2、从支票账户余额中减去200美元。

  1. 检查支票账户余额是否高于200块大洋
  2. 支票账户减少200块大洋
  3. 储蓄账户中增加200块大洋

      操作,这就是事务的原子性。

3、在储蓄帐户余额中增加200美元。

试想一下,如果上面步骤执行到第二步,突然因为什么原因而终止了,顾客支票账户中莫名其妙的减少了200块大洋。如果顾客恰好是一位情绪激动的大妈,那你就等着大妈带着平底锅和四级头去银行找你吧!

  2)一致性(consistency)

上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。

所以为了避免这种情况,就必须用到事务,上述三个步骤中有任何一个执行失败,就必须回滚所有的步骤,以免有大妈找上门。事务SQL如下所示:

  数据库的总是从一个一致性的状态转换到另一个一致性的状态。

 

  1. START TRANSACTION;
  2. SELECT balance FROM checking WHERE customer_id=123456;
  3. UPDATE checking SET balance = balance - 200 WHERE customer_id=123456;
  4. UPDATE savings SET balance = balance + 200 WHERE customer_id=123456;
  5. COMMIT;

  3)隔离性(isolation)

可以用START TRANSACTION语句开始一个事务,然后要么使用COMMIT提交将修改的数据持久保存,要么使用ROLLBACK撤销所有的修改。事务SQL的样本如下:

事务之所以可靠,当然离不开ACID特性:

  通常来说,一个事务所做的修改在最终提交之前,对其他事务是不可见的。

  1. start transaction;

  2. select balance from checking where customer_id = 10233276;

  3. update checking set balance = balance - 200.00 where customer_id = 10233276;

  4. update savings set balance = balance + 200.00 where customer_id = 10233276;

  5. commit;

  • 原子性(atomicity):整个事务中的操作要么全部成功,要么全部失败。
  • 一致性(consistency):数据库总是从一个一致性状态转换到另一个一致性状态。比如上面所说的,事务开始前和执行后,顾客johnson在银行的总账户余额是一样的。
  • 隔离性(isolation):通常来说,一个事务所做的修改在提交之前,其他事务是不可见的。也就是说事务间是相互隔离的。
  • 持久性(durability):事务在提交之后,对数据库数据所做的修改是永久性的。

  4)持久性(durability)

 

细心的人可能会注意到。在讨论隔离性的时候,我用了“通常来说”,下面就让我们讨论下事务的隔离级别。

  一旦事务提交,则其所做的修改就会永久保存到数据库中。

ACID表示原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。一个很好的事务处理系统,必须具备这些标准特性:

隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读
READ UNCOMMITTED YES YES YES NO
READ COMMITTED NO YES YES NO
REPEATABLE READ NO NO YES NO
SERIALIZABLE NO NO NO YES
  1. 隔离级别

 

 

  1)READ UNCOMMITTED(未提交读)

原子性(atomicity)

  • 未提交读(READ UNCOMMITTED):事务中的修改,即使没有提交,其他事务也可以读到,这就有可能造成了脏读。
  • 提交读(READ COMMITTED):大多数数据库系统默认实用的隔离级别就是这种,但mysql不是。READ COMMITTED就是在事务提交前,所做的修改对其他事务是不可见的。但READ COMMITTED可能会造成不可重复读。就是在一个事务中,同样的查询语句,可能会得到不一样的结果。其实就是在两次查询中间,另一个事务修改了查询结果的值。
  • 可重复读(REPETABLE READ):REPETABLE READ解决了脏读和不可重复读的问题,但理论上,REPETABLE READ无法解决幻读的问题。幻读就是指,一个事务在读取某一范围的值时,另一个事务恰好在该范围内插入了新纪录,那么当你再次读取该范围的值时,就会产生幻行。这与不可重复读有点像,只不过不可重复读时UPDATE,而幻读时INSERT
  • 可串行化(SERIALIZABLE):SERIALIZABLE读取每一行数据都要加锁,强制事务串行执行,所以可能导致大量的超时和锁争用问题。

    在该级别,事务中得修改,即使没提交,对其他事务也是可见的。

  一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性

到这里,如果还不是太懂,你需要细细消化下前面的内容,这时可以打开mysql,将隔离级别设置为READ COMMITTED。然后试试它是不是解决了脏读,会不会出现不可重复读?再将隔离级别设置为REPETABLE READ。看看REPETABLE READ是不是解决了不可重复读,会不会出现幻读?

  2)READ COMMITTED(提交读)

一致性(consistency)

SET session transaction isolation level read committed;

    在提交之前对其他事务都是不可见的

     数据库总是从一个一致性的状态转换到另一个一致性的状态。(在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,支票账户中也不会损失200美元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。)

如果你真的实验了,会发现mysql的REPETABLE READ隔离级别并不会出现幻读的现象。那你有没有想过mysql的事务是怎么实现的呢?

  3)REPEATABLE READ(可重复读)[mysql默认的事务隔离级别]

隔离性(isolation)

你肯定听说mysql的表锁和行锁,那你可能以为事务是基于行锁实现的。其实并没有那么简单,为了提高并发性能,mysql的大多是事务引擎都同时实现了多版本并发控制(MVCC)。它在很多情况下避免了加锁操作,所以开销更低。MVCC大都实现了非阻塞的读操作,写操作也只锁定必要的行。

    该级别保证了在同一个事务中多次读取同样的记录的结果是一致的。

     通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。(在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时有另外的一个账户汇总程序开始运行,则其看到支票帐户的余额并没有被减去200美元。)

那么InnoDB中的MVCC是如何工作的呢?其实是通过在每行数据后面增加两个列,一个是创建版本号,一个是删除版本号。里面存储的是系统版本号,你开启一个事务系统版本号就会递增。事务开始时刻的系统版本号就作为事务版本号,用来和查询的每行记录的版本号做比较。下面看下REPETABLE READ隔离界别下,MVCC具体是如何操作的。

    但是会产生幻读得问题

持久性(durability)

  • SELECT查询出的数据需要满足2个条件    1、创建版本号 <= 系统版本号  2、删除版本号为空或删除版本号>系统版本号
  • INSERT     为新插入的每一行保存当前事务版本号为行的创建版本号
  • UPDATE  为插入的一行新记录保存当前事务版本号为行的创建版本号,同时保存当前事务版本号为原来的行的删除版本号
  • DELETE  为删除的每一行保存当前事务版本号为行的删除版本号

  4)SERIALIZABLE(可串行化)

  一旦事务提交,则其所做的修改不会永久保存到数据库。(此时即使系统崩溃,修改的数据也不会丢失。持久性是个有占模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必,而且不可能有能做到100%的持久性保证的策略。)

保存这两个额外的系统版本号,可以使大多数读操作都不用加锁,这样性能就会更好。但需要额外的存储空间和一些额外的检查工作,这也相当于用空间换时间。

    该级别会在读取的每一行数据上都加锁

 

在某些情况下我们还是需要用的锁。InnoDB采用两段锁协议。在事务执行过程中随时都可以加锁,事务提交或回滚时同时释放所有锁。这些锁一般都是隐式锁定,InnoDB会根据需要自动加锁。当然,你也可以通过SQL语句自己加锁:

 

隔离级别:

SELECT ..... LOCK IN SHARE MODE;     乐观锁
SELECT ..... FOR UPDATE;             悲观锁
隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读
READ UNCOMMITED Yes Yes Yes No
READ COMMITTED No Yes Yes No
REPEATABLE READ No No Yes No
SERIALIZABLE No No No Yes

READ UNCOMMITTED(未提交读)

个人建议,除非你明确知道自己在干什么,否则轻易不要显式加锁,只会事倍功半!!!

 

  在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。

 

READ COMMITTED(提交读)

 

  大多数数据库系统的默认隔离级别都是READ COMMTTED(但MySQL不是)。READ COMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能"看见"已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候叫做不可重复读(nonrepeatble read),因为两次执行同样的查询,可能会得到不一样的结果

 

REPEATABLE READ(可重复读)

 

  REPEATABLE READ解决了脏读的问题。该隔离级别保证了在同一个事务中多次读取同样记录结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(Phantom Row)。InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读的问题。

 

SERIALIZABLE(可串行化)

4.死锁产生的原因

  SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,SERIALIZABLE会在读取每一行数据都加锁,所以可能导致大量的超时和锁争用问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

  1)因为真正的数据冲突

打钩说明该隔离级别还存在这种情况,打X代表该隔离级别已经解决了这种情况:

  2)存储引擎的实现方式

 图片 1

5.mysql 提供了两种事物型的引擎:Innodb 、NDB Cluster

 

6.多版本并发控制(MVCC)

作者:陆炫志

出处:xuanzhi的博客 http://www.cnblogs.com/xuanzhi201111

您的支持是对博主最大的鼓励,感谢您的认真阅读。本文版权归作者所有,欢迎转载,但请保留该声明。

 

本文由永利集团登录网址发布于永利集团登录网址,转载请注明出处:mysql事务

关键词:

  • 上一篇:没有了
  • 下一篇:没有了