2

我正在设计一个 cassandra 表,我需要能够通过它们的 geohash 检索行。我有一些可行的方法,但我想避免范围查询比我目前能够做的更多。

当前的表模式是这样的,geo_key 包含 geohash 字符串的前五个字符。我使用 geo_key 进行查询,然后对完整的 geohash 进行范围过滤,允许我基于 5 或​​更大长度的 geohash 进行前缀搜索:

CREATE TABLE georecords (geo_key text,geohash text, data text) PRIMARY KEY (geo_key, geohash))

我的想法是,我可以将 geohash 的字符存储为单独的列,允许我指定任意数量的字符,以便对 geohash 进行前缀匹配。我担心的是使用多个聚类列可能会产生什么影响:

CREATE TABLE georecords (g1 text,g2 text,g3 text,g4 text,g5 text,g6 text,g7 text,g8 text,geohash text, data text) PRIMARY KEY (g1,g2,g3,g4,g5,g6,g7,g8,geohash,pid))

(我并不真正关心分区键的基数 - g1 至少有 30 个值,我也有其他解决方法)

除了分区键的基数和额外的存储要求,如果我使用多簇列方法,我应该注意什么?

4

2 回答 2

3

除了分区键的基数和额外的存储要求,如果我使用多簇列方法,我应该注意什么?

这似乎是一个需要帮助的有趣问题,因此我构建了一些具有不同 PRIMARY KEY 结构和选项的 CQL 表。然后我使用http://geohash.org/提出了一些端点,并插入了它们。

aploetz@cqlsh:stackoverflow> SELECT g1, g2, g3, g4, g5, g6, g7, g8, geohash, pid, data FROm georecords3;

 g1 | g2 | g3 | g4 | g5 | g6 | g7 | g8 | geohash      | pid  | data
----+----+----+----+----+----+----+----+--------------+------+---------------
  d |  p |  8 |  9 |  v |  c |  n |  e |  dp89vcnem4n | 1001 |    Beloit, WI
  d |  p |  8 |  c |  p |  w |  g |  v |    dp8cpwgv3 | 1003 |   Harvard, IL
  d |  p |  c |  8 |  g |  e |  k |  t | dpc8gektg8w7 | 1002 | Sheboygan, WI
  9 |  x |  j |  6 |  5 |  j |  5 |  1 |    9xj65j518 | 1004 |    Denver, CO

(4 rows)

如您所知,Cassandra 旨在返回具有特定、精确键的数据。使用多个聚类列有助于该方法,因为您正在帮助 Cassandra 快速识别您希望检索的数据。

我唯一想改变的是,看看你是否可以在没有PRIMARY KEYgeohashpidPRIMARY KEY 的情况下做。我的直觉说要摆脱pid,因为它确实不是您要查询的任何东西。它提供的唯一价值是唯一性,如果您计划多次存储相同的地理哈希,您将需要它。

在您的 PRIMARY KEY 中包含pid一个非键列,这允许您使用该WITH COMPACT STORAGE指令。真正让您受益的唯一真正优势是节省磁盘空间,因为集群列名称不与值一起存储。从cassandra-cli工具中查看表格时,这一点变得很明显:

没有紧凑存储:

[default@stackoverflow] list georecords3;
Using default limit of 100
Using default cell limit of 100
-------------------
RowKey: d
=> (name=p:8:9:v:c:n:e:dp89vcnem4n:1001:, value=, timestamp=1428766191314431)
=> (name=p:8:9:v:c:n:e:dp89vcnem4n:1001:data, value=42656c6f69742c205749, timestamp=1428766191314431)
=> (name=p:8:c:p:w:g:v:dp8cpwgv3:1003:, value=, timestamp=1428766191382903)
=> (name=p:8:c:p:w:g:v:dp8cpwgv3:1003:data, value=486172766172642c20494c, timestamp=1428766191382903)
=> (name=p:c:8:g:e:k:t:dpc8gektg8w7:1002:, value=, timestamp=1428766191276179)
=> (name=p:c:8:g:e:k:t:dpc8gektg8w7:1002:data, value=536865626f7967616e2c205749, timestamp=1428766191276179)
-------------------
RowKey: 9
=> (name=x:j:6:5:j:5:1:9xj65j518:1004:, value=, timestamp=1428766191424701)
=> (name=x:j:6:5:j:5:1:9xj65j518:1004:data, value=44656e7665722c20434f, timestamp=1428766191424701)

