Cassandra - 如何在2个关键列中按范围搜索?

时间:2015-12-14 20:45:20

标签: select cassandra cql

如何在多个键列上按范围搜索? 我看到的方式我们不能有一个可能会转到多个分区的查询。 任何可能的解决方案来实现相同的查询结果???

CREATE TABLE RangeSearch (segment TEXT, date TIMESTAMP, dec decimal, value text, PRIMARY KEY (segment,date,dec));


SELECT * FROM decimalrangeck 
WHERE segment='SEG1'
AND date>='2015-01-01' AND date<='2015-12-12';
AND dec>=100 AND dec<=200;

2 个答案:

答案 0 :(得分:1)

简短回答:无法对Cassandra中的两个或更多聚类列进行有效范围查询。

答案很长:要了解此限制的核心原因,您应该考虑Cassandra如何将列存储在单个分区中。

单个SSTable文件中分区内的所有行都根据其聚类键进行排序,例如您的架构:

2015-01-01 100
2015-01-01 200
2015-01-01 300
2015-01-02 100
2015-01-02 200
2015-01-02 300
2015-01-03 100

有一种方法可以读取单个数据片段(通过使用范围查询,如SELECT * FROM decimalrangeck WHERE segment='SEG1' AND date>='2015-01-01' AND date<'2015-01-03';)。对于此查询,Cassandra将为行发出单个磁盘搜索,因为它肯定知道所需数据的开始和结束位置(因为它在磁盘上排序)。

您甚至可以使用SELECT * FROM decimalrangeck WHERE segment='SEG1' AND date>='2015-01-01' AND date<'2015-01-03' AND dec>=100 AND dec<=200这样的双列切片查询。我相信你会期待这些结果:

2015-01-01 100
2015-01-01 200
2015-01-02 100
2015-01-02 200

但是你会得到一个稍微令人惊讶的输出:

2015-01-01 100
2015-01-01 200
2015-01-01 300
2015-01-02 100
2015-01-02 200

差异是一行2015-01-01 300。它出现在输出中有一个原因:您的查询分为两部分:切片的开始(2015-01-01,100)和切片的结束(2015-03-01,100)。之后,Cassandra在单个磁盘搜索中读取这些数据点之间的所有数据。

2个群集列的原始范围查询将需要太多的磁盘读取才能完成。此类查询通常被认为对性能不友好。

答案 1 :(得分:0)

是的,您可以spark使用scalaspark-cassandra-connector来执行此操作!

然后你可以使用类似的东西:

val conf = new SparkConf().setAppName(appName)
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
val cc = new CassandraSQLContext(sc)

def getDays(startDate: String, endDate: String) : scala.Array[String] = {
    var dates = scala.Array[String]()

    var sDate = sdfDay.parseDateTime(startDate)
    var eDate = sdfDay.parseDateTime(endDate)

    var date = ""

    while(sDate.isBefore(eDate.plusDays(1)) == true){
        var sDay = sDate.getDayOfMonth()
        var sMonth = sDate.getMonthOfYear()
        var sYear = sDate.getYear()

        date = "" + sYear + "-" + sMonth + "-" + sDay
        println(date)
        dates :+= date

        sDate = sDate.plusDays(1)
    }

    dates //return
}

def preaperQuery(wereElement: String, andDateElement: String, andDecElement: String, tableName: String, days: scala.Array[String], decs: scala.Array[String]) : String = {
    var q = "select * from " + tableName + "where " + wereElement + " and "
    for (day <- days) { 
        q = q + andDateElement + " = '" + day + "' or " 
    }

    for (dec <- decs) { 
        q = q + andDecElement + " = '" + dec + "' or " 
    }
    //remove last " or " 4 chars
    q = q.dropRight(4) 
    q //return
}

val days = Utils.getDays(startDate, endDate)
val decs = Array("100", "200", "300", "400") //or any function

var q = Utils.preaperQuery(wereElement="segment='SEG1'", andDateElement="date", andDecElement="ec", tableName="RangeSearch", days=days, decs=decs)

val selected_data = cc.sql(q).map(row => (row(0).toString, row(1).toString,row(2).toString,row(3).toString))

您可以收集所选的数据rdd

val data = selected_data.collect

我有similar problem ...