Transactions
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在计算机术语中,事务通常就是指数据库事务。
应用场景:
应用程序需要执行多个操作,包括一个或多个数据库命令
- 其中一个数据库命令失败或某些其他操作失败(例如注册表更新)
- 多项行动是为了一起工作。应用程序现在处于不一致的状态。
我们使用 transaction 来解决这个问题,transaction 就是执行所需的一组命令原子操作
➢如果所有命令都成功完成,则 transaction 成功。
➢如果有任何命令失败,整个 transaction 将 rollback 到 pre-transaction 状态。
➢造成rollback的故障不一定与数据库相关。
ACID transaction properties 属性
All database management systems adhere to a set of properties known as the ACID properties:
并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为ACID特性
Atomicity: “All or nothing.” No partial success. Either the transaction succeeds in its entirety or it fails.
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。Consistency: Integrity constraints are met. Acceptable for inconsistency to occur during the transaction, but must be completely consistent upon completion.
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。Isolation: The extent to which other transactions can “see” changes made by other transactions while they are executing.
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。Durability: Guarantees permanent survival of data after the commit is complete.
持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中
.NET 页、XML Web services方法或 .NET Framework 类一旦被标记为参与事务,它们将自动在事务范围内执行。您可以通过在页、XML Web services 方法或类中设置一个事务属性值来控制对象的事务行为。特性值反过来确定实例化对象的事务性行为。因此,根据声明特性值的不同,对象将自动参与现有事务或正在进行的事务,成为新事务的根或者根本不参与事务。声明事务属性的语法在 .NET Framework 类、.NET 页和 XML Web services 方法中稍有不同。
Isolation Level 事务隔离层级
A property of the TransactionScope object, specifying the degree of transaction isolation.
隔离级别定义了事务与事务之间的隔离程度。
隔离级别与并发性是互为矛盾的:隔离程度越高,数据库的并发性越差;隔离程度越低,数据库的并发性越好。
Serializable (highest): read, write, and range locks (if necessary) used until end of the transaction.
序列化:提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。RepeatableRead: Same, except no range locks. Other transactions can delete/update resulting in phantom reads.
可重复读取:禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。Snapshot: Instead of write-locking, “snapshot” of to-be-written data is taken and served to reading transactions.
“snapshot” 取代 write-locking写锁定,获取待写入数据的 snapshot 并将其送达
reading transactions。这个其实是tradeoff,并没有复制所有的数据,
搞清楚OS中的 readlock,writelock, rangelock。
读取数据时,可以保证读操作读取的行是事务开始时可用的最后提交版本。这意味着这种隔离级别可以保证读取的是已经提交过的数据,并且可以实现可重复读,也能确保不会幻读。
ReadCommitted: No dirty reads (not-yet-committed data from other transactions not visible), but non-repeatable reads may occur. (Default IL for SQL Server)
授权读取:在commit之前,不允许dirty read(尚未提交的其他交易数据不可见)。但可能会发生不可重复的读取,因为反复读取会有不一样的结果。 (SQL Server的默认level)允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。ReadUncommitted (lowest): Dirty reads permitted. One transaction can see another transaction’s changes before the commit.
未授权读取: 允许脏读,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。不管事务B何时Commit,事务A中的SELECT语句2都可以读出数据(有可能是脏数据,因事务B可能会ROLLBACK),且与语句1的数据不同。
Isolation Levels Illustrated
这个图按照时间顺序,上至下操作。
UnCommitted level时,只要A做出了更改,B就会读取到这个更改,不存在Commit的考虑。
Committed level时,T1读取了数据A,T2得到一样数据A。T1把数据修改为数据B,T2再读取依然是数据A。commit之后,T2 就会得到数据B。不可重复读。
RepeatableRead和Serializable level时,区别在于Range Lock。T2 在commit之前,读取的都是未修改的数据A。
选择什么level很多程度上取决于我们对数据变化的重视程度。
下面是个例子:
关于 三种 读
脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
不可重复读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不 可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果 只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。
幻读 : 幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样.一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。
最高隔离级别SERIALIZABLE_READ可以保证不出现幻读的问题。
Transactions in EF
在Entity Framework中使用 transaction 更容易:事务自动发生在同一个事务中上下文。
SaveChanges 就自动有了transaction,是readcommitted level。
- Transactions using this technique are limited to the scope of the context in the using clause
- Transactions spanning multiple contexts require heavier artillery: TransactionScope
- You must add the reference System.Transactions
- Connecting to multiple databases requires enabling MSDTC on the server.
在EF版本6中:
SaveChanges之后还要Commit。
Try block之后Catch,任何问题发生都可以rollback。
如果我们希望用其他level的isolation,则需要用TransactionScope object:
这里有 TransactionScope constructor。
需要两个参数,一个是new,一个是transaction option object 设置 isolation level,这里设置成最低的 Uncommitted。
下面的例子并未完成,因为没有rollback
Change tracking
当修改EF对象或从容器中添加/删除EF对象时...
- Changes are tracked locally by the context
- You can stop change tracking by detaching an entity from the context. (example to follow)
Calling SaveChanges does the following:
- Generates and executes SQL commands 生成并执行SQL命令
- Commits changes to the database in a single transaction 在单个事务中提交对数据库的更改
- Resets tracking information重置跟踪信息
改变跟踪 attaching/detaching 例子
c2 脱离了,改变不再被记录。Savechanges时,只有c1 会被记载,c2还是会被放在course的container里,在Course里还是可以看见c1和c2,但是数据库里只有c1。
相关链接
彻底理解数据库事务
事务隔离层级(Transaction Isolation Level)
什么是脏读,不可重复读,幻读
百度 - 事务隔离级别
百度 - 幻读
Sql Server来龙去脉系列之三 查询过程跟踪