cassandra table 是否可以仅使用复合分区键的一部分进行查询?
Can a cassandra table be queried using only a part of the composite partition key?
考虑像这样的 table 来存储用户的联系人 -
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 不允许这样做。所以我现在的问题是,您将如何对数据建模以支持两个查询 -
- 获取特定用户和联系人的数据
- 获取用户的所有联系人姓名
我可以想到两个选项-
- 创建另一个包含 user_name 和 contact_name 的 table,仅将 user_name 作为主键。但是如果一个用户有太多的联系人,那会不会是一个广泛的行问题?
- 在 user_name 上创建索引。但是考虑到 1 亿用户,每个用户只有几百个联系人,user_name 会被认为是高基数值因此不适合在索引中使用吗?
在 RDBMS 中,查询计划器可能能够为这种查询创建高效的查询计划。但卡桑德拉不能。 Cassandra 必须进行 table 扫描。 Cassandra 尽量不让您进行此类查询。所以它应该拒绝它。
您可以使用结构相同但分区键不同的两个不同 table:
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)
}
使用此结构,您有数据重复,您必须手动维护两个 table。
如果您使用的是 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
这种情况下,你只需要维护tablecontracts
,视图会自动更新
不可以。如果你看一下cassandra存储数据的机制,你就会明白为什么不能按部分复合分区键查询。
Cassandra 根据分区键跨节点分布数据。写入请求的协调器使用分区键上的 murmur3 算法生成哈希令牌,并将写入请求发送给令牌的所有者。(每个节点都有一个它拥有的令牌范围)。在读取期间,协调器再次根据分区键计算哈希令牌,并将读取请求发送到令牌的所有者节点。
由于您使用的是复合分区键,因此在写入请求期间,键的所有组件(用户,contact_name)都将用于生成哈希令牌。此令牌的所有者节点具有整行。在读取请求期间,您必须提供密钥的所有组件以计算令牌并将读取请求发送给该令牌的正确所有者。因此,Cassandra 强制您提供整个分区键。
考虑像这样的 table 来存储用户的联系人 -
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 不允许这样做。所以我现在的问题是,您将如何对数据建模以支持两个查询 -
- 获取特定用户和联系人的数据
- 获取用户的所有联系人姓名
我可以想到两个选项-
- 创建另一个包含 user_name 和 contact_name 的 table,仅将 user_name 作为主键。但是如果一个用户有太多的联系人,那会不会是一个广泛的行问题?
- 在 user_name 上创建索引。但是考虑到 1 亿用户,每个用户只有几百个联系人,user_name 会被认为是高基数值因此不适合在索引中使用吗?
在 RDBMS 中,查询计划器可能能够为这种查询创建高效的查询计划。但卡桑德拉不能。 Cassandra 必须进行 table 扫描。 Cassandra 尽量不让您进行此类查询。所以它应该拒绝它。
您可以使用结构相同但分区键不同的两个不同 table:
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)
}
使用此结构,您有数据重复,您必须手动维护两个 table。
如果您使用的是 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
这种情况下,你只需要维护tablecontracts
,视图会自动更新
不可以。如果你看一下cassandra存储数据的机制,你就会明白为什么不能按部分复合分区键查询。
Cassandra 根据分区键跨节点分布数据。写入请求的协调器使用分区键上的 murmur3 算法生成哈希令牌,并将写入请求发送给令牌的所有者。(每个节点都有一个它拥有的令牌范围)。在读取期间,协调器再次根据分区键计算哈希令牌,并将读取请求发送到令牌的所有者节点。
由于您使用的是复合分区键,因此在写入请求期间,键的所有组件(用户,contact_name)都将用于生成哈希令牌。此令牌的所有者节点具有整行。在读取请求期间,您必须提供密钥的所有组件以计算令牌并将读取请求发送给该令牌的正确所有者。因此,Cassandra 强制您提供整个分区键。