我在尝试对数据建模时遇到问题,这样我就可以有效地查询Cassandra最近修改的最后10个(实际上是任意数量)记录。每条记录都有一个last_modified_date列,由应用程序在插入/更新记录时设置。
我已从此示例代码中排除了数据列。
主数据表(每条记录只包含一行):
CREATE TABLE record (
record_id int,
last_modified_by text,
last_modified_date timestamp,
PRIMARY KEY (record_id)
);
我尝试创建一个单独的表,该表使用了聚类键顺序。
表格(每条记录一行;仅插入最后修改日期):
CREATE TABLE record_by_last_modified_index (
record_id int,
last_modified_by text,
last_modified_date timestamp,
PRIMARY KEY (record_id, last_modified_date)
) WITH CLUSTERING ORDER BY (last_modified_date DESC);
查询:
SELECT * FROM record_by_last_modified_index LIMIT 10
此解决方案不起作用,因为群集顺序仅适用于具有相同分区键的记录的排序。由于每行都有不同的分区键(record_id),因此查询结果不包含预期的记录。
我尝试的另一个解决方案是简单地查询Cassandra的所有record_id和last_modified_date值,对它们进行排序并选择我的应用程序中的前10条记录。这显然效率低下,不能很好地扩展。
我考虑的最后一个解决方案是对所有记录使用相同的分区键,并使用群集顺序确保记录正确排序。该解决方案的问题在于,数据将无法在节点之间正确分区,因为所有记录都具有相同的分区键。这对我来说似乎不是一件好事。
答案 0 :(得分:1)
CQL查询按字段排序的整个表/视图的唯一方法是使分区键保持不变。正好一台机器(复制因子的次数)将保存整个表。例如。使用partition INT
分区键始终为零,并将聚类键作为需要排序的字段。您应该观察类似于单节点数据库的读/写/容量性能,即使您的群集中有更多节点,也会在已排序字段上显示索引。这并没有完全打败Cassandra的目的,因为它有助于将来扩展。
如果性能不足,那么您可以通过增加分区种类来决定扩展。例如。当使用4个节点时,从0,1,2,3中随机选择插入将达到四倍的读/写/容量性能。然后找到" 10最近的"您必须手动查询所有4个分区并对结果进行合并排序。
理论上,Cassandra可以为INSERT提供动态node-count-max-modulo分区键,为SELECT提供合并排序(ALLOW FILTERING
)。
允许写入,读取和存储容量随节点数量线性扩展,Cassandra要求:
如果我理解正确,结果是全表单字段排序查询总是需要从整个集群读取并合并排序。
注意物化视图等同于表格,它们没有任何神奇的属性可以使它们更好地进行全局排序。请参阅http://www.datastax.com/dev/blog/we-shall-have-order,其中Aaron Ploetz同意cassandra和cql无法在没有分区和扩展的情况下对一个字段进行排序。
CREATE KEYSPACE IF NOT EXISTS
tmpsort
WITH REPLICATION =
{'class':'SimpleStrategy', 'replication_factor' : 1};
USE tmpsort;
CREATE TABLE record_ids (
partition int,
last_modified_date timestamp,
record_id int,
PRIMARY KEY((partition), last_modified_date, record_id))
WITH CLUSTERING ORDER BY (last_modified_date DESC);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 1, DATEOF(NOW()), 100);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 2, DATEOF(NOW()), 101);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 3, DATEOF(NOW()), 102);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 1, DATEOF(NOW()), 103);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 2, DATEOF(NOW()), 104);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 3, DATEOF(NOW()), 105);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 3, DATEOF(NOW()), 106);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 3, DATEOF(NOW()), 107);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 2, DATEOF(NOW()), 108);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 3, DATEOF(NOW()), 109);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 1, DATEOF(NOW()), 110);
INSERT INTO record_ids (partition, last_modified_date, record_id) VALUES ( 1, DATEOF(NOW()), 111);
SELECT * FROM record_ids;
-- Note the results are only sorted in their partition
-- To try again:
-- DROP KEYSPACE tmpsort;
请注意,如果没有WHERE
子句,您将获得令牌(分区键)顺序的结果。见https://dba.stackexchange.com/questions/157537/querying-cassandra-without-a-partition-key
如果我理解正确的话 - 在任何给定时间,CockroachDB都会在单调递增数据到单个节点上同样具有瓶颈读/写性能,但存储容量会线性扩展。还有其他范围查询,例如"最老的10"或者"在日期X和日期Y"之间会在更多节点上分配负载而不是Cassandra。这是因为CockroachDB的数据库是一个巨大的排序键值存储,只要一系列排序数据达到一定大小,它就会被重新分配。
答案 1 :(得分:1)
我认为接受的解决方案还有另一个问题。 如果您有多个副本,则不保证插入按顺序结束。
来自datastax docs:
now() - 在协调器节点中,在执行语句时以毫秒为单位生成新的唯一timeuuid。 timeuuid的时间戳部分符合UTC(通用时间)标准。此方法对于插入值很有用。 now()返回的值保证是唯一的。
如果您有多个副本,则还有多个协调节点,因为可以选择任何节点作为协调节点。这意味着由于节点上的任何小的时间变化,您的插入不正确。 因此,实际上稍后在参考帧中发生的一个插入可能在先前插入的记录之前进行排序,因为now()只是在协调器节点上生成稍微落后的日期。
您正在尝试对数据进行一致(或单一引用)视图。不幸的是,在分布式环境中,没有一个真实的参考。