Cassandra:使用 DataStax Java 驱动程序选择一系列 TimeUUID
Posted
技术标签:
【中文标题】Cassandra:使用 DataStax Java 驱动程序选择一系列 TimeUUID【英文标题】:Cassandra: Selecting a Range of TimeUUIDs using the DataStax Java Driver 【发布时间】:2015-05-22 04:19:38 【问题描述】:我们正在使用 Cassandra 解决的用例是这样的:我们需要检索在过去 90 天内某个时间范围内已更新的实体 UUID 列表。想象一下,我们正在构建一个文档跟踪系统,因此我们的相关实体是一个 Document,其键是一个 UUID。
在这个用例中我们需要支持的查询是:查找在 StartDateTime 和 EndDateTime 之间发生变化的所有 Document UUID。
问题 1:支持此查询的最佳 Cassandra 表设计是什么?
我认为答案如下:
CREATE TABLE document_change_events (
event_uuid TIMEUUID,
document_uuid uuid,
PRIMARY KEY ((event_uuid), document_uuid)
) WITH default_time_to_live='7776000';
鉴于我们不能对分区键进行范围查询,我们需要使用token()
方法。因此,查询将是:
SELECT document_uuid
WHERE token(event_uuid) > token(minTimeuuid(?))
AND token(event_uuid) < token(maxTimeuuid(?))
例如:
SELECT document_uuid
WHERE token(event_uuid) > token(minTimeuuid('2015-05-10 00:00+0000'))
AND token(event_uuid) < token(maxTimeuuid('2015-05-20 00:00+0000'))
问题 2:我似乎无法使用 DataStax 的驱动程序获得以下 Java 代码以可靠地返回正确的结果。
如果我运行以下代码 10 次,间隔 30 秒,那么我将在此表中有 10 行:
private void addEvent()
String cql = "INSERT INTO document_change_events (event_uuid, document_uuid) VALUES(?,?)";
PreparedStatement preparedStatement = cassandraSession.prepare(cql);
BoundStatement boundStatement = new BoundStatement(preparedStatement);
boundStatement.setConsistencyLevel(ConsistencyLevel.ANY);
boundStatement.setUUID("event_uuid", UUIDs.timeBased());
boundStatement.setUUID("document_uuid", UUIDs.random());
cassandraSession.execute(boundStatement);
结果如下:
cqlsh:> select event_uuid, dateOf(event_uuid), document_uuid from document_change_events;
event_uuid | dateOf(event_uuid) | document_uuid
--------------------------------------+--------------------------+--------------------------------------
414decc0-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:51:09-0500 | 92b6fb6a-9ded-47b0-a91c-68c63f45d338
9abb4be0-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:53:39-0500 | 548b320a-10f6-409f-a921-d4a1170a576e
6512b960-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:52:09-0500 | 970e5e77-1e07-40ea-870a-84637c9fc280
53307a20-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:51:39-0500 | 11b4a49c-b73d-4c8d-9f88-078a6f303167
ac9e0050-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:54:10-0500 | b29e7915-7c17-4900-b784-8ac24e9e72e2
88d7fb30-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:53:09-0500 | c8188b73-1b97-4b32-a897-7facdeecea35
0ba5cf70-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:49:39-0500 | a079b30f-be80-4a99-ae0e-a784d82f0432
76f56dd0-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:52:39-0500 | 3b593ca6-220c-4a8b-8c16-27dc1fb5adde
1d88f910-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:50:09-0500 | ec155e0b-39a5-4d2f-98f0-0cd7a5a07ec8
2f6b3850-0014-11e5-93a9-51f9a7931084 | 2015-05-21 18:50:39-0500 | db42271b-04f2-45d1-9ae7-0c8f9371a4db
(10 rows)
但如果我再运行这段代码:
private static void retrieveEvents(Instant startInstant, Instant endInstant)
String cql = "SELECT document_uuid FROM document_change_events " +
"WHERE token(event_uuid) > token(?) AND token(event_uuid) < token(?)";
PreparedStatement preparedStatement = cassandraSession.prepare(cql);
BoundStatement boundStatement = new BoundStatement(preparedStatement);
boundStatement.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
boundStatement.bind(UUIDs.startOf(Date.from(startInstant).getTime()),
UUIDs.endOf(Date.from(endInstant).getTime()));
ResultSet resultSet = cassandraSession.execute(boundStatement);
if (resultSet == null)
System.out.println("None found.");
return;
while (!resultSet.isExhausted())
System.out.println(resultSet.one().getUUID("document_uuid"));
它只检索三个结果:
3b593ca6-220c-4a8b-8c16-27dc1fb5adde
ec155e0b-39a5-4d2f-98f0-0cd7a5a07ec8
db42271b-04f2-45d1-9ae7-0c8f9371a4db
为什么没有检索到所有 10 个结果?为了支持这个用例,我需要进行哪些更改才能获得正确的结果?
作为参考,我已针对 dsc-2.1.1、dse-4.6 并使用 DataStax Java 驱动程序 v2.1.6 对此进行了测试。
【问题讨论】:
这个答案可能对你有帮助:so 【参考方案1】:首先,请一次只问一个问题。您在这里的两个问题都可以很容易地独立存在。我知道这些是相关的,但它只会让读者以 tl;dr 为例。
我将首先回答您的第二个问题,因为答案与正确理解数据模型的核心有关。当我插入您的行并运行以下查询时,这就是我得到的:
aploetz@cqlsh:***2> SELECT document_uuid FROM document_change_events
WHERE token(event_uuid) > token(minTimeuuid('2015-05-10 00:00-0500'))
AND token(event_uuid) < token(maxTimeuuid('2015-05-22 00:00-0500'));
document_uuid
--------------------------------------
a079b30f-be80-4a99-ae0e-a784d82f0432
3b593ca6-220c-4a8b-8c16-27dc1fb5adde
ec155e0b-39a5-4d2f-98f0-0cd7a5a07ec8
db42271b-04f2-45d1-9ae7-0c8f9371a4db
(4 rows)
这与您所看到的相似。为什么没有返回全部 10 个?好吧,当我在 SELECT 中包含 token(event_uuid)
时,答案就很明显了:
aploetz@cqlsh:***2> SELECT token(event_uuid),document_uuid FROM document_change_events WHERE token(event_uuid) > token(minTimeuuid('2015-05-10 00:00-0500')) AND token(event_uuid) < token(maxTimeuuid('2015-05-22 00:00-0500'));
token(event_uuid) | document_uuid
----------------------+--------------------------------------
-2112897298583224342 | a079b30f-be80-4a99-ae0e-a784d82f0432
2990331690803078123 | 3b593ca6-220c-4a8b-8c16-27dc1fb5adde
5049638908563824288 | ec155e0b-39a5-4d2f-98f0-0cd7a5a07ec8
5577339174953240576 | db42271b-04f2-45d1-9ae7-0c8f9371a4db
(4 rows)
Cassandra 按哈希令牌值的顺序存储分区键(在您的情况下为event_uuid
)。您可以在使用 token
函数时看到这一点。 Cassandra 使用名为consistent hashing 的进程生成分区令牌,以确保集群分布均匀。换句话说,除非实际(散列)令牌值对您的应用程序有意义,否则按令牌范围查询是没有意义的。
回到你的第一个问题,这意味着你必须找到一个不同的列来进行分区。我的建议是使用称为“日期桶”的时间序列机制。选择日期桶可能会很棘手,因为它取决于您的要求和查询模式......所以这真的取决于您来选择一个有用的。
就本示例而言,我将选择“月份”。因此,我将在 month
上重新创建您的表分区并按 event_uuid 进行集群:
CREATE TABLE document_change_events2 (
event_uuid TIMEUUID,
document_uuid uuid,
month text,
PRIMARY KEY ((month),event_uuid, document_uuid)
) WITH default_time_to_live='7776000';
现在我可以按日期范围查询,同时还可以按month
过滤:
aploetz@cqlsh:***2> SELECT document_uuid FROM document_change_events2
WHERE month='201505'
AND event_uuid > minTimeuuid('2015-05-10 00:00-0500')
AND event_uuid < maxTimeuuid('2015-05-22 00:00-0500');
document_uuid
--------------------------------------
a079b30f-be80-4a99-ae0e-a784d82f0432
ec155e0b-39a5-4d2f-98f0-0cd7a5a07ec8
db42271b-04f2-45d1-9ae7-0c8f9371a4db
92b6fb6a-9ded-47b0-a91c-68c63f45d338
11b4a49c-b73d-4c8d-9f88-078a6f303167
970e5e77-1e07-40ea-870a-84637c9fc280
3b593ca6-220c-4a8b-8c16-27dc1fb5adde
c8188b73-1b97-4b32-a897-7facdeecea35
548b320a-10f6-409f-a921-d4a1170a576e
b29e7915-7c17-4900-b784-8ac24e9e72e2
(10 rows)
同样,month
可能不适用于您的应用程序。所以考虑一下想出一个合适的列来分区,然后你应该能够解决这个问题。
【讨论】:
关于单独问题的要点。我在想我的问题是代码有点不正确,所以这是我的重点,但我想打开响应以包含更正确的设计。关于令牌,我的印象是 timeuuids 生成了顺序令牌,但显然这是不正确的!感谢您提供有关时间分段的建议。我刚刚实施了解决方案,到目前为止效果很好。以上是关于Cassandra:使用 DataStax Java 驱动程序选择一系列 TimeUUID的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 datastax 驱动程序创建 Cassandra 连接池
NoClassDefFoundError - Cassandra 的 datastax java 驱动程序
Apache Cassandra vs Datastax Cassandra [关闭]
com.datastax.oss -> java-driver-core 和 com.datastax.cassandra -> cassandra-driver-core 之间的 Cas