您的位置:首页 > 数据库

SQL 高级编程 - 事务处理

2013-09-26 16:05 155 查看
当输入一个查询,希望得到与之相一致的返回结果集。
如果输入一个SQL语句来更新几百条记录,而其中一个数据行的更新失败了,希望整个过程就失败,所有的数据行返回到更新前的状态。
如果更新成功了并且你将它提交给了数据库,希望自己的修改对其他用户可见并被保留在数据库中,至少直到数据再次被其他人更新。
希望读取数据时,不会阻止另一个会话的写入;反之亦然。

关系型数据库都是要进行事务处理的。
设计实物并进行编码的方式将会影响应用数据的完整性和一致性。
如果没有清楚定义事务的边界,应用可能会出现一些意料之外的行为。

当多个用户从中获取信息并修改其中的信息的时候,事务设置还会影响系统性能。
事务设置不好可扩展性也会受到很大的限制。

尽然只有很少的几条事务控制语句,要想理解事务如何进行处理需要理解Oracle数据库的一些更复杂的概念和架构组件。

参考:

Oracle Concepts Manual

Tom Kyte的Expert Oracle Database Architecture Oracle Datebase 9i,10g and 11g Programming Techniques and Solutions的第6、7、8章

事务 - 一个独立的逻辑工作单元 - 由特定的一系列必须做为一个整体一起成功或失败的SQL语句组成。
每个事务都一个很清楚的起点,就是第一个可以执行SQL语句开始执行的时候,以及一个确定的结束点,当事务的工作进行了提交或回滚之后。
已经开始,但还没有将其工作提交或回滚的事务是“活动事务”,活动事务中的所有修改在进行提交之前都是不确定的。
如果事务失败了或进行了回滚,则这些不确定的修改将不会在数据中存在。

进行一个合理的事务代码编写的最重要的元素就是:准确地将事务边界设定在一个逻辑工作单元中,并确保一个事务之内的所有运算都作为一个整体来处理。
需要理解应用需求以及商务流程。

数据库事务ACID:

原子性 Atomicity - 所有任务都必须执行或都不执行,不存在部分事务

一致性 Consistency - 从一个一致状态带到另一个一致性状态

隔离性 Isolation - 带来的影响直到该事务提交之前对其他事务来说都是不可见

持久性 Durability - 修改时永久性的



脏读 - 读取一个未提交的事务称为脏读。脏读没有经过提交,也就意味着数据还没有经过数据库中所设定的约束的检验。未提交的数据有可能永远也不会提交,也不会是数据库的一部分。

不可重复读 - 当事务第二次执行同一个查询的时候,由于另一个事务提交了更新而得到了不同结果的时候就是发生了不可重复读。

幻读 - 事务中的一个查询第二次执行,但返回了满足条件的的过滤标准的记录,就认为发生了幻读。当其他事务插入了更多的数据并进行了提交就有可能产生幻读。

ACID属性并没有确定数据库应该如何为事务提供数据一致性。
ANSI/ISO SQL事务隔离级别也没有定义如何实现事务隔离。
每个供应商自己来确定如何遵守ACID以及支持哪些隔离度水平。

Oracle听过多版本读一致性模型来提供数据的一致性和并发性。
Oracle能够基于事务请求信息的某个特定时间点以及事务隔离级别来同时显示数据的多个版本。
数据库通过保留修改过的数据块在修改之前和之后的状态实现的。

事务提交修改后,SGA数据库缓冲区会进行更新,但修改不一定会马上写入到数据文件中。
Oracle使用系统变更号System Change Number SCN来记录实例中所发生的变更的顺序并将变更与某个时间点联系起来。
如果事务失败,所有待定的事务都会进行回滚,从而当数据库重启的时候,数据又会再次回到一致性状态,仅考虑失败之前已经提交的工作。
如果数据库管理员输入一个命令将数据库闪回到某个特定的SCN,你也将会得到同样的结果。数据库返回到SCN 所标明的时间点上,任何在这一时点之后提交的事务都将不再在数据库中存在。

-- 多版本读一致,如何影响单独的事务?
-- 如果事务B请求已经被事务A修改了的数据,但是事务A还没有提交其修改;
-- Oracle将会将读取数据修改之前的状态,并将这个视图返回给事务B。
-- 如果事务C在事务A提交修改以后开始,返回给事务C的结果就会包含事务A所提交的修改。
-- 这意味着事务C接受的结果与事务B不同,但结果与各个会话发起信息请求时是一致的。


-- 提交
-- 与SQL标准相一致的Commit Work,通过使你的修改永久化并对其他用户可见来结束事务
commit

