【问题标题】:how to avoid secondary indexes in cassandra?如何避免cassandra中的二级索引?
【发布时间】:2014-09-27 07:11:12
【问题描述】:

我反复听说二级索引(在 cassandra 中)只是为了方便而不是为了更好的性能。唯一建议在基数较低时使用二级索引的情况(例如性别column,它有两个值男性或女性)

考虑这个例子:

CREATE TABLE users ( 
userID uuid, 
firstname text, 
lastname text, 
state text, 
zip int, 
PRIMARY KEY (userID) 
);

现在我无法执行此查询,除非我在 users 上的 firstname index 上创建二级索引

select * from users where firstname='john'

我如何对这个表进行非规范化以便我可以有这个查询: 这是使用复合键的唯一有效方法吗? 还有其他选择或建议吗?

CREATE TABLE users ( 
    userID uuid, 
    firstname text, 
    lastname text, 
    state text, 
    zip int, 
    PRIMARY KEY (firstname,userID) 
    );

【问题讨论】:

    标签: cql3 cassandra-2.0 secondary-indexes


    【解决方案1】:

    为了提出一个好的数据模型,您需要首先确定要执行的所有个查询。如果您只需要通过名字(或名字和用户 ID)查找用户,那么您的第二个设计就可以了......

    如果您还需要按姓氏查找用户,则可以创建另一个具有相同字段但主键位于 (lastname, userID) 上的表。显然,您需要同时更新两个表。 Cassandra 中的数据复制很好。

    不过,如果您担心两个或多个表所需的空间,您可以创建一个按用户 ID 分区的用户表,并为您要查询的字段创建其他表:

    CREATE TABLE users ( 
        userID uuid, 
        firstname text, 
        lastname text, 
        state text, 
        zip int, 
        PRIMARY KEY (userID) 
    );
    
    CREATE TABLE users_by_firstname (
        firstname text,
        userid uuid,
        PRIMARY KEY (firstname, userid)
    );
    

    此解决方案的缺点是您将需要两个查询来按用户的名字检索用户:

    SELECT userid FROM users_by_firstname WHERE firstname = 'Joe';
    SELECT * FROM users WHERE userid IN (...);
    

    希望对你有帮助

    【讨论】:

    • 感谢您的解释。但问题是关于二级索引。
    • 我认为你的问题的本质是“我如何非规范化这个表以便我可以有这个查询”。如前所述,二级索引的性能不是很好,除非您通过分区键预先过滤数据。根据我在相对较小的集群(5 个节点)上的测试,它们可能比按分区键查询慢 10 倍。在更大的集群上,差异可能更大。根据我的经验,最好使用辅助表来模拟它们,就像我使用 users_by_firstname 表一样。
    • 正确。背后的原因是行是由分区键分区的,因此在查找过程中,Cassandra 确切地知道哪个节点保存了数据。使用二级索引,每个节点都维护自己的索引,查询需要在所有节点上执行,然后需要合并结果。所以是的,分区键的两个查询比二级索引的一个查询快。使用不同的主键复制表也是一个很好的解决方案。
    • 以您的示例为例,您可以拥有用户表的主键(名字、用户 ID)。然后,您可以在姓氏上创建二级索引。在这种情况下,查询 SELECT * from users where firstname='Joe' and lastname='Doe' 可以从姓氏索引中受益,因为数据已经按名字进行了预过滤,因此可以在单个节点上执行。另一个用例是当您不太关心性能,只想要一个简单的解决方案时。
    • 更改名字将在 Cassandra 中创建一个新行。您必须删除或停用现有行。
    【解决方案2】:

    有几种方法可以做到这一点,各有利弊。

    • 您的第二个查询将起作用,但它只是一个索引表。 http://wiki.apache.org/cassandra/SecondaryIndexes 二级索引可能会有所帮助,如果你先点击一个分区(你不能在你的第一个表中这样做),那么 cassandra 的实现将为你省去麻烦,并使事情保持“本地原子”。但是,如果不命中分区,您的第一个带有索引的表将不会很好地用于您的查询,因为它会在任何地方命中所有内容。

    • 您可以完全非规范化,但也可以进行查找表。即您的第二个表只能存在以返回用户ID。然后,您可以进行第二次查询以仅获取相关分区的信息。如果您期望的结果很少,这可能会很好。如果没有,您将在许多节点上遇到许多分区(这取决于您的集群大小和热点避免标准,可能是好是坏)。做很多 ~1ms 的查询通常比做一个 ~1000ms 的查询要好。

    • 您可以进行人工分桶,并发出 n=bucketcount 查询。这会产生额外的开销,但会减少查询次数,并且可能是一个不错的选择。

    • 您的索引可能是名字的前几个字符。或者它可能是一个一致的散列到几个桶中。前者可以给你“开始于”语义。

    这些只是几个选项。从逻辑数据模型转变为物理数据模型需要评估您希望做出哪些权衡。

    【讨论】:

    • 请提供第 2 点和第 3 点的示例表。我不太了解。
    • 2:创建表查找(名字文本主键,用户ID uuid)3:创建表foo(bucketid int,somecol int,...主键(bucketid,your_cols)。查询时您可以然后do ... WHERE bucketid in (1,2,3)。这将在 3 个分区上发出 3 个查询。
    • 您的第 2 点与我在帖子中的第一个表相同。这将不允许where firstname="john" 类型的查询。 bucketid 似乎很有趣。但我并不完全清楚它是如何工作的。如果您可以使用如何使用此 bucketid 来编辑答案,那将非常有帮助。谢谢
    【解决方案3】:

    还有具有自动更新功能的物化视图,可将数据分区到不同的列上,因此可以加快读取速度并完全避免二级索引。自己做这件事还有一些额外的好处。

    避免热分区的总体思路仍然存在。

    然后,如果您要对物化视图主键进行大量更新以避免墓碑,那么还有 SASI 索引。

    【讨论】:

      猜你喜欢
      • 2013-07-25
      • 1970-01-01
      • 2018-07-01
      • 1970-01-01
      • 2018-06-23
      • 2016-06-29
      • 2013-09-30
      • 2015-06-23
      • 2014-09-27
      相关资源
      最近更新 更多