GaussDB(for Cassandra)新特性发布:支持Lucene二级索引,让复杂查询更智能

举报
Cassandra官方 发表于 2022/07/08 12:07:09 2022/07/08
【摘要】 当前,互联网、大数据飞速发展,数据量呈爆发式增长,在高并发、高可用、高扩展的业务需求推动下,NoSQL数据库成为了越来越多业务场景的刚需。但在查询方面,传统的NoSQL却有一定的局限性,严格来说,像开源MongoDB、Cassandra、Hbase等都不具备海量数据的多维查询、文本检索、统计分析等能力。多数企业仍然在寻求一套更完美的NoSQL解决方案。

Lucene版本正式上线公测,请联系客服开通体验

今天,华为云GaussDB(for Cassandra)Lucene引擎全新解决方案来啦

华为云原生多模数据库GaussDB NoSQL拥有强大的生态体系,支持键值、宽表、文档、时序四种引擎接口。其中,宽表引擎接口GaussDB(for Cassandra)现已发布Lucene二级索引功能,既具备NoSQL的优势,又能支持多种复杂查询场景,全面提升用户在海量数据场景下的查询体验,凭实力宠粉!相信大家一定有很多疑问,GaussDB(for Cassandra)是什么?二级索引如何使用?Lucene二级索引又有哪些区别?别着急,接下来让我们一一解读。

 

什么是GaussDB(for Cassandra)

GaussDB(for Cassandra)是一款华为自研、采用计算存储分离架构的分布式云数据库,在高性能、高可用、高可靠、高安全、可弹性扩缩容的基础上,提供了一键部署、备份恢复、监控告警等服务能力;并高度兼容开源Cassandra接口,提供高读写性能。当前已经广泛应用于IoT、气象、互联网、游戏等诸多领域。

什么是二级索引?

我们先来了解下索引的概念。索引是为了加快数据检索速度而创建的一种存储结构,是一种以空间换时间的设计思想。作用可以理解为书的目录,通过目录可快速定位到所需要的内容。

Cassandra中,Primary Key就是索引(也被称为一级索引),在查询的时候,根据Primary Key可以直接检索到对应的记录。而二级索引又称辅助索引,是为了帮助定位到一级索引,然后再根据一级索引找到对应记录。我们平时使用CREATE INDEX语句建立的就是二级索引。

当前Cassandra二级索引的痛点有哪些

原生Cassandra中二级索引的实现其实是创建了一张隐式的表,该表的Primary Key是创建索引的列,值为对应的Primary Key,实现相对简单,因此不可避免地带来了一些约束条件:

1.第一主键只能用“=”查询;

2.第二主键可以使用“=><>=<=”;

3.索引列只支持“=”查询;

4.删除、更新太过频繁的列不适合建立索引;

5.High-cardinality列不适合做索引;

基于以上约束,Cassandra二级索引能提供的查询功能非常有限。

Why Lucene

Lucene是当下最火的开源全文检索引擎工具,具有以下特点:

1.稳定、索引性能高;

2.是高效、准确、高性能的搜索算法;

3.具备丰富的查询类型:支持短语查询、通配符查询、近似查询、范围查询等;

4.有强大的开源社区支持,可维护性好;

因此,用集成Lucene引擎来补充Cassandra查询能力的弱点是最佳选择,毕竟谁又会拒绝一款性能稳定、持续成长、又更新迭代的搜索引擎呢?

Lucene引擎强大的倒排索引和列式存储能力,赋予了GaussDB(for Cassandra)高效的多维查询、文本检索、统计分析等能力,在使用体验上和原生二级索引相似,但同时拥有了更为丰富的语法支持。

Lucene二级索引架构

lucene索引.png

关键技术点:

内嵌Lucene搜索引擎,与存储引擎搭配,实现宽表存储引擎与搜索引擎的深度融合;

SQL层统一融合,在兼容原生Cassandra语法基础上,提供多维查询、文本检索、模糊查询、统计分析等能力,全面提升用户在海量数据场景下的查询体验

Lucene二级索引使用方式举例

宽表.png

表结构示例:

CREATE TABLE example (pk1 text, pk2 bigint, ck1 int,ck2 text,col1 int, col2 int, col3 text, col4 text, PRIMARY KEY ((pk1,pk2),ck1, ck2));

四个属性列创建Lucene索引:

