1

我正在开发一个服务器,它应该接收来自数百个业务部门的夜间报告。报告当前是加密的 csv 文件。每天的报告总数应达到 500 000 到 1 000 000 条记录,这些记录将保存到数据库中以备后用。

我为每个传输创建了一组 PreparedStatements。这些语句用于在执行和提交之前批处理 50 条记录。每条记录可能会导致多达 20 次数据库插入。当传输被排队并逐一处理时,一切都很好。

当我尝试同时执行此操作时,我注意到不同的线程获得了完全相同的 PreparedStatements 实例。这导致了以下问题

  1. 多个线程将语句添加到同一个批处理中
  2. 当任何线程决定是时候执行批处理时,批处理正在执行
  3. 当数据库不满足它的约束时调用提交,因为一些线程没有时间使用一些语句

问题是:有没有办法强制创建一个准备好的语句,而不是重用语句缓存中的现有语句?

如果没有,有没有比通过

  • 为没有语句/连接池的批次创建单独的数据源
  • 从数据库中删除约束;插入顺序不再重要
  • 强制顺序处理

编辑:尝试澄清问题

假设有线程 T1 和 T2。假设有准备好的语句 S1 和 S2。假设有批次 B1 和 B2。

每次使用 S1 时,都会将其添加到 B1。每次使用 S2 时,都会将其添加到 B2。提交时,每个外键约束必须在 S2 之前提交 S1。

问题发生时

  • T1 兴高采烈地处理传输
  • T2 无辜地处理传输
  • T1 使用语句 S1 将 s1a 添加到包含 s1a 的批次 B1
  • T1 使用语句 S2 将 s2a 添加到包含 s2a 的批次 B2
  • T1 决定是时候提交了
  • T1 提交包含 s1a 的批次 B1
  • T2 使用 S1 将 s1b 添加到包含 s1b 的批次 B1
  • T2 使用 S2 将 s2b 添加到包含 s2a、s2b 的批次 B2
  • T1 提交包含 s2a、s2b 的批次 B1
  • 数据库说“不”,因为 s2b 在 s1b 之前提交,这在外键中是被禁止的。

这可以通过手动同步以及在答案中指出来避免,但是我仍然必须单独跟踪每个批次的大小,而不是对每个线程应用本地逻辑。

4

3 回答 3

1

您是否尝试在单个连接实例中使用多个语句?IMO,建议为您描述的行为使用连接池。另一种方法是手动同步。

于 2009-08-25T09:57:28.777 回答
1

该解决方案是特定于供应商的。

如果您的代码在 servlet 下运行,那么您可以通过在 webapp 中配置数据源来解决您的问题。我已经在 Tomcat 下使用 Oracle 驱动程序做到了这一点,但我确信其他应用程序服务器也有类似的方式来配置连接池。

如果您的代码是独立的,那么您将不得不使用特定于供应商的 API。由于您将 Oracle 作为您的生产数据库,这里有一个 Oracle JDBC 驱动程序的快速示例:

import oracle.jdbc.OracleConnection;

...

public static void disableStatementCaching(java.sql.Connection conn)
        throws SQLException {
    ((OracleConnection)conn).setImplicitCachingEnabled(false);
}

...

有关详细信息,请参阅Oracle 10.2 的JDBC 开发指南

于 2009-08-25T14:48:18.443 回答
0

我目前的解决方案是停止担心并开始喜欢共享批次。我已将处理算法分为两个阶段

  1. 解析一组 N 条记录并以中间格式保存它们
  2. 将锁授予当前线程时,将 N 条记录集作为批处理保存

这允许解析为并发和批处理顺序。我只需要找到一个最佳位置来最小化线程之间的等待时间。

对甜蜜点的追求可能会导致实现某种两阶段锁定方案,即让每个线程随心所欲地进行操作,并在提交时确保所有线程在实际批处理执行之前都已完成其当前记录。

在后一种解决方案中,可能需要同步每个 PreparedStatement 的参数设置,尽管我尚未测试这是否会导致任何问题。它应该。

于 2009-08-28T05:39:46.233 回答