MySQL事务管理

cd4356
理解事务之前,先讲一个日常生活中最常干的事:转账

  从A账户中转1000块给B账户,转账这个过程必须是一个事务。否则,可能会由于各种原因而导致,A账户减少了1000块,但B账户没有相应的增加1000块,或者 B账户增加了1000块,但A账户没有减少1000块。一个步骤成功而另一个步骤失败,这样不是你用户损失就是银行损失。所以,为了保证转账前后的一致性,就必须确保转账操作是一个事务。这样,不管哪一个步骤失败了以后,就可以回滚到转账前的状态,就像这个事务从来没有执行过一样,对双方都有利。

  事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样

  在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。


cd4356
事务简介?

  • 事务一般特指数据库事务(Database Trabsaction)(事务是数据库的功能),通俗点说,就是数据库的一件事,是指一个程序执行单元执行的一系列操作。事务以结果为导向,要么完全执行,要么完全不执行。

  • 数据库内部提供回滚机制,如果事务执行过程中的某个步骤出现错误或者不执行了,那么就可以通过rollback语句,将事务还原回最初执行的哪个动作,从而保证一个事务是一个不可分割的工作单位。

  • 事务一般用来管理DML(insert、delete、update)语句,对注入DDL(create、drop、. . .)语句无效。


一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)

  • 原子性
    事务是原子工作单元,由一系列动作组成。一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

  • 一致性
    事务必须是使数据库从一个一致性状态 变成 另一个一致性状态,在事务开始之前和事务结束以后,数据库的完整性没有被破坏。

  • 隔离性
    数据库允许多个并发事务同时处理相同的数据,隔离性可以防止多个事务并发执行时,由于交叉执行而导致数据的不一致。

  • 永久性
    事务完成之后,它对数据库中数据的改变应该是永久性的(可以理解为:事务提交之前,它做出的修改保存在内存中;事务提交之后,它做出的修改保存到磁盘中),即便系统故障也不会丢失。




cd4356
MYSQL提供大量的数据库引擎,其中InnoDB 和 MyISAM是最为常见的两种引擎,我们可以通过show engines命令来查看服务器支持的数据库引擎。

MYSQL5.1之前,服务器默认的引擎是MyISAM,MYSQL5.1之后,默认的引擎是InnoDB。
cd4356
MYSQL中只有使用了InnoDB数据库引擎的数据库或表才支持事务,并且也只有InnoDB引擎才支持外键使用。如果默认引擎不是InnoDB,可以在MySQL安装目录的my.ini配置文件中修改default-storage-engine=MyISAMdefault-storage-engine=InnoDB
cd4356


MySQL事务有两个基本规则

  • MYSQL中只有使用了InnoDB数据库引擎的数据库或表才支持事务

  • MYSQL默认以自动提交(autocommit)模式运行。

自动提交(autocommit)模式,就是它会对提交到mysql服务器的任何一条sql语句封装成一个个独立的事务。这种模式会在每条SQL语句执行完毕后,立即自动进行commit提交,将它们作出的修改永久性的记入数据库,实际上就是把每条语句当作一个事务来执行。

如果想明确地执行事务,就需要使用手动提交(autocommit)模式,告诉MySQL,我想提交或回滚有关修改时,我自己来就好,就不劳你费心啦

手动提交模式又分为隐式事务模式(关闭自动提交模式)显式事务模式

  • 隐式事务模式
    • 隐式事务模式,数据库引擎实例会在提交或回滚当前事务后自动启动新事务,无需begin 或 start transaction来描述事务的开始,只需提交或回滚每个事务。隐式事务模式会导致连续的事务链生成,直至隐式事务模式关闭关闭命令行为止
      cd4356

    • 隐式事务模式,又称关闭自动提交模式,因为隐式事务模式必须需手动设置set autocommit=OFF;(默认值=ON,自动提交)或 set @@autocommit=0(默认值=1,自动提交)来关闭自动提交模式。

    • 可通过show variables like ‘%autocommit%’;select @@autocommit;命令查看MySQL客户端事务提交方式。autocommit=ON@@autocommit=1 表示自动提交
      cd4356

    • 关闭自动提交,只对当前mysql命令行窗口有效,打开一个新的命令行窗口,默认还是自动提交,除非设置永久手动提交,但我们一般不这样设置


  • 显式事务模式

    • 显式事务模式,就是可以显式地定义一个事务在何时开始、何时结束。显式事务通过下示语句来实现
      cd4356

    • 显式地开启一个事务可以用begin; 或 start transaction;语句,它们的效果是一样,仅是为了照顾不同人的书写习惯罢了。

    • 结束一个事务有commit; 和 rollback;两种类型的语句,使用commit;来结束一个事务,表示将从begin;开始到commit;之间的所有SQL语句作为一个事务,并进行提交,让该事务对数据库做的修改永久性的保存下来。

    • 如果执行rollback;语句来结束一个事务,那么代表从begin;开始,到rollback;之间执行的若干条SQL语句的结果都无效,回滚到begin;执行前的最初状态。

    • 所以,若干条SQL语句,包裹在begin;和commit;之间 或者 包裹在begin;和rollback;之间。那么MySQL就会把它当成一个原子性的流程的一件事,这就是显式事务。
      cd4356

    • 显式事务模式,它在自动提交模式 或 隐式模式下都适用。显式事务模式持续的时间只限于该事务的持续期。当事务结束时,连接将返回到启动显式事务前所处的事务模式,或者是 隐式模式,或者是 自动提交模式。
      cd4356
      cd4356


