Cassandra查询制作 - 无法执行此查询,因为它可能涉及数据过滤,因此可能具有不可预测的性能

时间:2015-03-28 06:55:39

标签: python cassandra cassandra-cli cqlengine

我有以下Cassandra模型: -

class Automobile(Model):
    manufacturer = columns.Text(primary_key=True)
    year = columns.Integer(index=True)
    model = columns.Text(index=True)
    price = columns.Decimal(index=True)

我需要以下查询: -

q = Automobile.objects.filter(manufacturer='Tesla')
q = Automobile.objects.filter(year='something')
q = Automobile.objects.filter(model='something')
q = Automobile.objects.filter(price='something')

这些都工作正常,直到我想要多列过滤,即当我尝试

q = Automobile.objects.filter(manufacturer='Tesla',year='2013')

它会抛出错误Cannot execute this query as it might involve data filtering and thus may have unpredictable performance.

我用allowed_filtering重写了查询,但这不是最佳解决方案。

然后在阅读更多内容后,我编辑了我的模型如下: -

class Automobile(Model):
    manufacturer = columns.Text(primary_key=True)
    year = columns.Integer(primary_key=True)
    model = columns.Text(primary_key=True)
    price = columns.Decimal()

有了这个,我也能够过滤多个库尔兹,没有任何警告。

当我DESCRIBE TABLE automobile时,它会显示这会创建复合键PRIMARY KEY ((manufacturer), year, model)

所以,我的问题是如果我将每个属性都声明为主键怎么办?这有什么问题吗,因为我也可以过滤多个列。

这只是一个小模型。如果我有一个如下模型怎么办? -

class UserProfile(Model):
    id = columns.UUID(primary_key=True, default=uuid.uuid4)
    model = columns.Text()
    msisdn = columns.Text(index=True)
    gender = columns.Text(index=True)
    imei1 = columns.Set(columns.Text)
    circle = columns.Text(index=True)
    epoch = columns.DateTime(index=True)
    cellid = columns.Text(index=True)
    lacid = columns.Text(index=True)
    mcc = columns.Text(index=True)
    mnc = columns.Text(index=True)
    installed_apps = columns.Set(columns.Text)
    otp = columns.Text(index=True)
    regtype = columns.Text(index=True)
    ctype = columns.Text(index=True)
    operator = columns.Text(index=True)
    dob = columns.DateTime(index=True)
    jsonver = columns.Text(index=True)

如果我将每个属性声明为PK,那么这有什么问题吗?

2 个答案:

答案 0 :(得分:4)

要理解这一点,您需要了解cassandra如何存储数据。主键中的第一个键称为分区键。它定义了行所属的分区。分区中的所有行都存储在一起,并一起复制。在分区内,根据聚类键存储行。这些是PK中的列,而不是分区键。因此,如果您的PK是(a,b,c,d),则a定义分区。并且在特定分区(例如,a = a1)中,行按b排序存储。对于每个b,行按c ...排序存储,依此类推。查询时,您会点击一个(或几个分区),然后需要指定每个连续的群集密钥,直到您要查找的密钥为止。除了查询中指定的最后一个聚类列(可能是范围查询)之外,它们必须具有精确的相等性。

在上一个示例中,您可以这样做

where a = a1 and b > b1 where a = a1 and b=b1 and c>c1 where a = a1 and b=b1 and c=c1 and d > d1

但不能这样做:

where a=a1 and c=c1

为此,您需要"允许过滤" (实际上,你应该考虑改变模型,或者在那时进行非规范化)。

现在,关于将每个列作为PK的一部分的问题。你可以这样做,但请记住,Cassandra的所有写作都是upserts。行由其主键标识。如果您将每个列都作为PK的一部分,那么您将无法编辑行。您不允许更新主键中任何列的值。

答案 1 :(得分:2)

解决此问题的正确方法是采用基于查询的建模方法。您应该使用四个(可能是三个)表和 ZERO 二级索引来解决此问题,而不是一个包含三个二级索引的表。

Automobile的原始表可能没问题。虽然我很想看到你的主键定义。但是所以解决你的Automobile.objects.filter(year='something')查询我会创建一个这样的附加查询表(注意:在CQL中定义):

CREATE TABLE automobileByYear (
  manufacturer text,
  year bigint,
  model text,
  price decimal,
  PRIMARY KEY ((year),manufacturer,model));

假设您还在此模型(AutomobileByYear)的Python端创建了相应的类,则可以提供如下查询:

AutomobileByYear.objects.filter(year='2013')

此外,将manufacturer作为第一个群集密钥也可以使此查询起作用:

AutomobileByYear.objects.filter(manufacturer='Tesla',year='2013')

同样,为了按模型求解你的查询,我会创建一个额外的查询表(automobileByModel),表的PRIMARY KEY定义重新排序如下:

PRIMARY KEY ((model),manufacturer,year));

群集密钥(manufactureryear)的顺序会因您的查询要求而异,但重点是model在这种情况下应该是您的分区密钥。

修改

  

...但是应该按照我的查询设计我的表,从而有很多数据冗余。比方说,我有这个相同的汽车模型,有N个字段,其中我们说N = 10。如果我想按每N个字段过滤。我应该为每个不同的过滤器类型查询创建不同的模型。

在这个时代,磁盘比过去便宜。话虽如此,我知道在问题上抛出更多磁盘并不容易。我看到的更大问题是调整应用程序的DAO层以保持10个表同步。

在这种情况下,我会建议使用像Elastic或Solr这样的搜索工具进行集成。事实上,Cassandra的企业版与开箱即用的Solr集成在一起。如果你真的需要在10+列上运行查询,那么一个强大的搜索工具可以很好地补充你的Cassandra集群。