背景
有2种常见的多维度查询场景,分别是:
- 带多个筛选条件的列表查询
- 不含分库分表列的其他维度查询
普通的数据库查询,很难实现上述需求场景,更不用提模糊查询、全文检索了。
下面结合楼主的经验和知识,介绍初级方案、进阶方案(上ElasticSearch),大部分情况下推荐使用ElasticSearch来实现多维度查询,赶时间的读者可以直接跳到“进阶方案:将ElasticSearch添加到现有系统中”。
初级方案
1、根据常见查询场景,增加相应字段的组合索引
这个是为了实现带多个筛选条件的列表查询的。
优点
- 非常简单
- 读写不一致时间较短:取决于数据库主从同步延时,一般为毫秒级别
缺点
- 非常局限:除非筛选条件比较固定,否则难以应付后续新增或修改筛选条件
- 如果每次来新的筛选查询字段的需求,就新增索引,最终导致索引过于庞大,影响性能
于是就出现了经典的一幕:产品提需求说要支持某个新字段的筛选查询,开发反馈说做不了、或者成本很高,于是不了了之 :)
2、异构出多份数据
更加优雅的方式,是异构出多份数据。
例如,C端按用户维度查询,B端按店铺维度查询,如果还有供应商,按供应商维度查询。一个数据库只能按一种维度来分库。
(1)程序写入多个数据源
优点是:非常简单。
缺点
- 跨库写存在一致性问题(除非不同维度的表使用公共的分库,事务写入),性能低
- 不能灵活支持更多其他维度的查询
(2)借助Canal实现数据的自动同步
通过Canal同步数据,异构出多个维度的数据源。详见之前写的这篇文章:架构师必备:巧用Canal实现异步、解耦的架构
优点是:更加优雅,无需改动程序主流程。
####缺点
- 仍然无法解决不断变化的需求,不可能为了支持新维度就异构出一份新数据
进阶方案:将ElasticSearch添加到现有系统中
应用架构
现有系统一般都会用到MySQL数据库,需要引入ES,为系统增强多维度查询的功能。
MySQL继续承担业务的实时读写请求、事务操作,ES承担近实时的多维度查询请求,ES可支撑十万级别qps(取决于节点数、分片数、副本数)。
需要注意的是:同步数据至ES是秒级延迟(主要耗费在索引refresh),而查询已进入索引的文档,是在数毫秒到数百毫秒级别。
导入数据
需要同步机制,来把MySQL中的数据导入到ES中,主要流程如下:
- 预先定义ES索引的mapping配置,而不依赖ES自动生成mapping
- 初始全量导入,后续增量导入:Canal+MQ数据管道同步,不需要或仅需少量代码工作
- 数据过滤:不导入无需检索的字段,减小索引大学,提高性能
- 数据扁平化处理:如果数据库中有json字段列,需要从中提取业务字段,避免嵌套类型的字段,提高性能
查询数据
-
从ES 8.x版本开始,建议使用Java api client,并且要Java 8及以上环境,因为可使用各种lambda函数,来提高代码可读性
- 优点是新客户端与server代码完全耦合(相比于原Java transport client,在8.x版本已废弃),并且API风格与http rest api很接近(相比于原Java rest client,在8.x版本已废弃),只要熟练掌握http json请求体写法,即可快速上手。
- 底层使用的还是原来的low level rest client,实现了http长连接、访问ES各节点的负载均衡、故障转移,最底层依赖的是apache http async client。
-
ES 7.x版本及以下,或使用Java 7及以下,建议升级,否则就只能继续用high level rest client。
代码示例如下(含详细注释):
public class EsClientDemo {
// demo演示:创建client,然后搜索
public void createClientAndS