架构大神毕玄:在系统设计上犯过的 14 个错 架构师电报群文字直播;为什么要从MongoDB迁移到Elasticsearch
Posted 架构师智库
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了架构大神毕玄:在系统设计上犯过的 14 个错 架构师电报群文字直播;为什么要从MongoDB迁移到Elasticsearch相关的知识,希望对你有一定的参考价值。
老胡 ༽:
下面分享一下:阿里毕玄:我在系统设计上犯过的 14 个错
老胡 ༽:
第1个错
老胡 ༽:
在设计服务框架时,我期望服务框架对使用者完全不侵入,于是做了一个在外部放一个.xml文件来描述spring里的哪些bean发布为服务的设计,这个版本发布后,第一个小白鼠的用户勉强在用,但觉得用的很别扭,不过还是忍着用下去了,到了发布的时候,发现出现了两个问题,一是这个xml文件研发也不知道放哪好,所以到了发布的时候都不知道去哪拿这个xml文件。
【架构师智库】
电报群每日动态推送架构领域的技术、方法、工具和理念资讯!
老胡 ༽:
这个设计的关键错误就在于在设计时没考虑过这个设计方式对研发阶段、运维阶段的影响,后来纠正这个错误的方法是去掉了这个xml文件,改为写了一个Spring FactoryBean,用户在spring的bean配置文件中配置下就可以。
老胡 ༽:
第2个错
老胡 ༽:
服务框架在核心应用上线时,出现了前端web应用负载高,处理线程数不够用的现象,当时处理这个故障的方式是回滚了服务框架的上线,这个故障排查了比较长的时间后,查到的原因是服务框架用的JBoss Remoting在通信时默认时间是60s,导致一些处理速度慢的请求占据了前端web应用的处理线程池。
老胡 ༽:
上面这里故障的原因简单来说是分布式调用中超时时间太长的问题,但更深层次来思考,问题是犯在了设计服务框架时的技术选型,在选择JBoss-Remoting时没有充分的掌握它的运行细节,这个设计的错误导致的是后来决定放弃JBoss-Remoting,改为基于Mina重写了服务框架的通信部分,这里造成了服务框架的可用版本发布推迟了两个多月。
老胡 ༽:
因此对于一个架构师来说,在技术选型上对技术细节是要有很强的掌控力的。
老胡 ༽:
第3个错
老胡 ༽:
在服务框架大概演进到第4个版本时,通信协议上需要做一些改造,突然发现一个问题是以前的通信协议上是没有版本号的,于是悲催的只能在代码上做一个很龌蹉的处理来判断是新版本还是老版本。
【架构师智库】
电报群每日动态推送架构领域的技术、方法、工具和理念资讯!
老胡 ༽:
这个设计的错误非常明显,这个其实只要在最早设计通信协议时参考下现有的很多的通信协议就可以避免了,因此这个错误纠正也非常简单,就是参考一些经典的协议重新设计了下。
老胡 ༽:
因此对于一个架构师来说,知识面的广是非常重要的,或者是在设计时对未来有一定的考虑也是非常重要的。
老胡 ༽:
说到协议,就顺带说下,当时在设计通信协议和选择序列化/反序列化上没充分考虑到将来多语言的问题,导致了后来在多语言场景非常的被动,这也是由于设计时前瞻性的缺失,所谓的前瞻性不是说一定要一开始就把未来可能会出现的问题就解掉,而是应该留下不需要整个改造就可以解掉的方法,这点对于架构师来说也是非常重要的。
老胡 ༽:
第4个错
老胡 ༽:
【架构师智库】
电报群每日动态推送架构领域的技术、方法、工具和理念资讯!
老胡 ༽:
这个设计的错误主要在于没有考虑生产环境中走硬件负载均衡后,这种单个长连接方式带来的问题,这个错误呢还真不太好纠正,当时临时用的一个方法是服务调用方的连接每发送了1w个请求后,就把连接自动断开重建,最终的解决方法是去掉了负载均衡设备这个中间点。
老胡 ༽:
因此对于一个架构师来说,设计时的全面性要非常的好,我现在一般更多采用的方式是推演上线后的状况,一般来说在脑海里过一遍会比较容易考虑到这些问题。
老胡 ༽:
第5个错
老胡 ༽:
服务框架在做了一年多以后,某个版本中出现了一个严重bug,然后我们就希望能通知到用了这个版本的应用紧急升级,在这个时候悲催的发现一个问题是我们压根就不知道生产环境中哪些应用和机器部署了这个版本,当时只好用一个临时的扫全网机器的方法来解决。
老胡 ༽:
这个问题后来纠正的方法是在服务发布和调用者在连接我们的一个点时,顺带把用的服务框架的版本号带上,于是就可以很简单的知道全网的服务框架目前在运行的版本号了。
老胡 ༽:
因此对于一个架构师来说,设计时的全面性是非常重要的,推演能起到很大的帮助作用。
老胡 ༽:
第6个错
老胡 ༽:
服务框架这种基础类型的产品,在发布时会碰到个很大的问题,就是需要通知到使用者去发布,导致了整个发布周期会相当的长,当时做了一个决定,投入资源去实现完全动态化的发布,就是不需要重启,等到做的时候才发现这完全就是个超级大坑,最终这件事在投入两个人做了接近半年后,才终于决定放弃,而且最终来看其实升级的问题也没那么大。
老胡 ༽:
这个问题最大的错误在于对细节把握不力,而且决策太慢。
老胡 ༽:
因此对于一个架构师来说,技术细节的掌控非常重要,同时决策力也是非常重要的。
老胡 ༽:
第7个错
老胡 ༽:
服务发布方经常会碰到一个问题,就是一个服务里的某些方法是比较耗资源的,另外的一些可能是不太耗资源,但对业务非常重要的方法,有些场景下会出现由于耗资源的方法被请求的多了些导致不太耗资源的方法受影响,这种场景下如果要去拆成多个服务,会导致开发阶段还是挺痛苦的,因此服务框架这边决定提供一个按方法做七层路由的功能,服务的发布方可以在一个地方编写一个规则文件,这个规则文件允许按照方法将生产环境的机器划分为不同组,这样当服务调用方调用时就可以做到不同方法调用到不同的机器。
老胡 ༽:
这个功能对有些场景来说用的很爽,但随着时间的演进和人员的更换,能维护那个文件的人越来越少了,也成为了问题。
老胡 ༽:
这个功能到现在为止我自己其实觉得也是一直处于争议中,我也不知道到底是好还是不好...
老胡 ༽:
因此对于一个架构师来说,设计时的全面性是非常重要的。
老胡 ༽:
| 减少犯错误的方法:多阅读、多实践、多总结。
老胡 ༽:
第8个错
老胡 ༽:
服务框架在用的越来越广后,碰到了一个比较突出的问题,服务框架依赖的jar版本和应用依赖的jar版本冲突,服务框架作为一个通用技术产品,基本上没办法为了一个应用改变服务框架自己依赖的jar版本,这个问题到底怎么去解,当时思考了比较久。
老胡 ༽:
可能是由于我以前OSGi这块背景的原因,在设计上我做了一个决定,引入OSGi,将服务框架的一堆jar处于一个独立的classloader,和应用本身的分开,这样就可以避免掉jar冲突的问题,在我做了引入OSGi这个决定后,团队的1个资深的同学就去做了,结果是折腾了近两个月整个匹配OSGi的maven开发环境都没完全搭好,后来我自己决定进去搞这件事,即使是我对OSGi比较熟,也折腾了差不多1个多月才把整个开发的环境,工程的结构,以及之前的代码基本迁移为OSGi结构,这件事当时折腾好上线后,效果看起来是不错的,达到了预期。
老胡 ༽:
但这件事后来随着加入服务框架的新的研发人员越来越多,发现多数的新人都在学习OSGi模式的开发这件事上投入了不少的时间,就是比较难适应,所以后来有其他业务问是不是要引入OSGi的时候,我基本都会建议不要引入,主要的原因是OSGi模式对大家熟悉的开发模式、排查问题的冲击,除非是明确需要classloader隔离、动态化这两个点。
老胡 ༽:
让我重新做一个决策的话,我会去掉对OSGi的引入,自己做一个简单的classloader隔离策略来解决jar版本冲突的问题,保持大家都很熟悉的开发模式。
老胡 ༽:
因此对于一个架构师来说,设计时的全面性是非常重要的。
老胡 ༽:
【架构师智库】的公开rep已经创建,欢迎大家去上面提issue,主要是为了将一些社群中遇到的一些,比较有代表性技术问题的沉淀下来!,所谓前人栽树,后人乘凉吧!git@github.com:hujf2/architect-thinktank.git
为什么要从MongoDB迁移到Elasticsearch
本文涉及到 MongoDB 与 Elasticsearch 两大阵营,可能会引起口水之争,仅代表个人经验之谈,非阵营之说。
图片来自 Pexels
我将围绕如下两个话题展开:
为什么要从 MongoDB 迁移到 Elasticsearch?
-
如何从 MongoDB 迁移到 Elasticsearch?
MongoDB 与 Elasticsearch 热度排名
现状背景
项目背景
①变更主数据,什么人在什么时间在系统哪个模块做了什么操作,数据编号是什么,操作跟踪编号是什么。
{
"dataId": 1,
"traceId": "abc",
"moduleCode": "crm_01",
"operateTime": "2019-11-11 12:12:12",
"operationId": 100,
"operationName": "张三",
"departmentId": 1000,
"departmentName": "客户部",
"operationContent": "拜访客户。。。"
}
②变更从数据,实际变更数据的变化前后,此类数据条数很多,一行数据多个字段变更就记录多条。
[
{
"dataId": 1,
"traceId": "abc",
"moduleCode": "crm_01",
"operateTime": "2019-11-11 12:12:12",
"operationId": 100,
"operationName": "张三",
"departmentId": 1000,
"departmentName": "客户部",
"operationContent": "拜访客户",
"beforeValue": "20",
"afterValue": "30",
"columnName": "customerType"
},
{
"dataId": 1,
"traceId": "abc",
"moduleCode": "crm_01",
"operateTime": "2019-11-11 12:12:12",
"operationId": 100,
"operationName": "张三",
"departmentId": 1000,
"departmentName": "客户部",
"operationContent": "拜访客户",
"beforeValue": "2019-11-02",
"afterValue": "2019-11-10",
"columnName": "lastVisitDate"
}
]
项目架构
业务系统新增或者编辑数据,产生操作日志记录发送到 Kafka 集群,基于 dataid 字段作为 key。
新增或编辑数据实际存储到 mysql 数据库。
Canal 集群订阅 MySQL 集群,按照业务系统模块配置监控的数据库与表。
Canal 将监控到的变更业务数据发送到 Kafka 集群,基于 dataid 字段作为 key。
操作日志系统从 Kafka 获取主记录数据与从记录数据。
-
操作日志系统写入数据到 MongoDB,同时需要反查询。
MongoDB 架构
服务器配置 8c/32gb/500gb ssd。
Router 路由服务器部署了 3 个节点。
Config 配置服务器部署了 3 个节点。
Shard 分片服务器部署了 9 个节点。
主操作记录设计 3 个分片。
-
从操作记录设计 3 个分片。
MongoDB 内部采用 B-Tree 作为索引结构,此索引基于最左优先原则,且必须保证查询顺序与索引字段的顺序一致才有效,这个即是优点,但在现在复杂业务场景也是致命的。
业务系统查询操作日志记录会有很多过滤条件,且查询条件是任意组合的,现有 MongoDB 是不支持的,或者说所有关系型数据库都不支持,如果要支持,得创建好多组合的 B+ 数索引,想法很不理智。
同时主记录与从记录中有很多字符类的数据,这些数据查询即要支持精确查询,也要支持全文检索,这几个方面 MongoDB 功能很单一,性能也很糟糕,业务系统查询时经常超时,反倒是 Elasticsearch 非常合适。
分片与副本实现问题,MongoDB 集合数据在设计时是需要绑定到具体的机器实例的,哪些分片分布在哪些节点上,哪些副本分布在哪些节点上。
这些都需要在配置集群时就要绑定死,跟传统的关系型数据库做分库分表本质上没有什么两样,其实现在很多数据产品的集群还是这种模式偏多,比如 Redis-cluster,ClickHouse 等。
而 Elasticsearch 的集群与分片和副本没有直接的绑定关系,可以任意的平衡调整,且节点的性能配置也可以很容易差异化。
操作日志数据量增加很快,单日写入超过千万条,不用多久,运维人员就需要对服务器进行扩容,且相对 Elasticsearch 复杂很多。
MongoDB 单集合数据量超过 10 亿条,此情况下即使简单条件查询性能也不理想,不如 Elasticsearch 倒排索引快。
公司对于 ES 与 MongoDB 技术栈的经验积累不同,Elasticsearch 在很多项目中运用,非常核心的项目也是大量运用。
对于其技术与运维经验更丰富,而 MongoDB 如果除去核心业务场景,几乎找不到合适的切入口,实际没有人敢在核心项目中使用 MongoDB,这就很尴尬。
迁移方案
上层应用系统迁移,原来是针对 MongoDB 的语法规则,现在要修改为面向 Elasticsearch 语法规则。
下层 MongoDB 数据迁移到 Elasticsearch。
Elastic-Api 支持多索引匹配查询,完美利用 Elastic 的特性解决跨多个月份的查询合并。对于非核心数据索引,按年创建索引生成足以。
主数据先到操作日志系统,从数据后到,从数据写的时候先拼凑主数据记录和 Binlog 字段数据。
从数据先到操作日志系统,主数据后到,主数据更新从索引的相关的索引字段。
通过这个中间索引可以找到主数据记录的 Id 或者从记录 Id, 索引数据模型多如下,detailId 为从索引的 _id 的数组记录。
{
"dataId": 1,
"traceId": "abc",
"moduleCode": "crm_01",
"operationId": 100,
"operationName": "张三",
"departmentId": 1000,
"departmentName": "客户部",
"operationContent": "拜访客户",
"detailId": [
1,
2,
3,
4,
5,
6
]
}
前面我们讲过主记录和从记录都是一个 Kafka 的分区上,我们拉一批数据的时候,操作 ES 用的用到的核心 API:
#批量获取从索引的记录
_mget
#批量插入
bulk
#批量删除中间临时索引
_delete_by_query
迁移过程
历史型数据。操作日志记录数据属于历史性的数据,记录产生之后几乎无需二次修改,等同于离线数据。
非持续性迁移。项目全部完工之后,原有的 MongoDB 集群会全部销毁,不会有二次迁移需求。
数据量问题。原有 MongoDB 操作日志数据量有几十亿条,迁移过程不能太快也不能太慢,速度太快,MongoDB 集群会出现性能问题,速度太慢,项目周期太长,增加运维的成本与复杂度。否则可以选择 Hadoop 作为中转平台的迁移。
DataX 源码特定场景改造。如日期类型的转换、索引主键 _id 的生成、索引主键 _id 映射,支持重复同步。
多实例多线程并行。主数据同步部署多个实例,从数据同步也部署多个实例,单实例中配置多个 Channel。
临时修改索引的一些设置,当数据同步完之后再修改回来,如下:
"index.number_of_replicas": 0,
"index.refresh_interval": "30s",
"index.translog.flush_threshold_size": "1024M"
"index.translog.durability": "async",
"index.translog.sync_interval": "5s"
操作日志项目采用 Spring Boot 构建,增加了自定义配置项,如下:
#应用写入mongodb标识
writeflag.mongodb: true
#应用写入elasticsearch标识
writeflag.elasticsearch: true
第一次上线的时候,先将 2 个写入标识设置为 true,双写 MongoDB 和 ES。
对于读,提供 2 个不同接口,前端自由的切换。
等数据迁移完,没有差异的时候,重新更改 flag 的值。
结语
简介:Elastic-stack 产品深度用户,ES 认证工程师,2012 年接触 Elasticsearch,对 Elastic-Stack 开发、架构、运维等方面有深入体验,实践过多种 Elasticsearch 项目,最暴力的大数据分析应用,最复杂的业务系统应用;业余为企业提供 Elastic-stack 咨询培训以及调优实施。
编辑:陶家龙
我已经提了一个问题[捂脸]
【架构师智库】
电报群每日动态推送架构领域的技术、方法、工具和理念资讯!
以上是关于架构大神毕玄:在系统设计上犯过的 14 个错 架构师电报群文字直播;为什么要从MongoDB迁移到Elasticsearch的主要内容,如果未能解决你的问题,请参考以下文章