赞
踩
理解事务之前,先讲一个日常生活中最常干的事:转账
从A账户中转1000块给B账户,转账这个过程必须是一个事务。否则,可能会由于各种原因而导致,A账户减少了1000块,但B账户没有相应的增加1000块,或者 B账户增加了1000块,但A账户没有减少1000块。一个步骤成功而另一个步骤失败,这样不是你用户损失就是银行损失。所以,为了保证转账前后的一致性,就必须确保转账操作是一个事务。这样,不管哪一个步骤失败了以后,就可以回滚到转账前的状态,就像这个事务从来没有执行过一样,对双方都有利。
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。
在企业级应用程序开发中,事务管理必不可少的技术,用来确保数据的完整性和一致性。
事务简介?
事务一般特指数据库事务(Database Trabsaction)(事务是数据库的功能),通俗点说,就是数据库的一件事,是指一个程序执行单元执行的一系列操作。事务以结果为导向,要么完全执行,要么完全不执行。
数据库内部提供回滚机制,如果事务执行过程中的某个步骤出现错误或者不执行了,那么就可以通过rollback语句,将事务还原回最初执行的哪个动作,从而保证一个事务是一个不可分割的工作单位。
事务一般用来管理DML(insert、delete、update)语句,对注入DDL(create、drop、. . .)语句无效。
一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性
事务是原子工作单元,由一系列动作组成。一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性
事务必须是使数据库从一个一致性状态 变成 另一个一致性状态,在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
隔离性
数据库允许多个并发事务同时处理相同的数据,隔离性可以防止多个事务并发执行时,由于交叉执行而导致数据的不一致。
永久性
事务完成之后,它对数据库中数据的改变应该是永久性的(可以理解为:事务提交之前,它做出的修改保存在内存中;事务提交之后,它做出的修改保存到磁盘中),即便系统故障也不会丢失。
MYSQL提供大量的数据库引擎,其中InnoDB 和 MyISAM是最为常见的两种引擎,我们可以通过show engines命令来查看服务器支持的数据库引擎。
MYSQL5.1之前,服务器默认的引擎是MyISAM,MYSQL5.1之后,默认的引擎是InnoDB。
MYSQL中只有使用了InnoDB数据库引擎的数据库或表才支持事务,并且也只有InnoDB引擎才支持外键使用。如果默认引擎不是InnoDB,可以在MySQL安装目录的my.ini配置文件中修改default-storage-engine=MyISAM 为 default-storage-engine=InnoDB
MySQL事务有两个基本规则
MYSQL中只有使用了InnoDB数据库引擎的数据库或表才支持事务
MYSQL默认以自动提交(autocommit)模式运行。
自动提交(autocommit)模式,就是它会对提交到mysql服务器的任何一条sql语句封装成一个个独立的事务。这种模式会在每条SQL语句执行完毕后,立即自动进行commit提交,将它们作出的修改永久性的记入数据库,实际上就是把每条语句当作一个事务来执行。
如果想明确地执行事务,就需要使用手动提交(autocommit)模式,告诉MySQL,我想提交或回滚有关修改时,我自己来就好,就不劳你费心啦
手动提交模式又分为隐式事务模式(关闭自动提交模式) 和 显式事务模式
隐式事务模式,数据库引擎实例会在提交或回滚当前事务后自动启动新事务,无需begin 或 start transaction来描述事务的开始,只需提交或回滚每个事务。隐式事务模式会导致连续的事务链生成,直至隐式事务模式关闭 或 关闭命令行为止
隐式事务模式,又称关闭自动提交模式,因为隐式事务模式必须需手动设置set autocommit=OFF;(默认值=ON,自动提交)或 set @@autocommit=0(默认值=1,自动提交)来关闭自动提交模式。
可通过show variables like ‘%autocommit%’; 或 select @@autocommit;命令查看MySQL客户端事务提交方式。autocommit=ON 或 @@autocommit=1 表示自动提交
关闭自动提交,只对当前mysql命令行窗口有效,打开一个新的命令行窗口,默认还是自动提交,除非设置永久手动提交,但我们一般不这样设置
显式事务模式
显式事务模式,就是可以显式地定义一个事务在何时开始、何时结束。显式事务通过下示语句来实现
显式地开启一个事务可以用begin; 或 start transaction;语句,它们的效果是一样,仅是为了照顾不同人的书写习惯罢了。
结束一个事务有commit; 和 rollback;两种类型的语句,使用commit;来结束一个事务,表示将从begin;开始到commit;之间的所有SQL语句作为一个事务,并进行提交,让该事务对数据库做的修改永久性的保存下来。
如果执行rollback;语句来结束一个事务,那么代表从begin;开始,到rollback;之间执行的若干条SQL语句的结果都无效,回滚到begin;执行前的最初状态。
所以,若干条SQL语句,包裹在begin;和commit;之间 或者 包裹在begin;和rollback;之间。那么MySQL就会把它当成一个原子性的流程的一件事,这就是显式事务。
显式事务模式,它在自动提交模式 或 隐式模式下都适用。显式事务模式持续的时间只限于该事务的持续期。当事务结束时,连接将返回到启动显式事务前所处的事务模式,或者是 隐式模式,或者是 自动提交模式。
事务并发过程由于交叉执行而产生了一系列错误问题,大致可分为下面三种情况:脏读、不可重复度、幻读
脏读:读取了非永久性的数据,相当于读取了操纵的内存中的数据
不可重复读:在同一个事务里面两次查询同一个数据项,期间其它事务对这条记录做了修改并提交,导致改事务前后两次得到的数据不一致
幻读:事务A按查询条件查询之前检索过的数据,事务B向事务A操纵的结果集中insert一条数据,并进行提交,导致事务A检索出来的结果集行数变多了
谈谈不可重复读和幻读的区别?
不可重复读和幻读极为相似,它们的区分是因为解决方式的不同
比如事务A要操作商品表中所有的数据,那么就把表中所有的行都锁上,这样在改事务结束前,其它事务就不能对这些数据进行操作了。
但若其它事务不是要修改修改或删除表中的数据,而是要插入一条数据呢,这样你锁住表中的数据对我没有任何影响,
因为我不是要操作你们这些数据,我只是要在你们间隙中插入一些新记录而已,你管不着我,除非你把间隙给锁上,把表给锁上
为解决事务并发过程产生了一系列问题,事务的隔离级别应运而生。
read-uncommitted(读未提交),三个现象都没有解决,隔离级别最低;serializable(串行化),三个现象都解决了,隔离级别最高。隔离级别由低到高,解决的事务并发问题越来越多,到这样就会导致在处理并发事务执行时的效率越来越低。所以,在实际开发中,并不是隔离级别越高越好,而是根据实际需要来设置合理的隔离级别。
查看默认隔离级别使用 select @@tx_isolation;命令。MySQL事务默认隔离级别为repeatable-read(读未提交)。
设置当前会话的隔离级别使用set session transaction isolation level xxx;命令。
前提:设置隔离级别为读未提交隔离级别。
事务B,先开始执行修改语句,将’100001’号商品库存改为0,这时事务B还未结束。接着事务A,就开始执行查询语句,读取了还未永久性保存的’100001’号商品,库存为0。但后来事务B发现,修改’100001’号商品库存这条SQL语句的条件不全,于是就让事务B进行回滚了。也就是说,事务A读取到的信息是一个错误的非永久性的错误信息。所以,读取到了非永久性的错误信息,这就是脏读现象。
所以,在read-uncommitted(读未提交)隔离级别中,脏读现象是存在的。
开启两个命令行窗口,分别开启事务A和事务B,通过SQL语句进行脏读现象演示
事务A先开始执行查询语句,读取’100001’号商品库存,库存为100,事务A未结束。接着,事务B开始执行修改语句,将’100001’号商品库存改为0,并进行事务提交。然后,事务A再一次执行查询语句,读取’100001’号商品库存,结果读取到的’100001’号商品库存0。所以,在同一个事务里面两次查询同一个数据项,得到了不同的数据,这就是不可重复读现象。
所以,在read-uncommitted(读未提交)隔离级别中,不可重复读现象也是存在的。
开启两个命令行窗口,分别开启事务A和事务B,通过SQL语句进行不可重复读现象演示
事务A先开始执行,把所有商品的库存数清空,事务A未结束。接着,事务B开始执行,插入一个库存数为100的新商品’100011’ ,并进行提交修改,让这个’100011’号商品变成永久性数据。然后,事务A查询它前面修改的结果,却发现,仍然又一个商品的库存数为100,没有达到事务A预期的效果。所以,事务A按查询条件查询之前检索过的数据,事务B向事务A操纵的结果集中insert一条数据,并进行提交,导致事务A检索出来的结果集行数变多了,这就是幻读现象。
所以,在read-uncommitted(读未提交)隔离级别中,幻读现象也是存在的。
开启两个命令行窗口,分别开启事务A和事务B,通过SQL语句进行幻读现象演示
不可重复读和幻读较为相似,很多人都容易搞混。但不可重复读重点在于update 和 delete,而幻读的重点在于insert 。
前面,在read-uncommitted(读未提交)隔离级别下,分别演示了脏读、不可重复读和幻读。
有需要的,可以通过set session transaction isolation level xxx;命令,修改当前会话的隔离级别,在另外3个隔离级别中演示脏读、不可重复读和幻读的存在情况。
总结:
事务隔离级别是用来解决事务并发时产生的问题。
另外,不论是事务隔离级别的概念 或者是 事务隔离级别的等级划分,几乎所有的数据库都是按照这样的规则来定义的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。