【问题标题】:JDBC - setAutoCommit for read only operationJDBC - setAutoCommit 用于只读操作
【发布时间】:2015-06-18 08:34:45
【问题描述】:

假设我有一个创建数据库连接的通用方法:

Connection getConnection() throws SQLException {
    Connection con = ... // create the connection
    con.setAutoCommit(false);
    return con;
}

我将setAutoCommit(false) 调用放在这里,这样该方法的调用者就不必担心设置它。但是,如果调用者执行的操作只是读取数据,这是一种不好的做法吗?有额外的开销吗?

我个人的看法是,最好将逻辑集中在一个地方,这样调用者就不必设置自动提交,这样可以避免代码冗余。我只是想确保它不会为只读操作带来任何不必要的开销。

【问题讨论】:

    标签: java jdbc connection


    【解决方案1】:

    我将 setAutoCommit(false) 调用放在这里,这样该方法的调用者就不必担心设置它。

    这很好 IMO,我个人认为永远不应该在应用程序中启用自动提交模式。所以我的建议是关闭自动提交。

    但是,如果调用者执行的操作只是读取数据,这是一种不好的做法吗?有额外的开销吗?

    从严格的性能角度来看,它为每个具有开销并可能降低应用程序性能的 SQL 语句启动和结束数据库事务。

    顺便说一下,根据javadoc,SELECT语句受setAutoCommit(boolean)的影响:

    设置此连接的自动提交 模式到给定的状态。 如果一个 连接处于自动提交模式, 那么它的所有 SQL 语句都将是 作为个人执行和承诺 交易。否则,它的 SQL 语句被分组为 被终止的交易 调用方法 commit 或 方法回滚。默认情况下,新 连接处于自动提交模式。

    提交发生在语句的时候 完成。 语句的时间 完成取决于 SQL 的类型 声明:

    • 对于 DML 语句,例如 Insert、Update 或 Delete 以及 DDL 语句, 声明完成后 它已完成执行。
    • 对于 Select 语句,语句完成时关联结果 集已关闭。
    • 对于 CallableStatement 对象或返回多个语句的语句 结果,语句完成 当所有关联的结果集 已关闭,所有更新 计数和输出参数已 已检索。

    【讨论】:

    • 我不同意开始和结束事务的性能问题。这从来不是我的经验,而是几个长事务比许多小事务造成更多的瓶颈。你有任何相反的证据吗?
    【解决方案2】:

    自动提交对SELECT 查询没有任何价值。但是关闭自动提交确实是一种更常见的做法。您经常希望在事务中触发查询。大多数连接池也默认将其关闭。但是,我建议将其设置为连接管理器的配置设置和/或重载采用布尔参数的方法,以便在这种情况下您至少可以 any 控制它。 p>

    【讨论】:

    • @BalusC select 查询可以引入共享锁,或者在事务期间需要读取元组的额外副本(取决于隔离级别)。你不是说自动提交 doesselect 查询有价值吗?
    【解决方案3】:

    这是一个老问题,但我想就这个问题给出不同的意见。

    性能

    事务的性能开销因并发控制机制而异:通常是多版本并发控制或锁定。其他答案中表达的担忧似乎归结为结束事务的成本,但根据我的经验,最大的痛苦是长时间运行的事务,这可能会导致性能瓶颈。例如,如果 DBMS 使用锁定,则在涉及该表的事务终止之前,无法更新表的某些部分。在 Oracle 等系统中更令人沮丧的是,DDL 操作(ALTER TABLE 等)必须等到使用该表的所有事务都结束,从而导致麻烦的超时。因此,如果您只是使用SELECTs,请不要认为您的交易不会受到惩罚。

    约定

    关闭自动提交行为的一个微妙问题是您正在更改默认值,因此使用您的代码的其他人可能不会期待它。 真的很容易在函数中留下一条意外路径,而不是以明确的 commitrollback 结尾,这可能会导致后续调用的函数出现不可预测的行为。相反,我看到的大部分 DB 接口代码在每个函数中都包含一条语句,自动提交行为非常适合。事实上,我遇到的许多多语句函数都可以重写为带有更多 SQL 知识的单语句 - 可悲的是,在 Java 中实现的连接的近似值很差。

    根据合理的经验,我个人对任何调用数据库的函数的偏好如下:

    • 保持自动提交的默认 JDBC 行为;
    • 当您的函数包含多个 SQL 语句时,通过在每个函数的开头设置setAutocommit(false) 并在结尾调用commit()(或rollback(),如果合适)来使用显式事务,最好是rollback()catch 块中;
    • 通过将setAutocommit(true) 放在将您的JDBC 调用包装在函数中的finally 块中来强制执行默认设置(与PHP/PDO 等API 不同,在commit()/rollback() 之后,JDBC 不会为您执行此操作);
    • 如果您感觉更加防御,请在每个函数的开头明确设置您选择的 setAutocommit(true)setAutocommit(false)

    【讨论】:

    • “保持默认的自动提交 JDBC 行为” - 不太同意这种方法。我在写java代码的时候总是把它关掉,原因可以参考这里:asktom.oracle.com/pls/asktom/…即使是单语句事务,保持关闭,让客户端代码对事务有完全的控制权更清楚。跨度>
    • @dcp 取决于您对交易的理解程度。许多人不这样做,但仍然编写代码来处理数据库(想想所有的灯堆栈,以及像 Ruby 中隐藏事务行为的所有包装器代码)。自动提交避免了语句看似有效但在会话之间没有持续存在的有害错误。
    • 我无法与你争论,因为缺乏对数据库的理解肯定是个问题。我知道当我们采访人们时,他们可能会知道 .Net/Java/Whatever language/etc。很好,但是让他们写一个简单的 SQL 语句或解释事务隔离级别,他们不知道如何回答。我认为这是一个大问题,因为关系数据库仍然是关键,人们需要了解如何使用它们。这就是为什么我坚持关闭自动提交,并期望开发人员提供更多,并迫使他们不要依赖工具。
    • dcp - 您提供的询问汤姆链接似乎没有提供任何关闭自动提交的理由,除了名义上的汤姆称其为“愚蠢”。有什么实际原因吗?也许是另一篇问汤姆的文章,他在其中指出了短期交易的一些性能或功能问题?
    • @Tom Dibble - 这是 asktom 的另一个链接:asktom.oracle.com/pls/apex/… 如果您向下滚动大约一半,您会看到 Tom 的 cmets:“您应该始终完成自己的工作 - 您应该明确提交或回滚根据您的逻辑。永远不要依赖环境来做这件事,它可能会做一些不同的事情“。底线是事务代表客户端打包在一起的一个工作单元,因此,客户端应该定义何时提交该工作。
    【解决方案4】:

    我永远不会在应用程序的任何地方将 autoCommit 设置为 true。 与侧面相比,性能开销(如果有的话)根本不算什么 autocommit=true 连接的影响。

    您说您永远不会将此连接用于 DML。但这就是意图,可能由编码标准等维护。但在实践中,可以将此连接用于 DML 语句。这足以让我永远不要设置自动提交。

    Select 语句肯定会占用一些内存/CPU/网络。让自动提交的开销成为每个 select 语句的(非常微量的)固定开销,以确保保持应用程序的数据完整性和稳定性。

    【讨论】:

      猜你喜欢
      • 2015-04-13
      • 2016-12-16
      • 1970-01-01
      • 1970-01-01
      • 2019-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多