什么是数据库事务
事务(Transaction):一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在计算机术语中,事务通常就是指数据库事务,是逻辑上的一组数据库操作,要么都执行,要么都不执行。
例子:假如A要给B转账500元,这个转账会涉及到两个关键操作: 一、将A的余额减少500元; 二、将B的余额增加500元; 如果两个操作之间突然出现错误,例如银行系统错误导致A的余额已经减少,而B的余额却没有增加,这样的系统是有问题的。而事务就是保证这两个关键操作要么都成功,要么都要失败。
数据库事务的特性
事务具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。 持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
以银行转账举例,说明如何通过数据库事务保证数据的准确性和完整性。熟悉关系型数据库事务的都知道从帐号A到帐号B需要6个操作:
用一个常用的“A账户向B账号汇钱”的例子来说明如何通过数据库事务保证数据的准确性和完整性。熟悉关系型数据库事务的都知道从帐号A到帐号B需要6个操作:
1、从A账号中把余额读出来:(500)。 2、对A账号做减法操作:(500-100)。 3、把结果写回A账号中:(400)。 4、从B账号中把余额读出来:(500)。 5、对B账号做加法操作:(500+100)。 6、把结果写回B账号中:(600)。
在这个过程中可能会出现以下问题:
- 1.转账操作的第一步执行成功,A账户上的钱减少了100元,但是第二步执行失败或者未执行便发生系统错误,导致B账户并没有相应增加100元。
- 2.转账操作刚完成就发生系统崩溃,系统重启恢复时丢失了崩溃前的转账记录。
- 3.同时又另一个用户转账给B账户,由于同时对B账户进行操作,导致B账户金额出现异常。
原子性:
保证1-6所有过程要么都执行,要么都不执行。一旦在执行某一步骤的过程中发生问题,就需要执行回滚操作。 假如执行到第五步的时候,B账户突然不可用(比如被注销),那么之前的所有操作都应该回滚到执行事务之前的状态。
一致性
在转账之前,A和B的账户中一共有500+500=1000元钱。在转账之后,A和B的账户中一共有400+600=1000元。也就是说,数据的状态在执行该事务操作之后从一个状态改变到了另外一个状态。同时一致性还能保证账户余额不会变成负数等。
隔离性
在A向B转账的整个过程中,只要事务还没有提交(commit),查询A账户和B账户的时候,两个账户里面的钱的数量都不会有变化。 如果在A给B转账的同时,有另外一个事务执行了C给B转账的操作,那么当两个事务都结束的时候,B账户里面的钱应该是A转给B的钱加上C转给B的钱再加上自己原有的钱。
持久性
一旦转账成功(事务提交),两个账户的里面的钱就会真的发生变化(会把数据写入数据库做持久化保存)!
如何实现数据库事务
在事务的ACID特性中,C即一致性是事务的根本追求,而对数据一致性的破坏主要来自两个方面
- 1.事务的并发执行
- 2.事务故障或系统故障
数据库系统是通过并发控制技术和日志恢复技术来避免这种情况发生的。
并发控制技术保证了事务的隔离性,使数据库的一致性状态不会因为并发执行的操作被破坏。 日志恢复技术保证了事务的原子性,使一致性状态不会因事务或系统故障被破坏。同时使已提交的对数据库的修改不会因系统崩溃而丢失,保证了事务的持久性。
事务的隔离等级
并发事务处理带来的问题
- 更新丢失 当两个或多个事务选择同一行,然后基于最初选定的值更新改行时,有于每个事务都不知道其他事务的存在,就会发生更新问题:最后的更新覆盖了由其他事务所做的更新。
- 脏读 一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致的状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此进一步的处理,就会产生未提交的数据依赖关系。这种现象被称为“脏读”。 事务A读取到事务B已经修改但未提交的数据,还在这个数据基础上做了修改。此时,如果事务B回滚了,事务A的数据无效,不符合一致性要求。
- 不可重读 一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现起读出的数据已经发生了改变、或某些记录已经被删除。这种现象叫做“不可重读”。 事务A读取到了事务B已经提交的修改数据,不符合隔离性。
- 幻读 一个事务按照相同的查询条件读取以前检索过的数据,却发现某些事务插入了满足其查询条件的新数据,这种现象称为“幻读”。 事务A读取了事务B提交的新增数据,不符合隔离性。
事务的隔离等级
- 事务具有隔离性,理论上来说事务之间的执行不应该相互产生影响,其对数据库的影响应该和它们串行执行时一样。
- 然而完全的隔离性会导致系统并发性能很低,降低对资源的利用率,因而实际上对隔离性的要求会有所放宽,这也会一定程度造成对数据库一致性要求降低
- SQL标准为事务定义了不同的隔离级别,从低到高依次是
- 读未提交
- 读已提交
- 可重复读
- 串行化
事务的隔离级别越低,可能出现的并发异常越多,但是通常而言系统能提供的并发能力越强。
不同的隔离级别与可能的并发异常的对应情况如下表所示,有一点需要强调,这种对应关系只是理论上的,对于特定的数据库实现不一定准确,MySQL的Innodb存储引擎通过Next-Key Locking技术在可重复读级别就消除了幻读的可能。
行锁与表锁
所有的事务实现,处理并发冲突,都需要依赖锁来实现。
一般可以分为两类,一个是悲观锁,一个是乐观锁,悲观锁一般就是我们通常说的数据库锁机制,乐观锁一般是指用户自己实现的一种锁机制,比如hibernate实现的乐观锁甚至编程语言也有乐观锁的思想的应用。
悲观锁:顾名思义,就是很悲观,它对于数据被外界修改持保守态度,认为数据随时会修改,所以整个数据处理中需要将数据加锁。悲观锁一般都是依靠关系数据库提供的锁机制,事实上关系数据库中的行锁,表锁不论是读写锁都是悲观锁。
1.表级锁定(table-level)
表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。
当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。
使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。
2.行级锁定(row-level)
行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。
使用行级锁定的主要是InnoDB存储引擎。
表级锁:开销小,加锁快;较难出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;
行级锁:开销大,加锁慢;容易出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;
标签:转账,事务,执行,数据库,并发,深入,锁定 From: https://blog.51cto.com/u_16516508/9232869