我需要读取一个包含 300 多百万个
TIMESTAMP
的数据库表(已排序的行),就像 Java 中的 LocalDateTime
一样,并且我需要获取所有这些表的单个哈希值。然后,我需要从迁移的数据库(不同品牌和所有)中获取相同的哈希值并获取哈希值进行比较。
我想我可以使用
LocalDateTime.toString()
来获取一个字符串,然后获取它们的字节并使用它们来更新哈希值。
但是,它是 3 亿个值……两倍。我将在数据库迁移期间运行它,所以希望它应该很快。
获取 LocalDateTime 的字节表示的有效方法是什么?
很大程度上取决于您的数据库引擎。当询问“我需要使用数据库引擎每天两次操作 X 几百万次”时,通常就是这种情况。 SQL 是语法标准,而不是性能配置文件。
对于这个答案的其余部分,我将假设使用 postgreSQL。
PostgreSQL 在这方面遵循 SQL 标准,并将类型
timestamp
视为 timestamp without time zone
的缩写,这确实与 java 类 java.time.LocalDateTime
匹配得最好。
但是,即使您只是在 JDBC 结果集上调用
rs.getObject(1, java.time.LocalDateTime.class)
,也必须发生大量转换。是的,JDBC 4.2 规范将保证这一点有效,并且,是的,这会导致有保证的无损转换。然而,Java 的 LDT 类型具有大量字段(一个代表年份,一个代表月份,等等),而 psql 将数据位打包为 8 字节序列。因此,如果您要求 JDBC 为您提供一个 LocalDateTime
对象,那么您就已经几乎输掉了比赛——这就是在做大量不需要的工作。事实上,如果目标是产生哈希值,那是非常痛苦的。
所以,如果可以的话,不要这样做。让数据库开始工作:
SELECT EXTRACT(epoch FROM TIMESTAMP '1999-01-08 04:05:06')
然后您可以通过 JDBC
rs.getLong(1)
获取该信息。
这将为您提供 915768306。对于 UTC 时区,这是自纪元(1970 年 1 月 1 日午夜)以来经过的秒数。如果您发现毫秒值相关,则必须选择
1000 * EXTRACT
:
try (var stmt = con.createStatement()) {
try (var rs = stmt.executeQuery("SELECT 1000 * EXTRACT(epoch FROM TIMESTAMP '1999-01-08 04:05:06')")) {
rs.next();
long a = rs.getLong(1);
long b = LocalDateTime.of(1999, 1, 8, 4, 5, 6).toInstant(ZoneOffset.UTC).toEpochMilli();
assertEquals(a, b); // this will hold.
}
}
这样更快吗?大概。当然,将 LDT 转换为字符串并进行散列处理会使情况变得更糟。 如果必须的话,只需在 LDT 上调用 .hashCode() 即可。