在 cassandra 中拥有多个聚类列有什么主要缺点吗?
Are there any major disadvantages to having multiple clustering columns in cassandra?
我正在设计一个 cassandra table,我需要能够通过它们的 geohash 检索行。我有一些有用的东西,但我想避免范围查询,而不是我目前能够做到的。
当前的 table 架构是这样的,其中 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 个值,我还有其他解决方法)
除了分区键的基数和额外的存储要求,如果我使用多簇列方法,我应该注意什么?
这是我找到的最好的 Cassandra 建模指南:http://www.ebaytechblog.com/2012/07/16/cassandra-data-modeling-best-practices-part-1/
我已经成功地使用复合柱(其中 6 个)来处理非常高的 write/read 负载。使用压缩存储 (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)。如果那里缺乏性能,我会 运行 对不同数据表示的性能测试。
Other that cardinality of the partition key, and extra storage requirements, what should I be aware of if I used the many cluster column approach?
这似乎是一个需要帮助解决的有趣问题,因此我构建了一些不同主键结构和选项的 CQL table。然后我使用 http://geohash.org/ 提出了一些端点,并插入了它们。
aploetz@cqlsh:Whosebug> 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 旨在 return 具有特定、精确密钥的数据。在该方法中使用多个聚类列 有助于 ,因为您正在帮助 Cassandra 快速识别您希望检索的数据。
我唯一会考虑更改的是看看您是否可以在 PRIMARY KEY 中不使用 geohash
或 pid
。我的直觉告诉我要摆脱 pid
,因为它确实不是您要查询的任何内容。它提供的唯一价值是唯一性,如果您计划多次存储相同的 geohashes,您将需要它。
在您的 PRIMARY KEY 中包含 pid
会留下一个非键列,这样您就可以使用 WITH COMPACT STORAGE
指令。真正让您受益的唯一真正优势是保存磁盘 space,因为聚类列名称未与值一起存储。从 cassandra-cli
工具中查看 table 时,这一点变得很明显:
无压缩存储:
[default@Whosebug] 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@Whosebug] 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
,原因如下:
- 创建 table 后无法添加或删除列。
- 它可以防止您在 table 中有多个非键列。
- 它确实打算用于旧的(已弃用的)基于节俭的列族 (table) 建模方法,并且真的不应该再 used/needed 了。
- 是的,它可以为您节省磁盘 space,但是磁盘 space 很便宜,所以我认为这是一个很小的好处。
我知道你说过"other than cardinality of the partition key",但我还是要在这里提一下。您会注意到在我的示例数据集中,几乎所有行都存储有 d
分区键值。如果我要为自己创建一个这样的应用程序,在 Wisconsin/Illinois stateline 区域跟踪 geohashes,我肯定会遇到我的大部分数据都存储在同一个分区中的问题(在我的集群中创建一个热点)。因此,了解我的用例和潜在数据后,我可能会将前三列左右组合成一个分区键。
将所有内容存储在同一个分区键中的另一个问题是每个分区最多可以存储大约 20 亿列。因此,考虑一下您的数据是否会超越该标记也是有意义的。显然,分区键的基数越高,您 运行 陷入此问题的可能性就越小。
通过查看您的问题,在我看来您已经查看了您的数据并且您理解了这一点...确定 "plus." 和 30 个唯一值分区键应提供足够的分布。我只是想花点时间说明这可能有多大意义。
无论如何,我还想添加一个"nicely done,",因为听起来你的方向是对的。
编辑
The still unresolved question for me is which approach will scale better, in which situations.
可扩展性更多地取决于您在 N 个节点上拥有多少 R 个副本。作为Cassandra scales linearly;您添加的节点越多,您的应用程序可以处理的事务就越多。纯粹从数据分布场景来看,你的第一个模型会有更高的基数分区键,所以它会比第二个分布得更均匀。但是,第一个模型在查询灵活性方面提出了更多限制性模型。
此外,如果您在分区内进行范围查询(我相信您说过),那么第二个模型将以 非常 的高性能方式实现。分区内的所有数据都存储在同一个节点上。因此查询 g1='d' AND g2='p'
...等...的多个结果会非常好。
I may just have to play with the data more and run test cases.
这是个好主意。我认为您会发现第二种模型是可行的(就查询灵活性和查询多行而言)。如果在单行查询时两者之间存在性能差异,我的怀疑是它应该可以忽略不计。
我正在设计一个 cassandra table,我需要能够通过它们的 geohash 检索行。我有一些有用的东西,但我想避免范围查询,而不是我目前能够做到的。
当前的 table 架构是这样的,其中 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 个值,我还有其他解决方法)
除了分区键的基数和额外的存储要求,如果我使用多簇列方法,我应该注意什么?
这是我找到的最好的 Cassandra 建模指南:http://www.ebaytechblog.com/2012/07/16/cassandra-data-modeling-best-practices-part-1/
我已经成功地使用复合柱(其中 6 个)来处理非常高的 write/read 负载。使用压缩存储 (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)。如果那里缺乏性能,我会 运行 对不同数据表示的性能测试。
Other that cardinality of the partition key, and extra storage requirements, what should I be aware of if I used the many cluster column approach?
这似乎是一个需要帮助解决的有趣问题,因此我构建了一些不同主键结构和选项的 CQL table。然后我使用 http://geohash.org/ 提出了一些端点,并插入了它们。
aploetz@cqlsh:Whosebug> 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 旨在 return 具有特定、精确密钥的数据。在该方法中使用多个聚类列 有助于 ,因为您正在帮助 Cassandra 快速识别您希望检索的数据。
我唯一会考虑更改的是看看您是否可以在 PRIMARY KEY 中不使用 geohash
或 pid
。我的直觉告诉我要摆脱 pid
,因为它确实不是您要查询的任何内容。它提供的唯一价值是唯一性,如果您计划多次存储相同的 geohashes,您将需要它。
在您的 PRIMARY KEY 中包含 pid
会留下一个非键列,这样您就可以使用 WITH COMPACT STORAGE
指令。真正让您受益的唯一真正优势是保存磁盘 space,因为聚类列名称未与值一起存储。从 cassandra-cli
工具中查看 table 时,这一点变得很明显:
无压缩存储:
[default@Whosebug] 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@Whosebug] 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
,原因如下:
- 创建 table 后无法添加或删除列。
- 它可以防止您在 table 中有多个非键列。
- 它确实打算用于旧的(已弃用的)基于节俭的列族 (table) 建模方法,并且真的不应该再 used/needed 了。
- 是的,它可以为您节省磁盘 space,但是磁盘 space 很便宜,所以我认为这是一个很小的好处。
我知道你说过"other than cardinality of the partition key",但我还是要在这里提一下。您会注意到在我的示例数据集中,几乎所有行都存储有 d
分区键值。如果我要为自己创建一个这样的应用程序,在 Wisconsin/Illinois stateline 区域跟踪 geohashes,我肯定会遇到我的大部分数据都存储在同一个分区中的问题(在我的集群中创建一个热点)。因此,了解我的用例和潜在数据后,我可能会将前三列左右组合成一个分区键。
将所有内容存储在同一个分区键中的另一个问题是每个分区最多可以存储大约 20 亿列。因此,考虑一下您的数据是否会超越该标记也是有意义的。显然,分区键的基数越高,您 运行 陷入此问题的可能性就越小。
通过查看您的问题,在我看来您已经查看了您的数据并且您理解了这一点...确定 "plus." 和 30 个唯一值分区键应提供足够的分布。我只是想花点时间说明这可能有多大意义。
无论如何,我还想添加一个"nicely done,",因为听起来你的方向是对的。
编辑
The still unresolved question for me is which approach will scale better, in which situations.
可扩展性更多地取决于您在 N 个节点上拥有多少 R 个副本。作为Cassandra scales linearly;您添加的节点越多,您的应用程序可以处理的事务就越多。纯粹从数据分布场景来看,你的第一个模型会有更高的基数分区键,所以它会比第二个分布得更均匀。但是,第一个模型在查询灵活性方面提出了更多限制性模型。
此外,如果您在分区内进行范围查询(我相信您说过),那么第二个模型将以 非常 的高性能方式实现。分区内的所有数据都存储在同一个节点上。因此查询 g1='d' AND g2='p'
...等...的多个结果会非常好。
I may just have to play with the data more and run test cases.
这是个好主意。我认为您会发现第二种模型是可行的(就查询灵活性和查询多行而言)。如果在单行查询时两者之间存在性能差异,我的怀疑是它应该可以忽略不计。