论坛首页 Java企业应用论坛

如果只提交一个查询,有必要用事务吗

浏览 33548 次
该帖已经被评为精华帖
作者 正文
   发表时间:2003-12-08  
从这篇文章来看,将查询放到事务中,还是有道理的。
虽然并发度不高的程序,出错的概率很小,但是拿 程序正确运行的保证 和 少许效率相比,我倾向于前者,即便是在并发度不高的应用中。
=========================================
SQL 标准 用三个必须在并行的事务之间避免的现象定义了四个级别的事务隔离。 这些不希望发生的现象是:



脏读(dirty reads)
一个事务读取了另一个未提交的并行事务写的数据。

不可重复读(non-repeatable reads)
一个事务重新读取前面读取过的数据, 发现该数据已经被另一个已提交的事务修改过。

幻读(phantom read)
一个事务重新执行一个查询,返回一套符合查询条件的行, 发现这些行因为其他最近提交的事务而发生了改变。


这四种隔离级别和对应的行为在Table 9-1 里描述.

Table 9-1. SQL 隔离级别

隔离级别  脏读(Dirty Read)  不可重复读(NonRepeatable Read)  幻读(Phantom Read) 
读未提交(Read uncommitted)  可能  可能  可能 
读已提交(Read committed)  不可能  可能  可能 
可重复读(Repeatable read)  不可能  不可能  可能 
可串行化(Serializable )  不可能  不可能  不可能 

PostgreSQL 提供读已提交(read committed)和可串行化(serializable)隔离级别。

9.2.1. 读已提交隔离级别
读已提交(Read Committed) 是 PostgreSQL 里的缺省隔离级别。 当一个事务运行在这个隔离级别时, 一个 SELECT 查询只能看到查询开始之前提交的数据而永远 无法看到未提交的数据或者是在查询执行时其他并行的事务提交做的改变。 (不过 SELECT 的确看得见同一次事务中前面更新 的结果.即使它们还没提交也看得到.) 实际上,一个 SELECT 查询看到一个在该查询开始 运行的瞬间该数据库地一个快照。 请注意两个相邻的 SELECT 可能看到不同 的数据,哪怕它们是在同一个事务里,因为其它事务会在第一个 SELECT执行的时候提交.

UPDATE, DELETE, 或者 SELECT FOR UPDATE 在搜索目标行的时候的行为和 SELECT 一样:它们只能找到在查询开始的时候已经提交 的行。 不过,这样的目标行在被找到的时候可能已经被其它并发的事务更新(或者删除,或者标记为更新的)。 在这种情况下,即将进行的更新将等待第一个更新事务提交或者回滚(如果它还在处理)。 如果第一个更新回滚,那么它的作用将被忽略,而第二个更新者将继续更新最初发现的行。 如果第一个更新者提交,那么如果第一个更新者删除了该行,则第二个更新者将忽略该行, 否则它将视图在该行的已更新的版本上施加它的操作。系统将重新计算查询搜索条件 (WHERE 子句),看看该行已更新的办不那是否仍然符合搜索条件。如果是, 则第二个更新继续其操作,从该行的已更新版本开始。

因为上面的规则,正在更新的规则可能会看到不一致的快找 --- 它们可以看到 影响它们试图更新的并发更新查询的效果,但是它们看不到那些查询对数据库 里其它行的作用。这样的行为令读已提交模式不适合用于哪种涉及复杂搜索条件 的查询。不过,它对于简单的情况而言是正确的。比如,假设我们用类似下面这样 的查询更新银行余额:

BEGIN;
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 7534;
COMMIT;
如果两个并发事务试图修改帐号 12345 的余额,那我们很明显希望第二个 事务是从帐户行的已经更新过的版本上进行更新。因为每个查询只是影响 一个已经决定了的行,因此让它看到更新后的版本不会导致任何不一致的 问题。

因为在读已提交模式里,每个新的查询都是从一个新的快照开始的,而这个 快照包含所有到该时刻为止已经提交的事务,因此同一个事务里的后面的 查询将看到任何已提交的并发事务的效果。这里要考虑的问题是我们在 一个查询里是否看到数据库里绝对一致的视图。

读已提交模式提供的部分事务隔离对于许多应用而言是足够的,并且这个 模式速度快,使用简单。不过,对于做复杂查询和更新的查询,可能需要 保证数据库有比读已提交模式提供的更加严格的一致性视图。

9.2.2. 可串行化隔离级别
可串行化(Serializable) 提供最严格的事务隔离。 这个级别模拟串行的事务执行, 就好象事务将被一个接着一个那样串行的,而不是并行的执行。 不过,使用这个级别的应用必须准备在串行化失败的时候重新发动事务.

