显示/隐藏全文目录
事务介绍及其ACID特性和隔离级别 事务简介 事务(Transaction)是一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。事务通常以BEGIN TRANSACTION开始,以COMMIT或ROLLBACK操作结束,COMMIT即提交,提交事务中所有的操作、事务正常结束。ROLLBACK即回滚,撤消已做的所有操作,回滚到事务开始时的状态。事务是数据库系统区别于文件系统的重要特性之一。
事务的作用:当多个进程都开启事务同时操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个进程获取到正确的数据。
对于事务可以举一个简单的例子:转账,有A和B两个用户,A用户转100到B用户,如下所示:
A:---->支出100,则 A-100
B:---->收到100,则 B+100
A--->B转账,对应如下SQL语句:
UPDATE ACCOUNT SET MONEY= MONEY - 100 WHERE NAME='A' ;
UPDATE ACCOUNT SET MONEY= MONEY + 100 WHERE NAME='B' ;
事务的四个特性
事务有4个特性,一般都称之为ACID特性,简单记为原一隔持(谐音:愿意各吃,即愿意各吃各的),如下表所示:
表 2-5 事务的ACID特性
名称 简介 举例 原子性(Atomicity) 所谓原子性是指事务在逻辑上是不可分割的操作单元,其所有语句要么都执行,要么都撤销执行。当每个事务运行结束时,可以选择“提交”所做的数据修改,并将这些修改永久应用到数据库中。 假设有两个账号,A账号和B账号。A账号转给B账号100元,这里有两个动作在里面,①A账号减去100元,②B账号增加100元,这两个动作不可分割即原子性。 一致性(Consistency) 事务是一种逻辑上的工作单元。一个事务就是一系列在逻辑上相关的操作指令的集合,用于完成一项任务,其本质是将数据库中的数据从一种一致性状态转换到另一种一致性状态,以体现现实世界中的状况变化。至于数据处于什么样的状态算是一致状态,这取决于现实生活中的业务逻辑以及具体的数据库内部实现。 拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。 隔离性(Isolation) 隔离性是针对并发事务而言的,所谓并发是指数据库服务器同时处理多个事务,如果不采取专门的控制机制,那么并发事务之间可能会相互干扰,进而导致数据出现不一致或错误的状态。隔离性就是要隔离并发运行的多个事务间的相互影响。关于事务的隔离性,数据库提供了多种隔离级别,后面的章节会介绍到。 隔离性即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其它事务在并发地执行。 持久性(Durability) 事务的持久性(也叫永久性)是指一旦事务提交成功,其对数据的修改是持久性的。数据更新的结果已经从内存转存到外部存储器上,此后即使发生了系统故障,已提交事务所做的数据更新也不会丢失。 当开发人员在使用JDBC(Java DataBase Connectivity,Java数据库连接)操作数据库时,在提交事务后,提示用户事务操作完成,那么这个时候数据就已经存储在磁盘上了。即使数据库重启,该事务所做的更改操作也不会丢失。
A、原子性 B、一致性 C、隔离性 D、持久性
答案:A、B、C、D。
A、事务中包括的所有操作要么都做,要么不做
B、事务一旦提交,对数据库的改变是永久的
C、一个事务内部的操作及使用的数据对并发的其它事务是隔离的
D、事务必须是使数据库从一个一致性状态变到另一个一致性状态
答案:B。
本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信dbaup66,谢谢!
事务的4种隔离级别(Isolation Level)分别是什么? 当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,所以,对于不同的事务,采用不同的隔离级别会有不同的结果。如果不考虑事务的隔离性,那么会发生下表所示的3种问题:
现象 简介 举例 脏读(Dirty Read) 一个事务读取了已被另一个事务修改、但尚未提交的数据。当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时另外一个并发的事务来访问该数据时,就会造成两个事务得到的数据不一致。 用户A向用户B转账100元,对应SQL命令如下所示:UPDATE ACCOUNT SET MONEY=MONEY + 100 WHERE NAME='B'; (此时A通知B)UPDATE ACCOUNT SET MONEY=MONEY - 100 WHERE NAME='A';当只执行第一条SQL时,A通知B查看账户,B发现钱确实已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,所有操作就都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转成功。 不可重复读(Nonrepeatable Read) 在同一个事务中,同一个查询在TIME1时刻读取某一行,在TIME2时刻重新读取这一行数据的时候,发现这一行的数据已经发生修改,可能被更新了(UPDATE),也可能被删除了(DELETE)。 事务T1在读取某一数据,而事务T2立即修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发生了不可重复读。 幻读(Phantom Read,也叫幻影读、幻像读、虚读) 在同一事务中,当同一查询多次执行的时候,由于其它插入(INSERT)操作的事务提交,会导致每次返回不同的结果集。幻读是事务非独立执行时发生的一种现象。 事务T1对一个表中所有的行的某个数据项执行了从“1”修改为“2”的操作,这时事务T2又在这个表中插入了一行数据,而这个数据项的数值还是“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,那么会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
不可重复读是由于事务并发修改同一条记录导致的,要避免这种情况,最简单的方法就是对要修改的记录加锁,这会导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。
脏读和不可重复读的区别为:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是在同一个事务范围内多次查询同一条数据却返回了不同的数据值,这是由于在查询间隔期间,该条数据被另一个事务修改并提交了。
幻读和不可重复读的区别为:幻读和不可重复读都是读取了另一个事务中已经提交的数据,不同的是不可重复读多次查询的都是同一个数据项,针对的是对同一行数据的修改或删除(UPDATE、DELETE),而幻读针对的是一个数据整体(例如,数据的条数),主要是INSERT操作。
在SQL标准中定义了4种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些是在事务内和事务间可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。SQL标准定义的四个隔离级别为:Read Uncommitted(未提交读)、Read Committed(提交读)、Repeatable Read(可重复读)、Serializable(可串行化),下面分别介绍。
(1)Read Uncommitted(未提交读,读取未提交内容)
在该隔离级别,所有事务都可以看到其它未提交事务的执行结果,即在未提交读级别,事务中的修改,即使没有提交,对其它事务也都是可见的,该隔离级别很少用于实际应用。读取未提交的数据,也被称之为脏读(Dirty Read)。该隔离级别最低,并发性能最高。
(2)Read Committed(提交读,读取提交内容)
这是大多数数据库系统的默认隔离级别。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。换句话说,一个事务从开始直到提交之前,所做的任何修改对其它事务都是不可见的。
(3)Repeatable Read(可重复读)
可重复读可以确保同一个事务,在多次读取同样的数据的时候,得到同样的结果。可重复读解决了脏读的问题,不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。MySQL数据库中的InnoDB和Falcon存储引擎通过MVCC(Multi-Version Concurrent Control,多版本并发控制)机制解决了该问题。需要注意的是,多版本只是解决不可重复读问题,而加上间隙锁(也就是它这里所谓的并发控制)才解决了幻读问题。
(4)Serializable(可串行化、序列化)
这是最高的隔离级别,它通过强制事务排序,强制事务串行执行,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑用该级别。这是花费代价最高但是最可靠的事务隔离级别。
隔离级别 Read Uncommitted(未提交读,读取未提交内容) Read Committed(提交读,读取提交内容) Repeatable Read(可重复读) Serializable(可串行化、序列化) 简介 在该隔离级别,所有事务都可以看到其它未提交事务的执行结果,即在未提交读级别,事务中的修改,即使没有提交,对其它事务也都是可见的,该隔离级别很少用于实际应用。读取未提交的数据,也被称之为脏读(Dirty Read)。该隔离级别最低,并发性能高。 这是大多数数据库系统的默认隔离级别。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。换句话说,一个事务从开始直到提交之前,所做的任何修改对其它事务都是不可见的。提交读是Oracle数据库默认的事务隔离级别。 可重复读可以确保同一个事务,在多次读取同样的数据的时候,得到同样的结果。可重复读解决了脏读的问题,不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。MySQL数据库中的InnoDB和Falcon存储引擎通过MVCC(Multi-Version Concurrent Control,多版本并发控制)机制解决了该问题。需要注意的是,多版本只是解决不可重复读问题,而加上间隙锁(也就是它这里所谓的并发控制)才解决了幻读问题。可重复读是MySQL数据库的默认隔离级别。 这是最高的隔离级别,它通过强制事务排序,强制事务串行执行,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑用该级别。这是花费代价最高但是最可靠的事务隔离级别。 脏读 允许 不可重复读 允许 允许 幻读 允许 允许 允许 默认级别数据库 Oracle、SQL Server、PG MySQL 并发性能 最高 比Read Uncommitted低 比Read Committed低 最低
不同的隔离级别有不同的现象,并有不同的锁和并发机制,隔离级别越高,数据库的并发性能就越差,4种事隔离级别与并发性能的关系: