架构师必备:多维度查询的最佳实践
liebian365 2024-11-02 13:34 21 浏览 0 评论
背景
有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++实现的专用搜索引擎
例如:
- 百度:通用搜索引擎,根据文字、图片搜索信息
- 电商垂类:电商专用搜索引擎,比如根据关键词查找商品,或根据品牌、价格筛选商品,可总结为商品的搜索、广告、推荐
相关推荐
- 4万多吨豪华游轮遇险 竟是因为这个原因……
-
(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...
- “菜鸟黑客”必用兵器之“渗透测试篇二”
-
"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...
- 科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白
-
作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...
- 麦子陪你做作业(二):KEGG通路数据库的正确打开姿势
-
作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...
- 知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势
-
智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...
- 每日新闻播报(September 14)_每日新闻播报英文
-
AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...
- 香港新巴城巴开放实时到站数据 供科技界研发使用
-
中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...
- 5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper
-
本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...
- Qt动画效果展示_qt显示图片
-
今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...
- 如何从0到1设计实现一门自己的脚本语言
-
作者:dong...
- 三年级语文上册 仿写句子 需要的直接下载打印吧
-
描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...
- C++|那些一看就很简洁、优雅、经典的小代码段
-
目录0等概率随机洗牌:1大小写转换2字符串复制...
- 二年级上册语文必考句子仿写,家长打印,孩子照着练
-
二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)