-- 保存点
-- 在事务中标记特定的点并将你的事务回滚到某个特定的保存点
-- 然后就可以继续你的事务而不是重新发起一个全新的事务
savepoint

-- 回滚
-- 结束事务的另一个选项,选择回滚,你的修改都被回退
-- 所有数据返回到之前的一致性状态
-- 可以选择回滚到一个特定的保存点,但回滚到保存点并不会结束事务
-- 事务会保持活动状态直到彻底回滚或进行提交
rollback

-- 设置事务
-- 提供了多种选项来修改默认的事务行为
set transaction
-- 将提供重复读但你不能修改数据
set transaction read only
-- 还可以指定可序列化隔离级别
-- set transaction还可以为事务选择一个特定的回滚段Segment
-- 还可以命名自己的事务

-- 设置约束
-- 可以将约束在事务中推迟
-- 默认行为:每个语句之后检查约束,但在一些事务中,可能知道事务中的
--         所有的更新进行完之后约束才能被满足。在这些情况下,可以推迟约束检验
-- 可以推迟一个单独的约束,也可以推迟所有的约束
set constraints


设计一个合理的事务有几条规则:

将每一个逻辑工作单元作为一个独立的事务来进行处理,不要包含额外的工作

确保在你的事务开始的时候数据时一致的,并且到事务完成的时候数据仍然保持一致

获取你所需要的资源来处理你的事务,然后释放资源给其他事务使用。按照你所需要的来占有共享资源,但不要更长。不在在事务中提交来释放你仍然需要的锁。加入提交打断逻辑工作单元并且对数据库没有好处

考虑可能同时进行的其他事务的处理

使用保存点来标记合适于事务中间进行回滚的特定SQL语句

事务必须进行显式地提交或回滚

一旦设计了一个可靠的事务,考虑将它封装到存储过程或包中

异常处理对事务的保存有很大的影响

考虑使用DBMS.Application_Info包标准事务。有助于在错误检修或性能调优的时候快速和准确地定位代码中的特定部分

当你发起第一条SQL语句来修改数据的时候,数据库就识别出一个活动的事务并创建一个事务ID
不管事务中包含一条还是二十条DML语句,事务ID都是保持一致的
并且事务ID只会在事务活动期间,你的修改还是待定的时候存在
SCN编号会不断增加而不管你的事务处理过程中处于什么步骤
SCN编号标识数据库中的一个特定时间点,用来数据库还原到之前的一个时间点同时还能保证数据的一致性

/*
*    在序列化模式下,执行事务并不意味着更新是串行来处理的。
*    如果一个序列化事务尝试更新一条在事务开始后已经发生了变化的记录,更新将不被允许
*    Oracle会返回一个错误:
*    ORA-08177:can't serialize access for this transaction
*    要想一个序列化事务成功完成,
*    就需要有很大的可能性在事务执行过程中不会有其他人来更新同样的数据。
*/
-- Section A
set transaction isolation level serializable;
variable o number
execute :o := &order_id
Enter value for order_id:5006
variable d number
execute :d := &discount
Enter value for discount:.1
------- Add new ordered item and reduce on-hand inventory
variable i number
execute :i := &first_item
Enter value for first_item:1791
variable q number
execute :q := &item_quantity
Enter value for item_quantity:15

variable p number
execute :p := get_ListPrice(:i)

insert into order_items
(order_id, line_item_id, product_id, unit_price, discount_price, quantity)
values
(:o, 1, :i, :p, :p-(:p*:*d),:q);
update inventories set quantity_on_hand = quantity_on_hand - :q
where product_id = :i and warehouse_id = 1;

pause Pause...

-- Section B:Order status update
variable s number
execute :s := &status
Enter value for status:4

update orders
set order_status = :s
where order_id = :o;

select order_id, customer, mobile, status, order_total, order_date
from order_detial_header
where order_id = :o;
select line_item_id, product_name, unit_price, discount_price, quantity, line_item_total
from order_detail_line_item
where order_id = :o
order by line_item_id;

commit;

-- Section A: Return to the serializable transaction
-- get new order total
variable t number
execute :t := get_OrderTotal(:o)
-- update order total
update order set order_total = :t where order_id = :o;
ORA-08177:can't serialize access for this transaction

-- 由于会话B已经提交了订单5006的修改,会话A不能更新订单总额
-- 防止丢失更新,是必要的。会话A不能看到订单的状态已经发生改变
-- 他的序列化视图仍然将订单视为“新的”,如果会话A可以用他的数据版本替换订单表中的记录
-- 会话B所做的修改就会被数据之前的版本重新,即使他们是已经提交的修改

-- 要想会话A中的事务成功执行,就必须尽早更新订单总额。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: