数据库事务

数据库事务Database Transaction首先它是一系列操作,其次这个操作是需要在一个逻辑单元内执行的。也就是说这组操作要么完全执行、要么完全不执行。

为什么要引入事务这个概念?

还是那个很经典的粟子:你的银行卡有1000元现金(Balance=1000),某日某时刻你到银行柜台取钱,巧的是你的另一伴正拿着副卡在商场买东西刷卡,这个时候比如你要取1000元,而她买的东西也是500元(为说明问题不择手段了)。你取钱的动作分解为:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 先检查帐户余额Balance>=1000,然后执行Balance-1000的操作,但Balance的新结果还未更新到数据库中;  
  2. 同时另一张卡也在检查余额Balance>=1000,然后执行Balance-500的操作;  
  3. 如果先更新你的操作那么结果是Balance=0,然后再更新她的操作Balance=500覆盖你刚才的结果,最终余额为500;  
  4. 如果先更新她的操作那么结果是Balance=500,然后再更新你的操作Balance=0覆盖她刚才的结果,最终余额为0.  

可以发现不论怎么算,只要操作顺序不一样结果就会不一样,而且不论怎么算结果银行都亏了,可银行也不傻嘛!所以就想出一个办法---事务,即一组操作要么完全执行要么完全不执行。具体实现对账户加锁就行了,还是上面的粟子:

[plain]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 先检查帐户余额Balance>=1000,然后执行lock(Balance),再执行Balance-1000的操作,直到将Balance=0的结果更新到数据库中才释放unlock(Balance);  
  2. 同时另一张卡也在检查余额Balance>=1000,然后执行lock(Balance)却执行不了就要处于等待状态,至Balance处于unlock状态才能进行Balance新的更新操作;  

利用事务维护数据库的完整性

尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN 关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. BEGIN;  
  2. INSERT INTO salesinfo SET CustomerID=14;  
  3. UPDATE inventory SET Quantity=11  
  4. WHERE item='book';  
  5. COMMIT;  
事务的另一个重要作用 是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。

锁定表

尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。

其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. LOCK TABLE inventory WRITE  
  2. SELECT Quantity FROM inventory  
  3. WHEREItem='book';  
  4. ...  
  5. UPDATE inventory SET Quantity=11  
  6. WHEREItem='book';  
  7. UNLOCK TABLES  

这里,我们用一个 SELECT 语句取出初始数据,通过一些计算,用 UPDATE 语句将新值更新到表中。包含有 WRITE 关键字的 LOCK TABLE 语句可以保证在 UNLOCK TABLES 命令被执行之前,不会有其它的访问来对 inventory 进行插入、更新或者删除的操作。

声明事务的边界:

1mysql声明事务

每个数据库都有个全局变量@@autocommit,表示当前的事务模式:0:手工提交模式,1:默认为自动提交模式.

MySQL查看事务模式

查看当前事务模式:select @@autocommit

设置当前事务模式: set autocommit = 0;

提交事务

l 自动提交模式下提交事务

每个SQL语句都是一个独立的事务。自动提交事务

l 手动提交模式

事务的开始边界:begin

提交事务:commit

撤销事务:rollback

设置保存点 savepoint a 
取消保存点a之后事务 rollback to a 

(2)jdbc api声明事务

Connection提供了用于控制事务的方法

l setAutoCommit(boolean autoCommit):设置是否自动提交事务

l Commit:提交事务

l Rollback:撤销事务

(3)Hibernate声明事务

声明事务开始的边界:Transaction tx = session.beginTransaction();

提交事务:tx.commit()

撤销事务:tx.rollback()

多个事务的并发问题

假设,存在两个事务(T1T2)并发执行

(1)脏读:一个事务读到了另一个事务未提交的更新数据

T1读取了T2修改但还未提交的数据,由于某种错误T2回滚,则T1读取就是无效数据。

(2)不可重复读:一个事务读到另一事务提交的更新数据

T1读取一个字段,之后T2更新了该字段,T1再次读取该字段发生了变化。

一个事务对同一行数据重复读取两次,但却得到不同的结果,同一查询在事务中进行多次,由于其他事务做了修改或删除,每次返回不同的结果集,此时发生不可重复读。

3虚读:一个事务读到另一个事务已提交的新插入的数据

T1从一个表中读取数据,然后T2在该表中插入新的行,T1再次读取该表时发现结果多几行

(3)丢失更新:这是不可重复读中的特例,一个事务覆盖另一事务已提交的更新数据。


由上面的粟子引出事务四个特性ACID):

1、原子性(Atomicity):事务必须是数据库的逻辑工作单位,即对数据的修改要么全执行要么全不执行;

2、一致性(Consistency):事务在完成时必须所有的数据都保持一致状态,即事务的执行结果是使数据库从一个一致性状态转变到另一个一致性状态;

3、隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的各事务之间不能互相干扰;

4、持久性(Durability):一个事务一旦提交它对数据库中数据的改变就是永久的,即事务完成后它的结果影响是永久性的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值