当一个事务处于可串行化级别, 一个 SELECT 查询只能看到在事务开始之前提交的数据而永远看不到未提交的 数据或事务执行中其他并行事务提交的修改。 (不过,SELECT 的确看得到同一次事务中 前面的更新的效果.即使事务还没有提交也一样.) 这个行为和读已提交级别是不太一样的,它的 SELECT 看到的是 该事务开始时的快照,而不是该事务内部当前查询开始时的快照. 这样,一个事务内部后面的 SELECT 总是看到同样的数据。

UPDATE, DELETE,和 SELECT FOR UPDATE 在搜索目标行上的行为和 SELECT 一样: 它们将只寻找在事务开始的时候已经提交的目标行。但是, 这样的目标行在被发现的时候可能已经被另外一个并发的事务更新了(或者是删除或者是标记为更新)。 在这种情况下,可串行化的事务将等待第一个正在更新的事务提交或者回滚 (如果它仍然在处理中)。如果第一个更新者回滚,那么它的影响将被忽略, 而这个可串行化的就可以继续更新它最初发现的行。但是如果第一个更新者 提交了(并且实际上更新或者删除了该行,而不只是为更新选中它) 那么可串行化事务将回滚,并返回下面信息。

ERROR:  Can't serialize access due to concurrent update
因为一个可串行化的事务在 可串行化事务开始之后不能更改被其他事务更改过的行。

当应用收到这样的错误信息时,它应该退出当前的事务然后从头开始重新 进行整个事务.第二次运行时,该事务看到的前一次提交的修改是该数据库 初始的样子中的一部分,所以把新版本的行作为新事务更新的起点不会有 逻辑冲突.

请注意只有更新事务才需要重试 --- 只读事务从来没有串行化冲突.

可串行化事务级别提供了严格的保证:每个事务都看到一个完全一致的数据库 的视图.不过,如果并行更新令数据库不能维持串行执行的样子,那么应用 必须准备重试事务。因为重做复杂的事务的开销可能是非常可观的,所以我们 只建议在更新查询中包含足够复杂的逻辑,在读已提交级别中可能导致错误 的结果的情况下才使用. 最常见的是,可串行化模式只是在这样的情况下是必要的:一个事务连续做若干个查询, 而这几个查询必须看到数据库完全一样的视图。
0 请登录后投票
   发表时间:2004-12-29  
对于firebird 或interbase来说,所有的操作必须处在事务中。
且只支持1Read committed 2repeatable read 3Serializable 这3钟事务隔离度

事务隔离度处于1Read committed 2repeatable read 时,读操作不会相互阻塞。读和写也不会相互阻塞,这样使用的时候可以并发n个读操作(只要内存够大,硬盘够快),写操作用单独的线程管理,这样就不会冲突,速度也贼快。。

select要限制条件,要么是top n风格的(fb15支持 select first(100) from table_name),要么限制pk的范围。。记录太大了网络也受不了

不爽的就是没有日志。。怕断电。。。

mysql的innodb的备份和恢复好像是要钱的。。。
等postgresql8吧。。。
0 请登录后投票
   发表时间:2005-01-21  
如果仅仅是select,我也不会启用事务的。但如果是对数据库进行了IUD操作后再Select,就会使用事务处理。因为一旦事务操作失败可以回滚,这是十分重要的。
0 请登录后投票
   发表时间:2005-01-21  
不启动事务,可能会出现幻读(如果刚好在查询中别的查询改了数据),因为不启动事务的话就会使用墨认的事务级别,不过大部分情况我们是不用在意这个的。
0 请登录后投票
   发表时间:2005-01-26  
被的数据库我不该说,DB2还是需要启动事务和COMMIT掉的,这个隔离级别等没关系,只和性能有关,为什么需要COMMIT?这个DB2的实现有关。看看这篇:
引用




许多正在使用 DB2 的人都认为对只读访问使用 COMMIT 是没有必要的。这些人认为,因为他们没有更改任何数据,所以没有必要使用 COMMIT。但是 COMMIT 不仅应用更改, 它还释放锁和声明(claim), 所以 COMMIT 会影响应用程序可用性。

我要解释在将 COMMIT 引入到进行只读访问的应用程序时要注意的可用性考虑事项以及声明和放弃(drain)的概念。我将使用的示例特定于只读环境;这些示例并不旨在讨论有关更新活动的 COMMIT 考虑事项。本文的范围限于在以下环境中对只读应用程序进行 COMMIT 操作:可能在该环境中执行进行更新的实用程序和其它应用程序。本文中,我将使用术语“应用程序可用性”来表示关于允许读访问的可用性。

获得声明


