标准SELECT/FROM/WHERE/IN查询的Cassandra模式



Cassandra很新-我有这样的数据:

<geohash text, category int, payload text>

我想运行的唯一查询是:

SELECT category, payload FROM table WHERE geohash IN (list of 9 geohashes)

在这种情况下,什么是最好的模式?

我知道我可以简单地将我的geohash作为主键并使用它,但有更好的方法吗?

定义PRIMARY KEY (geohash, category, payload)有什么好处?

这取决于每行数据的大小(geohash文本、category int、payload文本)。如果您的有效负载大小没有达到几十Mb,那么您可能希望通过使用人工bucketId int将更多geohash值放入同一分区中,这样您的查询就可以在服务器上执行。架构如下所示geohash text,bucketId int,category int,payload text其中分区键为goehash和bucketId。建议有一个相当大的分区<=100Mb,所以您不必查找太多分区。更多信息请点击此处。

如果您在(geohash, category, payload)上有主键,那么您可以在category and payload上按升序对数据进行排序。

因此,根据查询,听起来您正在考虑一个CQL模式,如下所示:

CREATE TABLE geohash_data (
  geohash text,
  category int,
  data text,
  PRIMARY KEY (geohash)
);

在Cassandra中,PRIMARY KEY中的第一列(在本例中也是唯一一列)是Partition Key。分区键是用于在集群中分发数据的键。因此,当您执行SELECT ... IN ()查询时,基本上是在9个不同分区中查询数据,根据集群的大小、复制因素和用于查询的一致性级别,这些分区最终可能会查询至少9台服务器(甚至更多)。为什么这很重要?

  1. 延迟:查询中涉及的分区(以及副本/服务器)越多,速度较慢的服务器就越有可能对数据返回的速度产生负面影响
  2. 可用性:查询中涉及的分区(以及副本/服务器)越多,单个服务器宕机就越有可能使查询根本无法得到满足

这两种情况都是糟糕的,所以(正如Toan在他的回答和他提供的链接中正确指出的那样),我们尝试在Cassandra中进行数据建模,这样我们的查询将访问尽可能少的分区(从而访问副本/服务器)。这对你的场景意味着什么?在不了解所有细节的情况下,很难确定,但让我对你的场景进行一些猜测,并给你一个我如何解决它的例子。

听起来你可能已经提前知道了可能的geohash值的列表(也许它们以预定义网格的某个规则间隔)。这听起来也像是你在查询9个地理哈希值,因为你正在进行某种"接近度"搜索,试图获得给定点周围每个方向上9个地理散列的数据。

如果是这样的话,诀窍可能是在写入时将数据反规范化为针对读取而优化的数据模型。例如,像这样的模式:

CREATE TABLE geohash_data (
  geohash text,
  data_geohash text,
  category int,
  data text,
  PRIMARY KEY (geohash, data_geohash)
);

当你INSERT一个数据点时,你会计算出你期望数据显示在结果中的周围区域的地理哈希。然后,对于您计算的每个地理哈希,您将多次INSERT数据。因此,geohash的值是您希望它显示在查询结果中的计算值,data_geohash的值是插入数据的实际值。因此,对于给定的geohash,您的分区中会有多个(最多9?)行,这些行表示周围地理散列的数据。

这意味着您的SELECT查询现在不必执行IN并命中多个分区。您只需在WHERE geohash = ?中查询要搜索的点即可。