事务介绍和使用
事务是指将一系列的数据操作捆绑成为一个整体进统一的管理。如果某一事务执行成功,则在该事务中进行所有数据更改均会提交,成为数据库中永久的部分。如果事务执行时遇到错误且必须取消或返回,则数据将全部恢复到操作前的状态,所有数据的更改均被清除。
什么是事务
事务是一种机制,一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行。因此事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时,事务是作为最小的控制单元来使用的,特别适用于多用户同时操作数据库系统。
事务作为单个逻辑工作单元执行一系列的操作。一个逻辑单元必须有四个属性,即原子性,一致性,隔离性,持久性。这些特性简称为ACID。
1. 原子性(Atomic)
事务是一个完整的操作。
一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。
回滚实际上是一个比较高层抽象的概念,大多数DB在实现事务时,是在事务操作的数据快照上进行的(比如,MVCC),并不修改实际的数据,如果有错并不会提交,所以很自然的支持回滚。
而在其他支持简单事务的系统中,不会在快照上更新,而直接操作实际数据。可以先预演一边所有要执行的操作,如果失败则这些操作不会被执行,通过这种方式很简单的实现了原子性。
2. 一致性(Consistency)
当事务完成时,数据必须处于同一状态。
一致性是指事务使得系统从一个一致的状态转换到另一个一致状态。事务的一致性决定了一个系统设计和实现的复杂度,也导致了事务的不同隔离级别。
事务可以不同程度的一致性:
**强一致性:**读操作可以立即读到提交的更新操作。
**弱一致性:**提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口,指的是读操作可以读到最新值的一段时间。
**最终一致性:**是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。
其他一致性变体还有:
**单调一致性:**如果一个进程已经读到一个值,那么后续不会读到更早的值。
**会话一致性:**保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。
3. 隔离性(Isolation)
对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的。
并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。在事务并发操作时,可能出现的问题有:
**脏读:**事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
**不可重复读:**在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
**幻读:**在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。
事务的隔离级别从低到高有:
**Read Uncommitted:**最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。
**Read Committed:**只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
**Repeated Read:**在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。
**Serialization:**事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
通常,在工程实践中,为了性能的考虑会对隔离性进行折中。
总之:Read Uncommitted 一个事务可以读取另一个事务未提交的数据。
Read Committed 一个事务等待另一个事务提交后才能查询(脏数据)
Repeated Read 事务开启后不允许另一个修改
Serialization 串行化执行
4. 持久性(Durability)
事务提交后,不管系统是否发生了故障,事务处理的结果都是永久的。
处理事务
Mysql中提供了多种存储引擎支持事务操作,主要有InnoDB和BDB。InnoBD存储引擎事务主要是通过UNDO日志和REDO日志实现的,MyISAM存储引擎不支持事务操作。
UNDO日志:复制事务执行前的数据,用于在事务发生异常是回滚数据。
REDO日志:记录在事务执行中,每条对数据进行更新的操作,当事务提交时,该内容将被刷新到磁盘。
默认设置下,每一句sql语句就是一个事务,执行sql后会自动提交。为了达到将几个操作作为一个整体的目的,需要使用BEGIN或者START TRANSACTION开启一个事务,或执行命令SET AUTOMCOMMIT=0,来禁止当前会话的自动提交后,后面的语句作为事务的开始。
执行事务的语法
1)开始事务。
BEGIN;
或者
START TRANSACTION;
这个语句显示地标记一个事务的起始点。
2)提交事务
语法:
COMMIT;
这个语句标志一个事务成功提交。自事务开始至提交语句之间执行的所有数据更新的操作将永久的保存在数据库数据文件中,并释放连接时占用的资源。
3)事务回滚
ROLLBACK;
清除自事务起始点值该语句所做的所有数据更新操作,将数据状态恢复到事务开始之前,并释放由事务控制的资源。
BEGIN或START TRANCTION 语句后面的sql语句对数据的更新操作都将记录在事务日志中,直到遇到ROLLBACK或者COMMIT语句。如果事务某一操作失败了且执行了ROLLBACK语句,那么再开始事务语句之后所有更新的数据都能回滚到事物开始前的状态。如果事务中的所有操作都全部正确完成,并且使用了COMMIT语句向数据库提交更新数据,则此时的数据又处在新的一致状态。
设置自动提交关闭或者开启
MySQL中的默认开启自动提交模式,即未指定开启事务时,每条SQL语句都是单独的事务执行完毕自动提交。可以关闭自动提交模式,手动提交或回滚事务。
语法:
SET autocommit=0|1;
值0 :关闭自动提交;
值1:开启自动提交;
当执行SET autocommit=0后,即关闭自动提交,从下一条语句开始新事务,需使用COMMIT或者ROLLBACK语句结束该事物。
编写事务要注意的原则:
1.事务要尽可能简短。
2.事务中的访问数量尽量最少。
3.查询数据时尽量不要使用事务。
4.在事务处理过程中尽量不要出现等待用户输入的操作。