二级索引在 Cassandra 中如何工作?

How do secondary indexes work in Cassandra?

假设我有一个列族:

CREATE TABLE update_audit (
  scopeid bigint,
  formid bigint,
  time timestamp,
  record_link_id bigint,
  ipaddress text,
  user_zuid bigint,
  value text,
  PRIMARY KEY ((scopeid, formid), time)
  ) WITH CLUSTERING ORDER BY (time DESC)

有两个二级索引,其中 record_link_id 是一个高基数列:

CREATE INDEX update_audit_id_idx ON update_audit (record_link_id);

CREATE INDEX update_audit_user_zuid_idx ON update_audit (user_zuid);

据我所知,Cassandra 会像这样创建两个隐藏的列族:

CREATE TABLE update_audit_id_idx(
    record_link_id bigint,
    scopeid bigint,
    formid bigint,
    time timestamp
    PRIMARY KEY ((record_link_id), scopeid, formid, time)
);

CREATE TABLE update_audit_user_zuid_idx(
    user_zuid bigint,
    scopeid bigint,
    formid bigint,
    time timestamp
    PRIMARY KEY ((user_zuid), scopeid, formid, time)
);

Cassandra 二级索引作为本地索引实现,而不是像正常的 tables 那样分布。每个节点只为其存储的数据存储一个索引。

考虑以下查询:

select * from update_audit where scopeid=35 and formid=78005 and record_link_id=9897;
  1. 这个查询将如何在 Cassandra 中执行 'under the hood'?
  2. 高基数列索引 (record_link_id) 将如何影响其性能?
  3. 对于上述查询,Cassandra 会触及所有节点吗? 为什么?
  4. 先执行哪个条件,base table partition_key or secondary index partition_key? Cassandra 如何将这两个结果相交?
select * from update_audit where scopeid=35 and formid=78005 and record_link_id=9897;

How the above query will work internally in cassandra?

本质上,分区 scopeid=35formid=78005 的所有数据都将被 returned,然后由 record_link_id 索引过滤。它将查找 9897record_link_id 条目,并尝试匹配与行 return 匹配的条目,其中 scopeid=35formid=78005。分区键和索引键的行的交集将被 returned.

How high-cardinality column (record_link_id)index will affect the query performance for the above query?

高基数索引实质上为主 table 中的(几乎)每个条目创建一行。性能受到影响,因为 Cassandra 旨在对查询结果执行顺序读取。索引查询本质上是强制 Cassandra 执行 random 读取。随着索引值的基数增加,查找查询值所需的时间也会增加。

Does cassandra will touch all nodes for the above query? WHY?

没有。它应该只接触负责 scopeid=35formid=78005 分区的节点。索引同样存储在本地,仅包含对本地节点有效的条目。

creating index over high-cardinality columns will be the fastest and best data model

这里的问题是该方法无法扩展,如果 update_audit 是一个大型数据集,它会很慢。 MVP Richard Low 有一篇关于二级索引的很棒的文章 (The Sweet Spot For Cassandra Secondary Indexing),尤其是在这一点上:

If your table was significantly larger than memory, a query would be very slow even to return just a few thousand results. Returning potentially millions of users would be disastrous even though it would appear to be an efficient query.

...

In practice, this means indexing is most useful for returning tens, maybe hundreds of results. Bear this in mind when you next consider using a secondary index.

现在,您首先通过特定分区进行限制的方法会有所帮助(因为您的分区当然应该适合内存)。但我觉得这里性能更好的选择是使 record_link_id 成为聚类键,而不是依赖二级索引。

编辑

How does having index on low cardinality index when there are millions of users scale even when we provide the primary key

这将取决于您的行的宽度。极低基数索引的棘手之处在于,returned 行的百分比通常更大。例如,考虑一个宽行 users table。您通过查询中的分区键进行限制,但仍有 10,000 行 returned。如果您的索引位于 gender 之类的位置,您的查询将不得不过滤掉大约一半的行,这将不会很好地执行。

二级索引往往最适合(因为缺乏更好的描述)“中间道路”基数。使用上面的宽行 users table 示例,countrystate 上的索引应该比 gender 上的索引执行得更好(假设这些用户中的大多数并不都住在同一个国家或州)。

编辑 20180913

For your answer to 1st question "How the above query will work internally in cassandra?", do you know what's the behavior when query with pagination?

考虑下图,摘自 Java Driver documentation (v3.6):

基本上,分页会导致查询自行分解,并 return 到集群以进行下一次结果迭代。它不太可能超时,但性能会下降,与总结果集的大小和集群中的节点数成正比。

TL;DR;分布在更多节点上的请求结果越多,所需的时间就越长。

在 Cassandra 中也可以只使用二级索引进行查询 2.x

select * from update_audit where record_link_id=9897;

但这对取数据有很大的影响,因为它读取了分布式环境中的所有分区。此查询获取的数据也不一致,无法中继。

建议:
使用二级索引被认为是 NoSQL 数据模型视图中的 DIRT 查询。

为了避免二级索引,我们可以创建一个新的 table 并将数据复制到它。由于这是应用程序的查询,因此表是从查询中派生的。