Postgres/JDBC/逻辑复制 - 内存不足问题
Posted
技术标签:
【中文标题】Postgres/JDBC/逻辑复制 - 内存不足问题【英文标题】:Postgres/JDBC/Logical replication - out of memory issues 【发布时间】:2018-02-24 08:12:45 【问题描述】:我正在开发一个连接到逻辑复制槽的应用程序,以使用 WAL 事件。然后将这些 WAL 事件转发到 MQ 代理。这很好用,但我注意到一段时间后我的内存不足。我设法将问题最小化为负责获取 WAL 事件的代码。它发生在以下代码中:
final Properties properties = new Properties();
PGProperty.USER.set(properties, "user");
PGProperty.PASSWORD.set(properties, "password");
PGProperty.ASSUME_MIN_SERVER_VERSION.set(properties, "9.4");
PGProperty.REPLICATION.set(properties, "database");
PGProperty.PREFER_QUERY_MODE.set(properties, "simple");
while (true)
Connection connection = null;
PGConnection PGConnection = null;
PGReplicationStream stream = null;
try
connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/db", properties);
PGConnection = connection.unwrap(PGConnection.class);
stream = PGConnection.getReplicationAPI().replicationStream().logical().withSlotName("slot").start();
while (true)
final ByteBuffer buffer = stream.read();
// ... logic here ... (disabled during memory test)
stream.setAppliedLSN(stream.getLastReceiveLSN());
stream.setFlushedLSN(stream.getLastReceiveLSN());
catch (final SQLException e1)
Logger.getLogger(getClass()).error(e1);
if (stream != null)
try
stream.close();
catch (final SQLException e2)
Logger.getLogger(getClass()).error(e2);
if (connection != null)
try
connection.close();
catch (final SQLException e2)
Logger.getLogger(getClass()).error(e2);
我注释掉了解析消息并将其转发到 MQ 代理的逻辑,因为没有这个也会发生内存不足。
我也尝试通过使用轮询方法readPending()
而不是阻塞方法read()
(如https://jdbc.postgresql.org/documentation/head/replication.html所示)来更改此示例,但问题仍然存在。
我还注意到,一段时间后,应用程序的 CPU 使用率达到 100%。这一定是由底层库引起的,因为此时read()
仍在正常处理(也就是说,它按顺序处理每个 WAL 事件)。
在这些测试期间,我正在以低速率执行 INSERT
和 UPDATE
查询。
我正在使用以下依赖项:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.1.4</version>
</dependency>
应用程序在 Tomcat8 容器中作为 WAR 运行。
知道发生了什么吗?
更新 1
我知道发生了什么,但目前无法解释。我会详细介绍。
如前所述,我每 10 秒进行一次 INSERT
和 UPDATE
查询。这些查询导致 645 个 WAL 事件。所以每 10 秒,我必须read()
645 个事件。一开始,read()
一个事件需要 0(或有时 1)毫秒。一段时间后,需要 1 毫秒。然后,再过一段时间,它需要 2 毫秒。等等……
所以过了一会儿,我无法在 10 秒内read()
645 个事件,因为read()
所需的时间不断增加。这解释了 100% 的 CPU 使用率和内存不足。
我仍然不确定如何解释以及如何解决此问题。我会继续调查的。
更新 2
我尝试在循环末尾添加buffer.clear()
,但没有成功。我仍然遇到 100% CPU 和内存问题。正如预期的那样,因为缓冲区是一个局部变量,所以它在每次循环之后都会被 GC 处理。但我认为无论如何测试都是个好主意。
【问题讨论】:
【参考方案1】:我找到了内存不足的原因。我正在使用decoderbufs
解码输出插件https://github.com/xstevens/decoderbufs 进行测试。当用内置的test
插件或wal2json
(https://github.com/eulerto/wal2json)替换时,我没有这些问题。
我会尽量通知decoderbufs
插件的作者。
【讨论】:
以上是关于Postgres/JDBC/逻辑复制 - 内存不足问题的主要内容,如果未能解决你的问题,请参考以下文章
我出现内存不足错误,如何解决?Permgen 空间区域是啥意思?是啥原因造成的? [复制]
Postgres / JDBC 与 pgjdbc-ng:将 EAN 类型写入数据库