如果您熟悉 DB2,那么您可能对于对象(例如行、页面、表或表空间)上的不兼容锁(例如 X 和 S)会降低并发性有相当的了解。但如果您将只读程序与 ISOLATION UnCOMMITted Read(UR)联编在一起,会怎么样呢?这样做意味着您的只读程序可能不会获得任何锁。要牢记的是,如果涉及到大量删除,则与 ISOLATION UR 联编的程序确实会获得针对表或表空间的 MASS DELETE 锁。如果表空间使用工作文件数据库,则还会获得针对它的 IX 锁。但是,本文讨论只读方案。

所以问题是,在以 ISOLATION UR 隔离级别绑定进行只读访问的应用程序中使用 COMMIT 会对整个应用程序的可用性产生任何影响吗?

回答是:会产生影响。

尽管以 ISOLATION UR 隔离级别进行绑定时,进行只读访问的应用程序没有获得锁,但它确实声明了这个对象。而正如我将在本文稍后解释的,声明可能就是导致可用性降低的原因。正如我前面提到的,COMMIT 释放了声明。

现在,在应用程序可用性极其重要的环境中,当更新活动很少时,最好安排数据库维护作业(诸如 REORG 或 IMAGE COPIES)。这样做增加了 REORG 作业与长时间运行的只读批处理作业并发运行的可能性 — 这就是可用性成为问题的原因所在。

REORG 类似于其它实用程序,(在某个阶段)需要一个针对对象的 drain 锁,它会一直等待,直到能获得一个这样的锁为止。放弃是一种用于接管对象并序列化对它的访问的机制。当释放了对象上的所有声明,而且预先不存在 drain 锁时,就可以获得 drain 锁。Drain 请求器防止实用程序获得已放弃对象上的任何新声明。

从应用程序观点来看,您可能对可用性问题是如何结束的感到疑惑。REORG 作业将一直等待,直到应用程序的作业完成为止吗?如果在 REORG 实用程序可以请求放弃之前,应用程序获取了声明,则它会一直等待。但是如果 REORG 先请求放弃,那么它就不会等待。

当然,在启动 REORG 实用程序之前可以初始化应用程序的作业,以确保 REORG 会等待批处理作业。但是,如果还有另一个批处理作业或在线事务,那么它将会进入等待,因为它不能获取对象的声明。(新作业或事务必需获取声明,但是 Drain 请求器 REORG 会阻止该对象的任何声明。)

作为准备执行的只读程序和 REORG 实用程序(我将解释这两个程序可能会也可能不会同时执行)的结果所产生的应用程序非可用性程度取决于:


REORG 上指定的 SHRLEVEL OPTION
与 REORG 并发执行的批处理作业的 COMMIT 频率。

REORG 实用程序在终止之前,需要放弃所有声明类。如果 REORG 在执行时使用 SHRLEVEL REFERENCE 或 SHRLEVEL CHANGE,那么会在 SWITCH 阶段发生这个放弃,如果该实用程序执行时使用 SHRLEVEL NONE,那么会在 RELOAD 阶段发生这个放弃。

长时间运行的进程的 COMMIT 频率也会影响可用性。通过引入 COMMIT 就可以释放对象上的声明。注:如果批处理作业正在使用用 WITH HOLD 定义的游标,那么在经过 COMMIT 点之后仍会保留声明。在所有其它情况下,在 COMMIT 时都释放对象的声明。对象声明的持续时间并不取决于计划或包的 BIND 过程中指定的 RELEASE 参数(COMMIT 或 DEALLOCATE)。

表 1 演示了 REORG SHRLEVEL 参数和 COMMIT 频率对应用程序可用性的的影响。为简单起见,我们假设采用一个未分区的表空间。该表还假设批处理作业正在执行,并且在 T1 时间触发 REORG 作业。让我们研究该表中显示的各种情况。

表 1:比较使用 COMMIT 和不使用 COMMIT 时的进程


情况 1. 进程执行时不使用任何中间 COMMIT。使用 SHRLEVEL NONE 的 REORG 作业在 T1 时间启动。

情况 2. 进程执行时不使用任何中间 COMMIT。使用 SHRLEVEL REFERENCE 或 CHANGE 的 REORG 作业在 T1 时间启动。

情况 3. 进程执行时,在 T3 时间进行中间 COMMIT。使用 SHRLEVEL NONE 的 REORG 作业在 T1 时间启动。

情况 4. 进程执行时,在 T3 时间进行中间 COMMIT。使用 SHRLEVEL REFERENCE 或 CHANGE 的 REORG 作业在 T1 时间启动。

情况 5. 进程执行时,不包含任何中间 COMMIT,但与情况 2 相比,执行完成所花的时间比较长。

情况 6. 进程执行时,在 T3 时间进行中间 COMMIT,但与情况 4 相比,执行完成所花的时间比较长。

从表 1 您可以得出如下结论:


