【问题标题】:How do secondary indexes work in Cassandra?二级索引在 Cassandra 中是如何工作的?
【发布时间】:2015-06-23 22:09:32
【问题描述】:

假设我有一个列族:

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 二级索引是作为本地索引实现的,而不是像普通表那样分布。每个节点只为其存储的数据存储一个索引。

考虑以下查询:

select * from update_audit where scopeid=35 and formid=78005 and record_link_id=9897;
  1. 此查询将如何在 Cassandra 中“在后台”执行?
  2. 高基数列索引 (record_link_id) 将如何影响其性能?
  3. Cassandra 是否会触及上述查询的所有节点? 为什么?
  4. 首先执行哪个条件,基表partition_key还是二级索引partition_key? Cassandra 将如何交叉这两个结果?

【问题讨论】:

  • 我的 2 美分:由于您指定了完整的分区键,因此查询所有节点是没有意义的。它显然应该只查询负责 (35, 78005) 的节点。由于 Cassandra 的设计方式,我希望它优先考虑减少所涉及节点的数量。鉴于此,唯一涉及的节点可能应该查看它有多少条记录 (35, 78005) 以及它在record_link_id=9897 的索引中有多少条记录,并使用最快的一条来提供查询(这不一定是最小的一个,取决于索引是否也按主键排序)。
  • 我的理论似乎得到了docs.datastax.com/en/cql/3.0/cql/ddl/…的支持
  • 如果是这样,那么在高基数列上创建索引将是最快和最好的数据模型(如果您在条件中也包括分区键)。

标签: cassandra cql cassandra-2.0 cql3


【解决方案1】:
select * from update_audit where scopeid=35 and formid=78005 and record_link_id=9897;

上述查询将如何在 cassandra 内部工作?

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

高基数列(record_link_id)索引对上述查询的查询性能有何影响?

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

cassandra 是否会触及上述查询的所有节点?为什么?

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

在高基数列上创建索引将是最快和最好的数据模型

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

如果您的表明显大于内存,那么即使只返回几千个结果,查询也会非常慢。返回潜在的数百万用户将是灾难性的,即使这看起来是一个有效的查询。

...

实际上,这意味着索引对于返回数十甚至数百个结果最有用。下次考虑使用二级索引时请记住这一点。

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

编辑

即使我们提供主键,当有数百万用户时,低基数索引上的索引如何扩展

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

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

编辑 20180913

对于第一个问题“上述查询将如何在 cassandra 内部工作?”的回答,您知道分页查询时的行为是什么吗?

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

基本上,分页会导致查询自行分解并返回集群以获取下一次迭代结果。超时的可能性较小,但性能会呈下降趋势,与总结果集的大小和集群中的节点数成正比。

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

【讨论】:

  • 像往常一样透彻而深刻。
  • 感谢您的洞察力!即使我们在查询中提供分区键(如select * from users partitionkey=x and gender='M'),当有数百万用户时,低基数索引上的索引如何扩展。从存储角度看,关于性别的隐藏列族,会不会溢出?是否会导致问题,因为它需要扫描隐藏的列族以过滤掉结果? stackoverflow.com/questions/29659564/…
  • @pinkpanther 已编辑。
  • @BryceAtNetwork23 谢谢,但我是从隐藏的 CF 角度说的。如果我们在查询中提供整个主键,在这种情况下只有一行,但隐藏的性别 CF 很宽,因为会有很多相同性别的人。例如,如果男性人数为 50,000。它是否将二级索引返回的所有用户ID与主键返回的一行连接起来并过滤结果?当隐藏的 CF 行很宽时,我特别询问此过滤的性能。提前致谢。
  • @grisaitis 这取决于您尝试使用它的方式。根据定义,索引与 SSTables 配对,这意味着当不与分区键配对时,它仍然会表现出较差的性能。有关详细信息,我建议阅读 Doan Duyhai 的这篇文章,该文章检查了 SASI 索引的内部工作原理:doanduyhai.com/blog/?p=2058
【解决方案2】:

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

select * from update_audit where record_link_id=9897;

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

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

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

【讨论】:

    猜你喜欢
    • 2014-09-27
    • 2011-09-19
    • 2013-07-25
    • 1970-01-01
    • 1970-01-01
    • 2011-06-08
    • 2014-09-27
    • 2018-07-01
    • 1970-01-01
    相关资源
    最近更新 更多