我有以下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,那么这有什么问题吗?
答案 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));
群集密钥(manufacturer
和year
)的顺序会因您的查询要求而异,但重点是model
在这种情况下应该是您的分区密钥。
修改强>
...但是应该按照我的查询设计我的表,从而有很多数据冗余。比方说,我有这个相同的汽车模型,有N个字段,其中我们说N = 10。如果我想按每N个字段过滤。我应该为每个不同的过滤器类型查询创建不同的模型。
在这个时代,磁盘比过去便宜。话虽如此,我知道在问题上抛出更多磁盘并不容易。我看到的更大问题是调整应用程序的DAO层以保持10个表同步。
在这种情况下,我会建议使用像Elastic或Solr这样的搜索工具进行集成。事实上,Cassandra的企业版与开箱即用的Solr集成在一起。如果你真的需要在10+列上运行查询,那么一个强大的搜索工具可以很好地补充你的Cassandra集群。