cd4356
事务并发过程由于交叉执行而产生了一系列错误问题,大致可分为下面三种情况:脏读、不可重复度、幻读

  • 脏读:读取了非永久性的数据,相当于读取了操纵的内存中的数据

    • 解决办法:不同的客户端访问服务器,那么在不同的客户端执行的事务,每一个session操纵的内存空间是独立的,所以它们就只能读取到永久性的数据,而不能读取到别的客户端的非永久性数据。也就是所谓的会话独立

  • 不可重复读:在同一个事务里面两次查询同一个数据项,期间其它事务对这条记录做了修改并提交,导致改事务前后两次得到的数据不一致

    • 解决办法:行锁,以锁行方式锁住要操作的该条记录,在改事务执行完之前,不让其它事务操作该行数据

  • 幻读:事务A按查询条件查询之前检索过的数据,事务B向事务A操纵的结果集中insert一条数据,并进行提交,导致事务A检索出来的结果集行数变多了

    • 解决办法:间隙锁,锁表方式,不让其它事务在表中各行间隙之间插入数据


谈谈不可重复读和幻读的区别?

  • 不可重复读和幻读极为相似,它们的区分是因为解决方式的不同

  • 比如事务A要操作商品表中所有的数据,那么就把表中所有的行都锁上,这样在改事务结束前,其它事务就不能对这些数据进行操作了。
    但若其它事务不是要修改修改或删除表中的数据,而是要插入一条数据呢,这样你锁住表中的数据对我没有任何影响,
    因为我不是要操作你们这些数据,我只是要在你们间隙中插入一些新记录而已,你管不着我,除非你把间隙给锁上,把表给锁上


cd4356
为解决事务并发过程产生了一系列问题,事务的隔离级别应运而生。

    read-uncommitted(读未提交),三个现象都没有解决,隔离级别最低;serializable(串行化),三个现象都解决了,隔离级别最高。隔离级别由低到高,解决的事务并发问题越来越多,到这样就会导致在处理并发事务执行时的效率越来越低。所以,在实际开发中,并不是隔离级别越高越好,而是根据实际需要来设置合理的隔离级别。

cd4356

查看默认隔离级别使用 select @@tx_isolation;命令。MySQL事务默认隔离级别为repeatable-read(读未提交)
cd4356
设置当前会话的隔离级别使用set session transaction isolation level xxx;命令。
cd4356


cd4356前提:设置隔离级别为读未提交隔离级别。
事务B,先开始执行修改语句,将’100001’号商品库存改为0,这时事务B还未结束。接着事务A,就开始执行查询语句,读取了还未永久性保存的’100001’号商品,库存为0。但后来事务B发现,修改’100001’号商品库存这条SQL语句的条件不全,于是就让事务B进行回滚了。也就是说,事务A读取到的信息是一个错误的非永久性的错误信息。所以,读取到了非永久性的错误信息,这就是脏读现象

所以,在read-uncommitted(读未提交)隔离级别中,脏读现象是存在的


开启两个命令行窗口,分别开启事务A和事务B,通过SQL语句进行脏读现象演示
cd4356



cd4356
事务A先开始执行查询语句,读取’100001’号商品库存,库存为100,事务A未结束。接着,事务B开始执行修改语句,将’100001’号商品库存改为0,并进行事务提交。然后,事务A再一次执行查询语句,读取’100001’号商品库存,结果读取到的’100001’号商品库存0。所以,在同一个事务里面两次查询同一个数据项,得到了不同的数据,这就是不可重复读现象

所以,在read-uncommitted(读未提交)隔离级别中,不可重复读现象也是存在的


开启两个命令行窗口,分别开启事务A和事务B,通过SQL语句进行不可重复读现象演示
cd4356



cd4356
事务A先开始执行,把所有商品的库存数清空,事务A未结束。接着,事务B开始执行,插入一个库存数为100的新商品’100011’ ,并进行提交修改,让这个’100011’号商品变成永久性数据。然后,事务A查询它前面修改的结果,却发现,仍然又一个商品的库存数为100,没有达到事务A预期的效果。所以,事务A按查询条件查询之前检索过的数据,事务B向事务A操纵的结果集中insert一条数据,并进行提交,导致事务A检索出来的结果集行数变多了,这就是幻读现象。

所以,在read-uncommitted(读未提交)隔离级别中,幻读现象也是存在的


开启两个命令行窗口,分别开启事务A和事务B,通过SQL语句进行幻读现象演示
cd4356
不可重复读和幻读较为相似,很多人都容易搞混。但不可重复读重点在于update 和 delete,而幻读的重点在于insert 。


前面,在read-uncommitted(读未提交)隔离级别下,分别演示了脏读、不可重复读和幻读。

有需要的,可以通过set session transaction isolation level xxx;命令,修改当前会话的隔离级别,在另外3个隔离级别中演示脏读、不可重复读和幻读的存在情况。





总结:

  • 事务隔离级别是用来解决事务并发时产生的问题。

  • 另外,不论是事务隔离级别的概念 或者是 事务隔离级别的等级划分,几乎所有的数据库都是按照这样的规则来定义的。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家师曹先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值