实际的SQL执行过程中,有时候我们生产过程需要保证某些SQL要么一起并顺序执行成功,要么一起失败回滚,不做任何操作。那么就有了事务的概念。事务是数据库中保证交易可靠的机制。
a.开始事务:BEGIN TRANSACTION(这条语句之后的sql语句将处在一个事务当中,这些sql语句并不会立即执行)
b.提交事务:COMMIT TRANSACTION(一旦提交事务,事务中的所有sql语句才会执行)
c.回滚事务:ROLLBACK TRANSACTION(回滚事务,将之前所有的sql取消)
事务的作用是保证数据的一致性、完整性。
比如银行转账过程,都是简单分三步:第一步查询付款人的账户的余额是否足够扣的金额;第二步:如果余额足够,扣付款人账户的款项金额;第三步:对收款人账户,余额加上转账的款项金额,完成付款。那么三步操作只一或两步操作成功,不是全部成功且按顺序成功的话,就可能出现付款人账户不足却扣款到0甚至负数,付款人扣款了但收款人没收到,等等多种不可遇见的异常情况。而这些异常,生产是不能够接受的。那么如果有了事务就能保证一致成功或失败,数据是完整提交或回滚。
原子性:事务是数据库最小的提交单位,事务中的操作要么都做,要么都不做
隔离性:没有commit之前,并发执行的事务之间某种程度互不干扰
一致性:数据不会因为事务的执行而遭到破坏。 (有地方少,别的地方就肯定有多的,数据不会凭空消失)
持久性:一旦提交,不可回滚
并发执行的事务之间的不干扰主要针对增删改。但是对于数据的读取、修改时加锁的处理机制,数据库处理模式产生了分歧和不同需求。因此也就有了隔离级别,即定义了事务间的可见性程度。
为什么要有事务隔离级别,因为事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大,因此很多时候必须在并发性和性能之间做一个权衡。所以设立了几种事务隔离级别,以便让不同的项目可以根据自己项目的并发情况选择合适的事务隔离级别,对于在事务隔离级别之外会产生的并发问题,在代码中做补偿。
在这之前,需要了解排他锁、共享锁的概念:
有多种策略定义事务,也就是隔离级别:
-Serializable 序列化 :对行所在的表,启用排他锁,阻止其他事务读、写和修改;
-Repeatable read 重复读 :对行记录,修改时启用排他锁(阻止其他事务读写),事务提交才释放锁;读取时启用共享锁(不阻止其他事务读,阻止其他事务写),事务提交才释放锁。
-Read committed 读提交 :对行记录,修改时启用排他锁(阻止其他事务读写),事务提交才释放锁;读取时启用共享锁(不阻止其他事务读,阻止其他事务写),读完即释放锁。
-READ UNCOMMITTED(未提交读) :不加锁,允许其他事务任意读、写和修改;
在四种隔离级别中, Serializable的级别最高, Read Uncommited级别最低.
大部分数据库的默认隔离级别为: Read Commited,如Sql Server , Oracle.
其他数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎
脏读:读取到尚未提交发生的过程数据,而这个数据记录有可能回滚。
不可重复读:同样的条件下,某一条的数据记录在事务中不能重复读取,也就是每次读取的结果可能会不一致。
发生幻读: 主要针对某一范围的数据记录而言。同样的条件下,某一范围的数据记录在事务中检索的记录数可能会不一致。
-Read committed 读提交 :因修改时启用排他锁,事务提交才释放,因此如果有事务A正在修改数据,其他事务如B是不能读取到,只有事务A提交或回滚才能读取,如此避免“脏读“问题。而因读取数据的时候,是读完即释放共享锁的,事务A中读取一次后,刚好事务B去修改并提交,事务A这时候再读取,数据可能就不一致了,也就是发生“不可重复读”问题。
-Repeatable read 重复读 :根据以上的场景,很容易,如果读取启用的共享锁,在事务提交才释放的话,那么每个事务只能等别人提交后才能读取,也就避免了“不可重复读”问题。例如第一个事务对一个表中的数据进行了修改, 这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行, 就好象发生了幻觉一样,也就是发生“幻读”问题。
-Repeatable read 重复读 +范围锁:针对某一记录仍然是Repeatable read 重复读 的规则。针对范围的记录, 如果启用范围锁,修改时启用范围排他,事务提交才释放,读取启用范围共享,读完即释放。那么就可以避免了“幻读”问题。
-Serializable 序列化 :针对行所在的表启用排他锁,所有事务都是串行操作,没有脏读、不可重复读、幻读问题。当然性能也是最差的,一般不采用。