【问题标题】:Can a cassandra table be queried using only a part of the composite partition key?可以只使用复合分区键的一部分来查询 cassandra 表吗?
【发布时间】:2016-11-23 09:37:03
【问题描述】:

考虑像这样的表来存储用户的联系人 -

CREATE TABLE contacts {
    user_name text,
    contact_name text,
    contact_id int, 
    contact_data blob,
    PRIMARYKEY ((user, contact_name), contact_id)
    //          ^-- Note the composite partition key
}

复合分区键为每个联系人生成一行。

假设有 1 亿用户,每个用户都有几百个联系人。

我可以使用

查找特定用户的特定联系人的数据
SELECT contact_data FROM contacts WHERE user_name='foo' AND contact_name='bar'

但是,是否也可以使用类似的方式查找用户的所有联系人姓名,

SELECT contact_name FROM contacts WHERE user_name='foo'

? WHERE 子句可以只包含构成主键的所有列中的一部分吗?

编辑——我试过了,但 cassandra 不允许这样做。所以我现在的问题是,您将如何对数据进行建模以支持两个查询 -

  1. 获取特定用户和联系人的数据
  2. 获取用户的所有联系人姓名

我能想到两个选择 -

  1. 创建另一个表,其中包含 user_name 和 contact_name,仅 user_name 作为主键。但是,如果用户的联系人太多,这会是一个宽行问题吗?
  2. 在用户名上创建索引。但是考虑到 1 亿用户,每个用户只有几百个联系人,user_name 是否会被视为高基数,因此不适合在索引中使用?

【问题讨论】:

    标签: cassandra data-modeling cql


    【解决方案1】:

    在 RDBMS 中,查询计划器可能能够为此类查询创建有效的查询计划。但卡桑德拉不能。 Cassandra 必须进行表扫描。 Cassandra 尽量不让您进行此类查询。所以它应该拒绝它。

    【讨论】:

    • 我只是想验证一下,结果如你所指。我已经编辑了我的问题 - 你能评论一下我在编辑中提到的两个选项吗
    • @PuneetArora 不,我不会。如果您有新问题,请提出新问题。
    • 好的,不用担心,我继续单独询问:stackoverflow.com/questions/38488557/…
    【解决方案2】:

    不,你不能。看看cassandra存储数据的机制,你就会明白为什么不能通过部分复合分区键查询。

    Cassandra 根据分区键跨节点分布数据。写请求的协调者使用 murmur3 算法对分区键生成哈希令牌,并将写请求发送给令牌的所有者。(每个节点都有一个它拥有的令牌范围)。在读取过程中,协调器再次根据分区键计算哈希令牌,并将读取请求发送到令牌的所有者节点。

    由于您使用的是复合分区键,因此在写入请求期间,键的所有组件(用户、联系人名称)都将用于生成哈希令牌。此令牌的所有者节点具有整行。在读取请求期间,您必须提供密钥的所有组件来计算令牌并将读取请求发送给该令牌的正确所有者。因此,Cassandra 强制您提供整个分区键。

    【讨论】:

      【解决方案3】:

      你可以使用结构相同但分区键不同的两个不同的表:

      CREATE TABLE contacts { user_name text, contact_name text, contact_id int, contact_data blob, PRIMARY KEY ((user_name, contact_name), contact_id) } CREATE TABLE contacts_by_users { user_name text, contact_name text, contact_id int, contact_data blob, PRIMARY KEY ((user_name), contact_id) }

      使用这种结构,您有数据重复,您必须手动维护这两个表。

      如果你使用的是cassandra > 3.0,你也可以使用物化视图:

      CREATE TABLE contacts { user_name text, contact_name text, contact_id int, contact_data blob, PRIMARY KEY ((user_name, contact_name), contact_id) } CREATE MATERIALIZED VIEW contracts_by_users AS SELECT * FROM contracts WHERE user_name IS NOT NULL AND contract_name IS NOT NULL AND contract_id IS NOT NULL PRIMARY KEY ((user_name), contract_name, contract_id) WITH CLUSTERING ORDER BY contract_name ASC

      这种情况下,你只需要维护表contracts,视图会自动更新

      【讨论】:

      • contacts_by_user 表中只有 user_name 和 contact_name 不是更好吗?联系人表的想法是不要在单个分区上存储太多。考虑contact_data是一个巨大的夹头
      • 是的,您可以,但您必须对合同运行另一个查询才能获取您的合同数据。
      • 好的。我没有看到让contacts_by_user 以及你所说的联系人的意义。在这种情况下,联系人没有任何用途,因为我最终会在第一步中获取联系人的数据
      猜你喜欢
      • 2019-08-31
      • 2016-03-22
      • 2012-12-05
      • 2015-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多