2 Rows Returned.
Elapsed time: 217 msec(s).

紧凑型存储:

[default@stackoverflow] list georecords2;
Using default limit of 100
Using default cell limit of 100
-------------------
RowKey: d
=> (name=p:8:9:v:c:n:e:dp89vcnem4n:1001, value=Beloit, WI, timestamp=1428765102994932)
=> (name=p:8:c:p:w:g:v:dp8cpwgv3:1003, value=Harvard, IL, timestamp=1428765717512832)
=> (name=p:c:8:g:e:k:t:dpc8gektg8w7:1002, value=Sheboygan, WI, timestamp=1428765102919171)
-------------------
RowKey: 9
=> (name=x:j:6:5:j:5:1:9xj65j518:1004, value=Denver, CO, timestamp=1428766022126266)

2 Rows Returned.
Elapsed time: 39 msec(s).

但是,我建议不要使用WITH COMPACT STORAGE,原因如下:

  • 创建表后不能添加或删除列。
  • 它可以防止您在表中有多个非键列。
  • 它真的打算用于旧的(已弃用)基于节俭的列族(表)建模方法,并且真的不应该再使用/需要了。
  • 是的,它可以节省磁盘空间,但磁盘空间很便宜,所以我认为这是一个很小的好处。

我知道你说“除了分区键的基数”,但无论如何我还是要在这里提一下。您会在我的示例数据集中注意到,几乎所有行都与d分区键值一起存储。如果我要为自己创建一个这样的应用程序,在威斯康星州/伊利诺伊州州线区域跟踪 geohashes,我肯定会遇到我的大部分数据存储在同一个分区中的问题(在我的集群中创建一个热点)。因此,了解我的用例和潜在数据后,我可能会将前三个左右的列组合成一个分区键。

将所有内容存储在同一个分区键中的另一个问题是,每个分区最多可以存储大约 20 亿列。因此,在您的数据是否会超越该标记的情况下添加一些内容也是有意义的。显然,分区键的基数越高,遇到此问题的可能性就越小。

通过查看您的问题,在我看来,您已经查看了您的数据并且您理解了这一点……明确的“加号”。分区键中的 30 个唯一值应提供足够的分布。我只是想花一些时间来说明一下这可能有多大的交易。

无论如何,我还想添加一个“做得很好”,因为听起来你在正确的轨道上。

编辑

对我来说,仍然没有解决的问题是哪种方法可以更好地扩展,在哪些情况下。

可扩展性更依赖于您在 N 个节点上拥有多少 R 个副本。随着Cassandra 线性扩展;您添加的节点越多,您的应用程序可以处理的事务就越多。纯粹从数据分布场景来看,您的第一个模型将具有更高的基数分区键,因此它将比第二个分布更均匀。然而,第一个模型在查询灵活性方面提出了一个更具限制性的模型。

此外,如果您在分区内进行范围查询(我相信您说过是这样),那么第二个模型将以非常高效的方式实现这一点。分区内的所有数据都存储在同一个节点上。因此,查询g1='d' AND g2='p'...etc... 的多个结果将执行得非常好。

我可能只需要更多地使用数据并运行测试用例。

这是一个好主意。我想您会发现第二种模型是可行的方法(就查询灵活性和多行查询而言)。如果在单行查询方面两者之间存在性能差异,我怀疑它应该可以忽略不计。

于 2015-04-11T16:11:41.850 回答
1

这是我发现的最好的 Cassandra 建模指南:http ://www.ebaytechblog.com/2012/07/16/cassandra-data-modeling-best-practices-part-1/

我已经成功地使用了复合列(其中 6 个)来实现非常高的写入/读取负载。使用紧凑型存储 ( http://docs.datastax.com/en/cql/3.0/cql/cql_reference/create_table_r.html )时没有明显的性能损失。

紧凑存储意味着数据在内部存储在单行中,但限制是您只能拥有一个数据列。无论您选择哪种数据模型,这似乎都非常适合您的应用程序,并且会最大限度地利用您的 geo_key 过滤。

要考虑的另一个方面是列在 Cassandra 中排序。拥有更多的聚类列将提高排序速度和潜在的查找。

但是,在您的情况下,我首先将 geohash 作为行键并打开行缓存以进行快速查找(http://www.datastax.com/dev/blog/row-caching-in-cassandra-2 -1 )。如果那里缺乏性能,我将对不同的数据表示进行性能测试。

于 2015-04-09T15:23:53.877 回答