前言
我们知道数据库事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)四个特性,简称 ACID。今天就简单说一下事务的四种特性
事务的特性
原子性
事务的原子性保证事务包含的一组更新操作是原子不可分的,也就是说这些更新操作是
一个整体,对数据库而言全做或者全不做,不能部分地完成。这一性质即使在系统崩溃之后仍能得到保证,在系统崩溃之后将进行数据库恢复,用来恢复和撤销系统崩溃时处于活动状态的事务对数据库的影响,从而保证事务的原子性。系统对磁盘上的任何实际数据的修改之前都会将修改操作本身的信息记录到磁盘上。当发生崩溃时,系统能根据这些操作记录当时该事务处于何种状态,以此确定是撤销该事务所做出的所有修改操作,还是将修改的操作重新执行。
在数据库不自动提交的情况下,举例简单说明:
--创建表
create table test_uncommitted(id int, info varchar);
--插入数据,同时数据库自动打开事务,只有进行commit或者rollback后才会结束事务
insert into test_uncommitted values (1, 'test');
insert into test_uncommitted values (2, 'test1');
insert into test_uncommitted values (3, 'test2');
--执行commmit或rollback,对整个事务进行提交或者回滚,结束事务,来确保事务的原子性
commit;
rollback;
一致性
数据一致性是指表示客观世界同一事务状态的数据,不管出现在何时何处都是一致的、
正确的、完整的。换句话说,数据一致性是任何点上保证数据以及内部数据结构的完整性,如 B 树索引的正确性。
一致性要求事务执行完成后,将数据库从一个一致状态转变到另一个一致状态。它是一
种以一致性规则为基础的逻辑属性,例如在转账的操作中,各账户金额必须平衡,这一条规则对于程序员而言是一个强制的规定。事务的一致性属性要求事务在并发执行的情况下事务的一致性仍然满足。
举例简单说明:
--创建用户表
create table user_balance(name varchar(10),balance int);
--模拟插入数据,并提交
insert into user_balance values ('zhangsan', 500);
insert into user_balance values ('lisi', 0);
commit;
--模拟转账业务,zhangsan向lisi转账100元,说明事务一致性
--首先zhangsan账户应该减去100元
update user_balance set balance=400 where name='zhangsan';
--同时lisi账户应该增加100元
update user_balance set balance=100 where name='lisi';
commit;
--只有同时满足两个条件才能保证事务的一致性,金额总体不变,说明事务的一致性
隔离性
事务是隔离的,意味着每个事务的执行效果与系统中只有该事务的执行效果一样,也就
是说,某个并发事务所做的修改必须与任何其他的并发事务所做的修改相互隔离。这样,只有当某个值被一个事务修改完并提交后才会影响到另一个事务。事务只会识别另一并发事务修改之前或者修改完成之后的数据,不会识别处于这中间状态的数据。事务的隔离行为依赖于指定的隔离级别。
在关系型数据库中,事务的隔离性分为四个隔离级别,在解读这四个级别前先介绍几个
关于读数据的概念。
脏读(DirtyRead)
所谓脏读就是对脏数据的读取,而脏数据所指的就是未提交的已修改数据。也就是说,
一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被称为脏读。如果一个事务在提交操作结果之前,另一个事务可以看到该结果,就会发生脏读。
脏读测试
--准备测试数据
create table test_uncommitted(id int, info varchar);
insert into test_uncommitted values (1, 'test');
commit;
--设置读未提交
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
--本次测试需要两个或两个以上事务
--事务一,进行数据验证
select * from test_uncommitted;
ID INFO
1 test
--事务二,执行如下语句但不提交
update test_uncommitted set info='new' where id=1;
--事务一,查询同一张表,脏读出现(事务二并没有提交,但是事务一可以看到被事务二修改的后的数据)
select * from test_uncommitted;
ID INFO
1 new
不可重复读(Non-RepeatableRead)
一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。如果
一个事务在读取了一条记录后,另一个事务修改了这条记录并且提交了事务,再次读取记录时如果获取到的是修改后的数据,这就发生了不可重复读情况。
不可重复读测试
--准备测试数据
create table test_unrepeatable(id int, info varchar);
insert into test_unrepeatable values (1, 'test');
commit;
--设置读已提交
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
--本次测试需要两个或两个以上事务
--事务一,进行数据验证
select * from test_unrepeatable where id=1;
ID INFO
1 test
--事务二,执行如下语句
update test_unrepeatable set info='new' where id=1;
commit;
--事务一,根据同一个条件查询同一张表,不可重复读出现(在事物一中,不同时间相同的查询条件,查询出来的数据是不一样的,因为被事务二修改,所以导致不可重复读出现)
select * from test_unrepeatable where id=1;
ID INFO
1 new
幻像读(PhantomRead)
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其
查询条件的新数据,这种现象就称为幻像读。
幻行读测试
--准备测试数据
create table test_phantom(id int, info varchar);
insert into test_phantom values (1, 'test');
commit;
--设置可重复读
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
--本次测试需要两个或两个以上事务
--事务一,进行数据验证
select * from test_phantom;
ID INFO
1 test
--事务二,执行如下语句
insert into test_phantom values (2, 'new');
commit;
--事务一,查询同一张表,幻读出现(在同一个事务一中, 再次执行同样的SQL, 得到的结果可能不一致。通常指数据插入(INSERT)操作。)
select * from test_phantom;
ID INFO
1 test
2 new
事务的隔离级别
在 SQL-92 标准中,定义了四种隔离级别:读未提交、读提交、可重复读和串行化。
- 未提交读 (READ UNCOMMITTED)
- 已读提交 (READ COMMITTED)
- 可重复读 (REPEATABLE READ)
- 可串行化 (SERIALIZABLE)
事务的隔离级别是为了解决脏读、不可重复读、幻读这几个问题,如下是每种隔离级别下对于读数据有不同的要求
隔离级别 \ 解决 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | 可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
可串行化 | 不可能 | 不可能 | 不可能 |
达梦如何开启事务隔离级别
未提交读隔离级别
未提交读隔离级别是最不严格的隔离级别,在使用这个隔离级别时,会发生脏读、不可重复读和幻读。一般来说,未提交读隔离级别通常只用于访问只读表和只读视图,以消除可见性判断带来的系统开销,提升查询性能,该级别下事务为只读模式。
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
已提交读隔离级别(默认)
数据库的已提交读隔离级别可以确保只访问到已提交事务修改的数据,保证数据处于一致性状态。
默认就是已提交读隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
可重复读隔离级别
在达梦数据库中可重复读隔离级别可以避免不可重复读和幻读问题,为了在并发环境下得到一个稳定结果,比如,财务对账中,我们可以设置可重复读事务隔离级别。可重复读相对于序列化在性能上能更灵活一些
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
可串行化隔离级别
在要求消除不可重复读或幻读的情况下,我们可以设置事务隔离级为可串行化。跟已提交读隔离级别相比,可串行化事务的查询本身不会增加任何代价,但修改数据可能引发“串行化事务被打断”的错误。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
持久性
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其
他操作和数据库故障不应该对其有任何影响。即一旦一个事务提交,DBMS 保证它对数据库中数据的改变应该是永久性的。如果 DM 数据库或者操作系统出现故障,那么在 DM 数据库启的时候,数据库会自动恢复。如果某个数据驱动器出现故障,并且数据丢失或者被损坏,可以通过备份和联机重做日志来恢复数据库。需要注意的是,如果备份驱动器也出现故障,且系统没有准备其他的可靠性解决措施,备份就会丢失,那么就无法恢复数据库了。
--执行insert语句并提交,数据提交后处于持久化状态
create table test_phantom(id int, info varchar);
insert into test_phantom values (1, 'test');
commit;
--执行如下命令停止数据库
shutdown immediate
--重启数据库后,发现数据依然存在,说明事务一旦被提交在数据库中的改变是永久性的