CREATE CUSTOM INDEX index_lucene ON test.example(col1,col2,col3,col4) USING 'org.apache.cassandra.index.lucene.LuceneSecondaryIndex' 
WITH OPTIONS = {
'analyzer_class': 'org.apache.lucene.analysis.standard.StandardAnalyzer',
'analyzed_columns': 'col4'
};
多维查询:任意索引列组合的嵌套查询,支持精确查询和范围查询
SELECT * from example WHERE pk1>='a' and pk2>=1000 and ck2 in ('a','b','c') and col1 <= 4 and col2 >= 2;
count计数:获取数据表的总行数,或根据索引列具体查询条件返回命中的数据行数

SELECT count(*) FROM example WHERE col1 > 3 AND EXPR(index_lucene, 'count');  
索引列排序:支持指定多个索引列排序规则,结合多维查询,返回指定排序的结果集(通过JSON扩展语义支持,见下一节JSON示例)

模糊查询:支持前缀查询和通配符查询
SELECT * FROM example WHERE col3 LIKE 'test%'; 
SELECT * FROM example WHERE col3 LIKE 'start*end';
聚合分析:按照索引列组合条件进行简单的聚合分析(sum/max/min/avg)
SELECT sum(col1) from example WHERE pk1>='a' and pk2>=1000 and col1 <= 4 and col2 >= 2;
全文检索:支持指定中/英文分词器,进行分词检索,返回相关性高的结果
SELECT * FROM example WHERE col4 LIKE '%+test -index%';  

扩展JSON语义

filter

在查询语句中json查询的关键字

term

查询时判断某个document是否包含某个具体的值,不会对被查询的值进行分词查询

match

将被询值进行分词,进行全文检索

range

查询指定某个字段在某个特定的范围(范围查询子关键字:"eq"/"gte"/"gt"/"lte"/"lt")

bool

必须和 "must""should""must not" 一起组合出复杂的查询

must

bool类型的子查询,类型为list,封装"term""match""range" 查询

should

bool类型的子查询,类型为list,封装"term""match""range" 查询

must not

bool类型的子查询,类型为list,封装"term""match""range" 查询

offset

单分区查询,支持offset偏移量查询功能,直接从符合条件的数据中的第offset个开始查询

sort

单分区查询,支持索引列排序功能;排序功能不能与原生page查询共用,分页需要与offset配合使用

几个原则:

  1. 普通cql可以满足的查询条件,尽量避免依赖json查询
  2. 单分区查询,要将分区键条件单独作为查询条件,不要放入json中,否则会影响single查询的性能
  3. 尽量避免使用"must_not"

典型JSON查询语句示例

{
  "filter": {
    "bool": {
      "should": [
        {"term": {"col1": 1, "col1": 2, "col1": 3, "col3": "testcase7"}}
      ], 
      "must": [
        {"range": {"col2": {"lte": 7, "gt": 0}, "ck1": {"gte": 2}}},
        {"match": {"col4": "+lucene -index"}}
      ]
    }
  }, 
  "sort": [{"col1":"desc"}, {"col2":"asc"}], 
  "offset": 50
}
示例里因为使用了sort和offset关键字,因此必须带分区键查询,完整cql如下:
SELECT * from example where pk1=*** and pk2=*** and expr(index_lucene, '{"filter": {"bool": {"should": [{"term": {"col1": 1, "col1": 2, "col1": 3, "col3": "testcase7"}}], "must": [{"range": {"col2": {"lte": 7, "gt": 0}, "ck1": {"gte": 2}}},{"match": {"col4": "+lucene -index"}}]}}, "sort": [{"col1":"desc"}, {"col2":"asc"}], "offset": 50}');

下面对典型的查询场景cql语句结合JSON一起进行对比举例