不进行中间 COMMIT 的批处理作业在与 REORG(SHRLEVEL NONE)作业并发执行时所产生的非可用性持续的时间最长。非可用性程度取决于进程(最可能的是批处理作业)的持续时间和正在被重组的表空间大小。
从情况 2 和情况 4 中可以看出,当 REORG 与不考虑 COMMIT 频率的批处理作业并发执行时,非可用性时间看起来相同;但是事实并非如此。如果情况 2 中的进程执行的时间更长,它会导致 REORG 等待的时间更长,从而增加应用程序非可用性时间。如果情况 4 中也出现类似的情况,它导致很少的进程等待时间(T5 时间),而 REORG 实用程序在切换(switch)阶段执行并在该阶段终止。但是,不会增加非可用性时间(请参阅情况 5 和情况 6)。
如果进程的等待时间超过了 DSNZPARM 中的 IRLMRWT 值,那么进程将以 -911(超时)终止。如果应用程序中这种情况很常见,那么请考虑提高 IRLMRWT 值,或将重试逻辑合并到程序中。
如果实用程序的等待时间超过了实用程序的超时时间,那么实用程序就超时。通过使 UTIMOUT 与 IRLMRWT 相乘可以确定实用程序超时时间。您可能有兴趣通过提高这两个参数中任一个或同时提高这两个参数来提高该实用程序的超时时间。但请谨慎使用这个方法。注:在所有情况中,每当 REORG 作业转至等待状态(等待获得 drain 锁),它会导致应用程序的非可用性。因此,当您在提高实用程序超时值时应该小心,因为它将对可用性产生直接影响。

解决方案


用 SHRLEVEL REFERENCE(允许并发的读操作)或用 SHRLEVEL CHANGE(允许并发的更改操作)方式执行 REORG 实用程序以及使长时间运行的进程发出中间 COMMIT 是解决可用性问题的优秀方案。决定 COMMIT 频率的关键因素是非可用性最大容许量以及 DSNZPARM 中定义的实用程序超时值。COMMIT 频率应该小于非可用性最大容许度。如果大于或等于的话,从本文中您已经了解到,它会导致可能的非可用性(当 REORG 在等待放弃所有声明时,禁止任何新的声明)。

即使 COMMIT 频率低于非可用性最大容许度,但使用 DB2 V6(或更低版本)时,已分区表空间的 SWITCH 阶段也可能会花相当一段时间(以便 IDCAMS 进行重命名),从而导致非可用性。使用 DB2 V7,通过使用 FASTSWITCH YES(缺省值)会缓解该问题。如果 COMMIT 频率大于实用程序超时值,那么它就可能使 REORG 实用程序未完成任何工作就超时了。DB2 V7 引入了两个新选项:DRAIN WAIT(指定覆盖实用程序超时值的时间)和 RETRY(在发出超时之前进行重试的次数),以防止 REORG 超时。

做就是了


虽然在引入中间 COMMIT 时会产生与之相关的额外开销(至少需要一个额外的数据库调用),但是通过仔细规划,用相当低的投资(CPU 开销)获得相当好的回报(应用程序可用性)是可能的。


0 请登录后投票
   发表时间:2005-03-07  
有一种很少发生的情况, select 只读之后,也需要commit。
下面三种情况都同时满足:

1. Local Transaction.
没有用到 JTA, XA, 不是Global Transaction.

2. 用了连接池,而且connection close不自动调用commit。
这个时候,close只是把 connection 放回到 pool中,并没有真正关闭。下次还可能被取出重用。

3. Transaction Isolation Level = Serializable
大多数情况下,Transaction Isolation Level = Read Committed.  这个时候,select 之后,不需要调用commit 释放资源 (当时快照等)。
某些情况下,Transaction Isolation Level = Serializable.  这个时候,select之后,需要调用commit 释放资源 (当时快照等)。
如果这个connection 没有commit 就放回到connection pool,那么,会有两个后果,
(1) 这个connection 将一直持有数据库资源 (当时快照等)。
(2) 这个connection 被取出来重用的时候,也只能读取 数据库当时快照的数据,而读取不到 最新的数据(比如,其它connection commit 的数据)。
0 请登录后投票
   发表时间:2005-03-07  
第一楼问“如果只提交一个查询”
第二楼说的是“查询也要用事务”
没有冲突啊
多个查询不用事务可能会有问题
当然一个查询就没有必要了
那为什么要把事情分成需要事务和不需要事务两种呢
增加开发效率不是一直提倡比性能要重要的东西吗

另外,提交一个查询(又或者提交一个update)本来就是一个事务
autocommit=true
每一句sql都是一个事务,又何来不需要事务
如果用spring的话
就会自动设置为autocommit=false
那不用startTransaction和commit一下
怎么会有数据操作呢
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics