0

所以我有一点性能问题。我制作了一个构建数据库的java程序。问题是在加载数据时。我正在将 5,000 个文件加载到 sql 数据库中。当程序启动时,它可以在 10 分钟内处理大约 10% 的文件,但是随着进程的进行它会变得慢得多。目前以 28% 的速度按目前的速度在 16 小时内完成。然而,这一速度正在显着放缓。

我的问题是为什么程序在运行时会逐渐变慢以及如何解决这个问题。

编辑:我有两个版本。一个是带螺纹的(上限为 5 个线程),一个不是。两者之间的差异可以忽略不计。如果有人喜欢,我可以再次发布代码,但我把它拿出来是因为我现在相当确定瓶颈是 MySQL(也适当地重新标记)。我继续使用批量插入。这确实导致了最初的速度提高,但在处理了大约 30% 的数据后,它再次迅速下降。

所以SQL点

  1. 我的所有 64 个表的引擎是 InnoDB 版本 10。
  2. 该表此时大约有 300k 行(约 30% 的数据)
  3. 所有表都有一个“联合”主键。一个 ID 和一个日期。
  4. 查看 MySQL WorkBench 我看到每个线程都有一个查询(5 个查询)
  5. 我不确定时间单位(只是从 MySQL 管理员那里读取),但是检查文件是否已插入的查询需要 300。(这个查询应该很快,因为它是从 MyTable 限制 1 到 1 的 SELECT MyIndex,其中 Date = 日期。)因为我一直在启动和停止我在此检查中构建的程序以查看文件是否已插入。这样我就可以在每次更改后启动它,看看是否有任何改进,而无需再次启动该过程。
  6. 我相当肯定性能的下降与表格的大小有关。(我现在可以停止和启动程序,但该过程仍然很慢。只有当表格很小时,该过程才能以可接受的速度进行。)
  7. 请,请询问,我会发布您需要的任何信息。

完毕!好吧,我只是让它运行了它需要的 4 天。谢谢大家的帮助。

干杯,

——奥兰

4

5 回答 5

1

您可以使用从文件直接插入到数据库(在此处阅读)。它工作得更快。当我为 postgres 做同样的事情时,我的性能提高了 20 倍。

还可以下载您的套件分析器并分析您的应用程序的性能。然后你会看到什么需要你的时间。

于 2012-06-14T06:32:27.040 回答
1

Q1:为什么程序越来越慢?

在您的问题空间中,您有 2 个系统交互:一个从文件系统读取并生成数据的生产者,以及一个将该数据转换为记录并将它们存储在数据库中的消费者。您的代码目前正在硬链接这两个进程,并且您的系统以两者中最慢的速度运行。

在您的程序中,您有一个固定的到达率(1/秒 - 当您运行超过 10 个线程时的等待)。如果您在要填充的表中有索引,则随着表变大,插入将花费更长的时间。这意味着,虽然您的到达率固定为 1/秒,但您的退出率却在不断增加。因此,您将创建越来越多的线程来共享相同的 CPU/IO 资源,并且每单位时间完成的事情越来越少。创建线程也是一项非常昂贵的操作。

Q2:这可能与我如何从字符串构造查询有关吗?

只是部分。您的字符串操作是系统中的固定成本。它增加了服务一个请求的成本。但是字符串操作是 CPU 受限的,而你的问题是 I/O 受限的,这意味着改进字符串处理(你应该)只会略微提高系统的性能。(见阿姆达尔定律)。

Q3:如何解决(性能问题)

(FileReaderProducer) --> 队列 --> (DBBulkInsertConsumer)

  • 不要创建新线程。使用 java.util.concurrent 包提供的工具,比如上面提到的执行器服务或完成服务。对于“裸”线程池,请使用Executors工厂。

  • 对于这个特定的问题,拥有 2 个单独的线程池(一个用于消费者,一个用于生产者)将允许您调整系统以获得最佳性能。文件读取随着并行化而提高(直到您的 I/O 限制),但数据库插入不是(I/O + 索引 + 关系一致性检查),因此您可能需要将文件读取线程的数量(3-5)限制为匹配插入率 (2-3)。您可以监控队列大小以评估系统性能。

  • 使用 JDBC 批量插入:http: //viralpatel.net/blogs/batch-insert-in-java-jdbc/
  • 使用 StringBuilder 而不是字符串连接。Java 中的字符串是不可变的。这意味着每次你这样做myString += ",":您正在创建一个新字符串并使旧字符串可以用于垃圾收集。反过来,这会增加垃圾收集性能损失。
于 2012-06-14T11:05:43.320 回答
0

您的代码中有很多东西可能会导致速度问题,并且您怀疑字符串起作用是正确的。

以这段代码为例:

字符串行String = ""; // - 末尾没有逗号的额外 1 for (int i = 0; i <= numberOfRows - 3; i++) { rowsString += "(DATA), \n"; } 行字符串+=“(数据)”;

根据有多少行,这是一个潜在的瓶颈和记忆猪。我认为最好在这里使用 StringBuilder 。我看到很多更适合 StringBuilders 的字符串操作。我是否可以建议您阅读一下字符串处理并优化这些内容,尤其是在您使用 += 字符串的地方?

那么下一个问题就是你的桌子是怎么设计的?可能会有一些事情使您的插入变慢,例如 varchars 的默认长度不正确、没有索引或索引过多等。

于 2012-06-14T06:29:12.280 回答
0

大多数数据库更有效地加载数据,如果,

  • 您批量加载数据,
  • 您加载的线程数量相对较少,例如一两个。

随着您添加更多线程,您会增加更多开销,因此您希望它会变慢。

尝试使用具有固定大小池(例如 2-4)的 ExecutorService,并尝试在事务中一次加载 100 个数据。

于 2012-06-14T07:17:30.367 回答
0

您有几个很好的尝试和测试的选项来加速数据库访问。

  1. 为您的线程使用一个ExecutorService。这可能对速度没有帮助,但它会帮助您实现以下内容。
  2. 保留 aThreadLocal Connection而不是为每个文件建立新连接。另外,显然,不要关闭它。
  3. 创建一个PreparedStatement而不是每次都制作一个新的。
  4. 批处理您的语句执行。
于 2012-06-14T13:34:57.297 回答