1. 带分区建的查询(指定pk1、pk2),需要将pk1和pk2从json条件中剥离出来,否则会影响性能
SELECT * from example where pk1=*** and pk2=*** and expr(index_lucene, 'json');
2. 查询条件: col1=1
SELECT * from example WHERE col1=1;
SELECT * from example WHERE expr(index_lucene, '{"filter": {"term": {"col1": 1}}}');
SELECT * from example WHERE expr(index_lucene, '{"filter": {"bool": {"must": [{"term": {"col1": 1}}]}}}');
上面三条语句,是等效的;类似这种情况,建议使用第一种的普通cql查询,只有当普通cql无法支持时,再使用json扩展查询;上面三个语句推荐顺序次为从上到下。
3. 查询条件:col1=1 and col2>=2
SELECT * from example WHERE col1=1 and col2>=2;
SELECT * from example WHERE expr(index_lucene, '{"filter": {"term": {"col1": 1},"range": {"col2": {"gte": 2}}}}');
SELECT * from example WHERE expr(index_lucene, '{"filter": {"bool": {"must": [{"term": {"col1": 1}}, {"range": {"col2": {"gte": 2}}}]}}}');
与第一种相同,推荐普通cql查询。
4. 查询条件:col1=1 and (col2<2 or col2>3)
SELECT * from example WHERE expr(index_lucene, '{"filter": {"bool": {"must": [{"term": {"col1": 1}}], "should": [{"range": {"col2": {"lt": 2}, "col2": {"gt": 3}}}]}}}');
SELECT * from example WHERE expr(index_lucene, '{"filter": {"bool": {"must": [{"term": {"col1": 1}}], "must_not": [{"range": {"col2": {"gte": 2, "lte": 3}}}]}}}');
上面两种方式效果相同,但是不推荐使用"must_not",性能不如"should";
5. 查询条件:col1 in (1,2,3,4) and (col2<2 or col2>3)
SELECT * from example WHERE expr(index_lucene, '{"filter": {"bool": {"should": [{"term": {"col1": 1, "col1": 2, "col1": 3, "col1": 4}}], "should": [{"range": {"col2": {"lt": 2}, "col2": {"gt": 3}}}]}}}');
SELECT * from example WHERE expr(index_lucene, '{"filter": {"bool": {"should": [{"term": {"col1": 1, "col1": 2, "col1": 3, "col1": 4}}], "must_not": [{"range": {"col2": {"gte": 2, "lte": 3}}}]}}}');
与4一样,上面两种方式效果相同,但是不推荐使用"must_not",性能不如"should";
6. 带分区键single查询:pk1='a' and pk2=1000 and col1 in (1,2,3,4) and (col2<2 or col2>3)
SELECT * from example WHERE pk1='a' and pk2=1000 and expr(index_lucene, '{"filter": {"bool": {"should": [{"term": {"col1": 1, "col1": 2, "col1": 3, "col1": 4}}], "should": [{"range": {"col2": {"lt": 2}, "col2": {"gt": 3}}}]}}}');
7. 查询条件:(((ck1<2 or ck1>=4) and (col1<2 or col1 >3)) or (pk1 in ('a', 'b', 'c'))) or (5<=col2<15 and pk2 > 2000)
SELECT * from example WHERE expr(index_lucene, '{"filter": {"bool": {"should": [{"bool": {"should": [{"bool": {"must": [{"bool": {"should": [{"range": {"ck1": {"lt": 2}, "ck1": {"gte": 4}}}]}}, {"bool": {"should": [{"range": {"col1": {"lt": 2}, "col1": {"gt": 3}}}]}}]}}, {"bool": {"should": [{"term": {"pk1": "a", "pk1": "b", "pk1": "c"}}]}}]}}, {"bool": {"must": [{"range": {"col2": {"gte":5, "lte": 15}, "pk2": {"gt": 2000}}}]}}]}}}');
8. count 查询,也可使用json构造查询条件,上面的查询条件,进行count查询,语句如下
SELECT count(*) from example WHERE expr(index_lucene, '{"filter": {"bool": {"should": [{"bool": {"should": [{"bool": {"must": [{"bool": {"should": [{"range": {"ck1": {"lt": 2}, "ck1": {"gte": 4}}}]}}, {"bool": {"should": [{"range": {"col1": {"lt": 2}, "col1": {"gt": 3}}}]}}]}}, {"bool": {"should": [{"term": {"pk1": "a", "pk1": "b", "pk1": "c"}}]}}]}}, {"bool": {"must": [{"range": {"col2": {"gte":5, "lte": 15}, "pk2": {"gt": 2000}}}]}}]}}}');

通过上面例子可以看出,通过JSON扩展语义,您可以DIY符合自身业务的查询语句,并且最高支持200层JSON嵌套,再复杂的场景也能处理!

 

华为云GaussDB(for Cassandra)搭载Lucene引擎,通过Lucene二级索引将搜索能力下沉至底层,从根本上解放了应用层查询,兼具多维查询、文本检索、统计分析等多种能力,可以完美地弥补NoSQL弱查询功能的短板,让企业从容应对海量数据的复杂查询场景。还等什么,速来体验吧!

 

附录

本文作者:华为云Cassandra团队

杭州西安深圳简历投递:mamingdi@huawei.com

更多技术文章,请关注Cassandra官方博客:

https://bbs.huaweicloud.com/community/usersnew/id_1563519101830986

华为云Cassandra官方首页:https://www.huaweicloud.com/product/geminidb.html

 

 

 

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。