微信号:java_bj

介绍:从算法基础到常用框架的知识体系,从初级程序员到高级架构师的成长之路,从创业小团队到Google、BAT的工作机会,始于JAVA而又不止于JAVA.JAVAer在北京,我们一起成长.

数据库系列一:事务(上)

2016-07-18 00:03 盛飞
点击上方“Java北京”关注我们



一、基本概念

所谓事务(Transaction),是指作为一个逻辑单元的一系列相关操作(增、删、改等),要么全部完成,要么全都不完成。事务具有四个基本属性:原子性、一致性、隔离性、持久性,即我们经常说的ACID。

  • 原子性(atomicity):事务是不可分割的工作的单位,整个事务中的所有操作,要么全部完成,要么全部不完成,如果事务在执行过程中发生错误,会回滚(Rollback)到事务开始前的状态。

  • 一致性(consistency):事务将数据库从一种一致性状态转变为下一种一致性状态。最常见的例子就是转账的例子,有三个账户,每个账户余额是100元,那么三个账户总额是300元,在三个账户之间发生转账,无论并发多少、流向如何,最终三个账户的总额还是300元。

  • 隔离性(isolation):多个事务并发执行,单独不影响 。

  • 持久性(durability):事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。

二、事务的工作原理及实现

由于一项操作通常会包含许多子操作,而这些子操作可能会因为硬件的损坏或其他因素产生问题,要正确实现ACID并不容易。ACID建议数据库将所有需要更新以及修改的资料一次操作完毕,但实际上并不可行。

目前主要有两种方式实现ACID:第一种是Write Ahead Logging(WAL),也就是日志式的方式(现代数据库均基于这种方式)。第二种是Shadowpaging。

以预写日志方式为例,事务开始之后,事务所有的操作都陆续写到事务日志中。写到日志中的操作,一般有两种:一种是针对数据的操作(例如插入、删除和修改,这是典型的事务操作,这些操作的对象是大量的数据;一种是针对任务的操作(例如创建索引,这些任务操作在事务日志中记录一个标志,用于表示执行了这种操作)。

当取消这种事务时,系统自动执行这种操作的反操作,保证系统的一致性。系统自动生成一个检查点机制,这个检查点周期地发生。检查点的周期是系统根据用户定义的时间间隔和系统活动的频度由系统自动计算出来的时间间隔。检查点周期地检查事务日志,如果在事务日志中,事务全部完成,那么检查点将事务日志中的事务提交到数据库中,并且在事务日志中做一个检查点提交标记。如果在事务日志中,事务没有完成,那么检查点将事务日志中的事务不提交到数据库中,并且在事务日志中做一个检查点未提交标记。事务的恢复以及检查点保护系统的完整和可恢复。

具体实现:

事务隔离性由锁实现;原子性、一致性、持久性通过数据库的redo和undo实现。

RedoLog

记录的是新数据的备份。在事务提交前,只要将Redo Log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是RedoLog已经持久化。系统可以根据RedoLog的内容,将所有数据恢复到最新的状态

UndoLog

Undo Log是为了实现事务的原子性,在MySQL数据库InnoDB存储引擎中,用Undo Log来实现多版本并发控制(简称:MVCC)。在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为UndoLog)。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用UndoLog中的备份将数据恢复到事务开始之前的状态

三、事务控制语句

在mysql命令行的默认下,事务都是自动提交的,sql语句提交后马上会执行commit操作。因此开启一个事务必须使用begin,start transaction,或者执行set autocommit=0来禁用当前会话的自动提交。

start transction | begin : 显示的开启一个事务

commit 事务提交

rollback事务回滚

四、事务隔离级别

数据库操作过程中可能出现以下几种不确定情况: 

1.   更新丢失(Lost Update)

两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。

2.   脏读(Dirty Reads)

一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。

A dirty read occurs when atransaction reads data from a row that has been modified by anothertransaction, but not yet committed.  

3.   不可重复读(Non-repeatable Reads)

一个事务对同一行数据重复读取两次,但是却得到了不同的结果。例如,在两次读取的中途,有另外一个事务对该行数据进行了修改,并提交。

In a lock-based concurrency controlmethod, non-repeatable reads may occur when read locks are not acquired whenperforming a SELECT. Under multiversion concurrency control, non-repeatablereads may occur when the requirement that a transaction affected by a commitconflict must rollback is relaxed. 

4.   两次更新问题(Second lost updates problem)

不可重复读的特例。有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。 

5.   幻读(Phantom reads)

事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据 (这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。

A phantom read occurs when, in thecourse of a transaction, two identical queries are executed, and the collectionof rows returned by the second query is different from the first. This canoccur when range locks are not acquired on performing a SELECT.

为了解决上述问题,在标准SQL规范中,定义了4个事务隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

1.     Read Uncommitted(读取未提交内容)

读取未提交的数据,也被称之为脏读(Dirty Read)。允许脏读,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事务读取此行数据。该隔离级别可以通过“排他写锁”实现。在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。

2.     Read Committed(读取提交内容)

允许不可重复读,但不允许脏读。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果

3.     Repeatable Read(可重读)

禁止不可重复读和脏读,但是有时可能出现幻影数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。这是MySQL的默认事务隔离级别(实际是InnoDb支持),它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

4.     Serializable(可串行化)

提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

五、事务与存储引擎

MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关。比如常见的存储引擎InnoDB(MySQL5.5及以后版本的默认存储引擎)支持事务,而MyISAM(MySQL5.5以前版本默认存储引擎)就不支持。

注:

show ENGINES;  -- 查询mysql提供的存储引擎

show variables like '%storage_engine%'; -- 查询mysql当前默认的存储引擎



是时候关注一个只分享干货的公众号了

长按二维码 关注我们

JAVA北京(java_bj)


 
Java北京 更多文章 Java中的ThreadPoolExecutor类 Innodb与MyISAM的区别 缓存架构设计细节二三事 线程数究竟设多少合理 如何选择开源项目?
猜您喜欢 走向国际化第一步——文档先行 苹果为什么设计单键鼠标? 快问快答(第五期) 你距离脱单还有 2 天! 2016年6月29日,今日全球能源资讯