背景

有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 createClientAndSearch() throws Exception {// 创建底层的low level rest client,连接ES节点的9200端口RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();// 创建transport类,传入底层的low level rest client,和json解析器ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());// 创建核心client类,后续操作都围绕此对象ElasticsearchClient esClient = new ElasticsearchClient(transport);// 多条件搜索// fluent API风格,并且使用lambda函数提高代码可读性,可以看出Java api client的语法,同http json请求体非常相似String searchText = "bike";String brand = "brandNew";double maxPrice = 1000;// 根据商品名称,做match全文检索查询Query byName = MatchQuery.of(m -> m.field("name").query(searchText))._toQuery();// 根据品牌,做term精确查询Query byBrand = new Query.Builder().term(t -> t                          .field("brand")                    .value(v -> v.stringValue(brand))).build();// 根据价格,做range范围查询Query byMaxPrice = RangeQuery.of(r -> r.field("price").lte(JsonData.of(maxPrice)))._toQuery();// 调用核心client,做查询SearchResponse<Product> response = esClient.search(s -> s.index("products")  // 指定ES索引.query(q -> q       // 指定查询DSL.bool(b -> b    // 多条件must组合,必须同时满足.must(byName).must(byBrand).must(byMaxPrice))),Product.class);// 遍历命中结果List<Hit<Product>> hits = response.hits().hits();for (Hit<Product> hit: hits) {Product product = hit.source();  // 通过source获取结果logger.info("Found product " + product.getName() + ", score " + hit.score());}}}

可参阅:https://www.elastic.co/guide/en/elasticsearch/client/index.html

数据模型转换

因为既有MySQL,又有ES,所以有2种异构的数据模型。需要在代码中定义2种数据模型,并且实现类型互相转换的工具类。

  • MySQL数据VO
  • ES数据VO
  • MySQL数据VO、ES数据VO互相转换工具
  • 业务层BO
  • 接口DTO

原理概要

ES之所以比MySQL,能胜任多维度查询、全文检索,是因为底层数据结构不同:

  • ES倒排索引

    • 如果是全文检索字段:会先分词,然后生成 term -> document 的倒排索引,查询时也会把query分词,然后检索出相关的文档。相关度算法如TF-IDF(term frequency–inverse document frequency),取决于:词在该文档中出现的频率(TF,term frequency),越高代表越相关;以及词在所有文档中出现的频率(IDF,inverse document frequency),越高代表越不相关,相当于是一个通用的词,对相关性影响较小。
    • 如果是精确值字段:则无需分词,直接把query作为一个整体的term,查询对应文档。
    • 因为文档中的所有字段,都生成了倒排索引,所以能处理多维度组合查询
  • MySQL B+树
    • B+树的非叶子节点记录了孩子节点值的范围,而叶子节点记录了真正的一组值,并且在同一层,形成了一个有序链表
    • 组合索引需要显式创建:选择需索引的字段、并且顺序是重要的,所以如果待查询的字段不在索引中,就无法高效查询,可能演变为全表扫描(对聚簇索引的叶子节点做一次遍历)

另外简要回顾一下ES的架构要点:

  • 节点分为主节点、数据节点,一个节点上可以有多个分片,分片分为主分片、副本分片,1对多,主分片与副本分片分布在不同的节点,来实现高可用
  • 主分片数在创建时,就需要指定,在创建后不能随意更改(如果变化,路由就会出错);而副本分片可以增加,来提高ES集群的查询QPS
  • 路由算法:id % 主分片数,如果创建文档时不指定id,则ES会自动生成;一般会传自定义业务id

优点、缺点

优点

  • 支持各字段的多维度组合查询,无惧未来新增字段(主要成本在于新增字段后、重建索引)
  • 与现有系统完全解耦,适合架构演进
  • 在数据量级上远胜Mysql,最大支持PB级数据的存储和查询

缺点

  • 读写不一致时间在秒级:因为有2个耗时阶段,一是同步阶段将数据从MySQL数据库写入ES,二是ES索引refresh阶段,数据从buffer写入索引后才可查到

    • 因此一个trick就是,在写入操作后,前端延迟调用后端的列表查询接口,比如延迟1秒后再展示
  • 超高并发下存在瓶颈,存在稳定性问题:目前原生版本支持大约 3-5 万分片,性能已经到达极限,创建索引基本到达 30 秒+ 甚至分钟级。节点数只能到 500 左右基本是极限了。但依然能满足绝大部分场景。数据来源:https://elasticsearch.cn/slides/259#page=30

ES最佳实践

  • 只把需要搜索的数据导入ES,避免索引过大
  • 数据扁平化,不用嵌套结构,提高性能
  • 合理设置字段类型,预先定义mapping配置,而不依赖ES自动生成mapping
  • 精确值的类型指定为keyword(mapping配置),并且使用term查询
    • 精确值是指无需进行range范围查询的字段,既可以是字符串,比如书的作者名字,也可以是数值,比如商品id、订单id、图书ISBN编号、枚举值。在使用中,大部分场景是以id类作为精确值
  • 避免无路由查询:无路由查询会并发在多个索引上查询、归并排序结果,会使得集群cpu飙升,影响稳定性
  • 避免深度分页查询:如有大量数据查询,推荐用scroll滚动查询
  • 设置合理的文件系统缓存(filesytem cache)大小,提高性能:因为ES查询的热数据在文件系统缓存中
  • ES分片数在创建后不能随意改动,但是副本数可以随时增加,来提高最大QPS。如果单个分片压力过大,需要扩容。

更进一步

前面提到ES超高并发下存在瓶颈,极端情况下可能遇到OOM,因此超高并发下需要C++实现的专用搜索引擎
例如:

  • 百度:通用搜索引擎,根据文字、图片搜索信息
  • 电商垂类:电商专用搜索引擎,比如根据关键词查找商品,或根据品牌、价格筛选商品,可总结为商品的搜索、广告、推荐

架构师必备:多维度查询的最佳实践相关推荐

  1. 【全网首发】听阿里云产品架构师罗小飞解读CDN产品最佳实践

    简介:近期,阿里云<极速奔跑吧 2021>首场直播在线开播.此次直播围绕CDN行业最佳实践展开分享,不仅对全网首发的阿里云CDN产品最佳实践图进行了详细解读,还对CDN产品和客户场景如何更 ...

  2. 华为架构师整理Redis数据结构的大厂最佳实践

    1 概述 数据结构和内部编码 无传统关系型数据库的 Table 模型 schema 所对应的db仅以编号区分.同一 db 内,key 作为顶层模型,它的值是扁平化的.即 db 就是key的命名空间. ...

  3. 互联网架构师必备技能

    一.每个好架构师都是一位出色的程序员 这一点毋庸置疑,如果不是写过N年代码的优秀程序员,一定不是好的架构师."架构师"这是一个听上去比较虚的职位,它的主要价值在于"落地& ...

  4. javaweb k8s_K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程...

    K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程 课程内容是关于Kubernetes微服务架构学习课程,基于K8S开展ASP.NET核心进行微 ...

  5. 国外经典!架构师必备:《MongoDB实战》第2版

    0.MongoDB优点与学习重要性 MongoDB是最流行的NoSQL数据库!MongoDB在NoSQL中排名第一!它高性能.轻量级,易于扩展.适用于移动互联网敏捷发展的需求.架构师必备!MongoD ...

  6. 【书】测试架构师-必备的6个能力

    进入现有的公司后,发现自己的语言能力真的是极差,同样的一件事情,别人能说的很高端,且有理有据,自己却说不出来,就算硬说也说的很苍白无力,毫无说服力. 例如,当自己很忙的时候,告诉领导自己忙不过来,但是 ...

  7. Java架构师必备知识体系

    写给一名java开发的一段话: 最近公司在组织面试,并由我担任面试官,前前后后面了几天,大概有十来个人,基本都是五年以上开发经验的,我问的问题也都不是很困难,都是一些偏原理和场景解决方案. 如: 1. ...

  8. 安卓sdk开发!阿里面试100%会问到的JVM,架构师必备技能

    接触这一行也有很久了,从开始的实习到带团队,中间接触过很多人,前不久身边刚好有人去面试了阿里,抖音等这些公司还成功的面试上了,现在来分享一下面试前需要准备的知识点 很多人去面试之前,不知道会问到那些知 ...

  9. 系统架构师必备高阶思维

    系统架构师必备高阶思维: 1.全球化思维.全局思维 2.文化差异思维 3.小步快跑,容忍试错的思维(试错成本评估) 4.用户中心思维.应用中心思维 5.中台思维,聚焦构建能力共享中心,实现赋能 6.数 ...

最新文章

  1. NoBrokersAvailableError
  2. javascript模拟sleep
  3. Pin code码已被重置
  4. 跑三小时的monkey测试该怎么算_浅谈App测试(下)~带音频
  5. activiti 批量 mysql_Activiti6系列(3)- 快速体验
  6. ​【文末有福利】连续型随机变量及实例详解
  7. v-viewer图片打不开一直在刷新_python实现将一组图片转化成视频
  8. 【ElasticSearch】Es 源码之 ClusterService 源码解读
  9. ParallelActivity
  10. C语言· 实现各进制间的相互转换
  11. window10_vs2015安装教程
  12. MapperReduce初学附加自定义输出的NameWordCount统计
  13. 计算机事业单位简答题MAC
  14. python十六进制字符码转中文
  15. 《麦田里的守望者》阅读笔记
  16. html name选择器,iframe标签的name属性
  17. 如何将SNS光纤交换机(OEM博科FC交换机)恢复为出厂设置
  18. Android初级,实现网易云音乐歌曲列表界面效果,播放界面效果,ListView,ViewPager方法详解
  19. C#求解一元二次方程的根
  20. [经验教程]2022京东618红包活动时间是什么时候开始什么时候结束及怎么领取京东618红包?

热门文章

  1. 1936年 柏林 第十一届奥运会
  2. 强化学习 ——On-Policy与Off-Policy
  3. photoshop不透明度和填充度原理
  4. Hadoop分布式(最小集群)搭建(三台虚拟机)
  5. 编程队伍队名_献礼集团25周年 | 走进编程大赛里的“程序媛”
  6. 抖音超火HTML+CSS+JS制作3D炫酷魔方
  7. 程序员成长的几件法宝
  8. 26HTML5期末大作业:动漫电网站设计——动漫电影《你的名字》(7页) HTML+CSS大作业_ 动漫电网页制作作业_动漫电网页设计...
  9. 搭建微信管家基于IIS7并支持伪静态
  10. 关于有音频中去除人声失败的问题