某智能产品业务数据以前存储在Elasticsearch(Es)中,磁盘占用约30T(按照单副本计算),总数据量25亿,按照不一样业务分类分别存在于不一样表中。迁移前,业务存在较严重的性能及成本问题,当前业务已经迁移部分数据到mongodb中,迁移后效果明显,成本实现十倍级节省,业务抖动问题也得以解决。html
当前我司已有数百亿Es数据迁移mongodb,同时也有数百亿mongodb迁移Es,根本缘由业务就是选型错误引发。mysql
本文以该场景业务迁移做为案例,主要分享如下方面的内容:git
- mongodb适用场景及不适用场景
- mongodb和Es各自优点
- mongodb和Es一样数据,真实磁盘消耗对比
- 对《从MongoDB迁移到ES后,咱们减小了80%的服务器》一文的一些不一样见解
没有万能的数据库,本文最后会总结mongodb和Es各自的适用场景,以客观立场分析评价mongodb和Es,拒绝“捧一个,踩一个”。github
Es绝对是一款优秀的搜索引擎,在模糊匹配、全文搜索、复杂检索等方面相比mongodb拥有更大的优点。算法
本文对应业务场景查询比较简单,查询更新等条件都是固定字段,不涉及复杂检索,因此在此场景mongodb更具优点。另外,mongodb当前默认的wiredtiger存储引擎,在高压缩、高性能、锁粒度等方面进一步提高了mongodb在该场景下的优点。sql
我司已有多个业务数百亿级数据从Es迁移mongodb,因为其余业务迁移mongodb的时间较早,以前没有详细记录迁移先后的ES和mongodb详细资源对比,只有大概资源消耗比值。为了尽可能客观评价迁移先后的数据对比,所以选择近期正在从Es迁移mongodb的一个集群,同时记录源集群和目的集群的详细资源占用状况,这样的对比结果会更加客观真实。mongodb
关于做者
前滴滴出行专家工程师,现任OPPO文档数据库mongodb负责人,负责数万亿级数据量文档数据库mongodb内核研发、性能优化及运维工做,一直专一于分布式缓存、高性能服务端、数据库、中间件等相关研发。后续持续分享《MongoDB内核源码设计、性能优化、最佳运维实践》,Github帐号地址:https://github.com/y123456yz数据库
序言
本文是oschina专栏《mongodb 源码实现、调优、最佳实践系列》的第21篇文章,其余文章能够参考以下连接:数组
Qcon-万亿级数据库 MongoDB 集群性能数十倍提高及机房多活容灾实践缓存
Qcon 现代数据架构 -《万亿级数据库 MongoDB 集群性能数十倍提高优化实践》核心 17 问详细解答
百万级高并发 mongodb 集群性能数十倍提高优化实践 (上篇)
百万级高并发 mongodb 集群性能数十倍提高优化实践 (下篇)
Mongodb特定场景性能数十倍提高优化实践(记一次mongodb核心集群雪崩故障)
经常使用高并发网络线程模型设计及mongodb线程模型优化实践
盘点 2020 | 我要为分布式数据库 mongodb 在国内影响力提高及推广作点事
话题讨论 | mongodb 拥有十大核心优点,为什么国内知名度远不如 mysql 高?
mongodb 详细表级操做及详细时延统计实现原理 (快速定位表级时延抖动)
[图、文、码配合分析]-Mongodb write 写 (增、删、改) 模块设计与实现
Mongodb集群搭建一篇就够了-复制集模式、分片模式、带认证、不带认证等(带详细步骤说明)
300条数据变动引起的血案-记某十亿级核心mongodb集群部分请求不可用故障踩坑记
- 业务背景
该业务存储智能产品相关数据,总数据量20多亿,单个集群ES磁盘消耗约30T。业务迁移背景以下(如下为业务开发同事整理):
- Es集群不太稳定,形成秒级耗时,对咱们业务影响挺大,感知很是明显。
- 具体咱们业务需求,主要是根据用户id来进行精确查询,没有复杂全文检索、模糊查询等需求,因此其实用不到Es的优势。
- Es成本过高。
- 源Elasticsearch集群资源及部署状况
源ES集群业务在两个机房各申请了一个集群,由业务本身经过双写的方式来保障数据一致性,当一个集群异常业务本身切流量到另一个集群。
源ES集群部署架构及资源规格以下:
2.1 源Elasticsearch集群部署架构
如上图所示,因为为了实现两机房双活容灾及单集群抖动引发的业务故障,在A机房和B机房各搭建了一个ES集群,业务经过双写来本身维护两个集群的数据一致性。
当A机房集群1异常,或者A机房掉电,则业务切流量到B机房备集群2,对应架构图以下:
2.2 集群资源规格
A机房集群1和B机房集群2内部部署架构彻底已有,单个集群总共有26个节点,每一个节点都部署在容器中,单个容器规格资源以下所示:
- CPU:32
- 内存:64G
- 磁盘:2T
- 磁盘类型:SSD
单个集群总资源消耗以下:
- CPU:32*26=832
- 内存:64G*32=2048G
- 磁盘:2T*32=64T
两个集群总资源消耗以下:
- CPU:32*26*2=1664
- 内存:64G*32*2=4096G
- 磁盘:2T*32*2=128T
2.3 源集群架构业务痛点
从上面的分析能够看出,为了保障业务多活和集群高可用,业务经过双写实现,异常后业务本身判断切换,这增长了业务痛点。该架构主要痛点以下:
- 成本高,自己每一个集群都是多副本,第2个备集群进一步增长了成本
- 增长了业务开发难度,业务须要双写逻辑
- 数据一致性没法获得保障,例如集群1异常或者故障,业务读写切到集群2,当集群1恢复正常,异常这断时间内,集群2的数据会比集群1多。
- 不利于业务快速迭代开发
- 业务只是按照固定字段作查询,查询条件单一,这种场景选择mongodb自己性能会更好。
- 目的mongodb集群架构
业务开始迁移mongodb的时候,经过和业务对接梳理,该集群规模及业务需求总结以下:
- 总数据量20多亿
- Es数据单集群磁盘消耗总和30.5T左右
- 读写峰值流量流量很小,几百上千
- 同城两机房多活容灾
3.1 mongodb资源评估
分片数及存储节点套餐规格选定评估过程以下:
- 内存评估
我司都是容器化部署,以以网经验来看,mongodb对内存消耗不高,历史百亿级以上mongodb集群单个容器最大内存基本上都是64Gb,所以内存规格肯定为64G。
- 分片评估
业务读写流量很低,可是数据量较大,所以分片数肯定为2个分片。
- 磁盘评估
按照以往测试验证及线上真实数据迁移对比,一样的数据存入mongodb和Es中真实磁盘消耗占好比下:mongodb:Es ≈1:6
25亿Es真实磁盘消耗30.5T,预计mongodb磁盘消耗5T左右,考虑到将来数据增加,咱们按照50亿数据计算,预计须要10T空间。2个分片,所以每一个分片5T数据,最终肯定单个mongod实例容器磁盘规格5T。
- CPU规格评估
因为容器调度套餐化限制,所以CPU只能限定为16CPU(实际上用不了这么多CPU)。
- mongos代理及config server规格评估
此外,因为分片集群还有mongos代理和config server复制集,所以还须要评估mongos代理和config server节点规格。因为config server只主要存储路由相关元数据,所以对磁盘、CUP、MEM消耗都很低;mongos代理只作路由转发只消耗CPU,所以对内存和磁盘消耗都不高。最终,为了最大化节省成本,咱们决定让一个代理和一个config server复用同一个容器,容器规格以下:
8CPU/8G内存/50G磁盘,一个代理和一个config server节点复用同一个容器。
分片及存储节点规格总结:2分片/16CPU、64G内存、5T磁盘。
mongos及config server规格总结:8CPU/8G内存/50G磁盘
3.2 集群部署架构
因为该业务所在城市只有两个机房,所以咱们采用2+2+1(2mongod+2mongod+1arbiter模式),在A机房部署2个mongod节点,B机房部署2个mongod节点,C机房部署一个最低规格的选举节点,以下图所示:
说明:
- 每一个机房代理部署2个mongos代理,保证业务访问代理高可用,任一代理挂掉,对应机房业务不受影响。
- 若是机房A挂掉,则机房B和机房C剩余2mongod+1arbiter,则会在B机房mongod中重新选举一个主节点。arbiter选举节点不消耗资源
- 客户端配置nearest ,实现就近读,确保请求经过代理转发的时候,转发到最近网络时延节点,也就是同机房对应存储节点读取数据。
弊端:若是是异地机房,B机房和C机房写存在跨机房写场景。若是A B C为同城机房,则没用该弊端,同城机房时延能够忽略。
4. 性能优化过程
该集群优化过程按照以下两个步骤优化:数据迁移开始前的提早预优化、迁移过程当中瓶颈分析及优化、迁移完成后性能优化。
4.1 数据迁移开始前的提早预操做
和业务沟通肯定,业务每条数据都携带有惟一_id(用户生成的,不是mongodb内部生成),同时业务查询更新等都是根据_id维度查询该设备下面的单条或者一批数据,所以片建选择_id。
- 分片方式
为了充分散列数据到2个分片,所以选择hash分片方式,这样数据能够最大化散列,同时能够知足同一个_id数据落到同一个分片,保证查询效率。
- 预分片
mongodb若是分片片建为hashed分片,则能够提早作预分片,这样就能够保证数据写进来的时候比较均衡的写入多个分片。预分片的好处能够规避非预分片状况下的chunk迁移问题,最大化提高写入性能。
sh.shardCollection("user_xxx.user_xxx", {_id:"hashed"}, false, { numInitialChunks: 8192} )
注意事项:切记提早对ssoid建立hashed索引,不然对后续分片扩容有影响。
- 就近读
客户端增长nearest 配置,从离本身最近的节点读,保证了读的性能。
- mongos代理配置
A机房业务只配置A机房的代理,B机房业务只配置B机房代理,同时带上nearest配置,最大化的实现本机房就近读,同时避免客户端跨机房访问代理。
- 禁用enableMajorityReadConcern
禁用该功能后ReadConcern majority将会报错,ReadConcern majority功能注意是避免脏读,和业务沟通业务没该需求,所以能够直接关闭。
mongodb默认使能了enableMajorityReadConcern,该功能开启对性能有必定影响,参考:
OPPO百万级高并发MongoDB集群性能数十倍提高优化实践
- 存储引擎cacheSize规格选择
单个容器规格:16CPU、64G内存、7T磁盘,考虑到全量迁移过程当中对内存压力,内存碎片等压力会比较大,为了不OOM,设置cacheSize=42G。
4.2 数据迁移过程当中优化过程
全量数据迁移过程当中,迁移速度较块,内存涨数据较多,当脏数据比例达到必定比例后用户读写请求对应线程将会阻塞,用户线程也会去淘汰内存中的脏数据page,最终写性能降低明显。
wiredtiger存储引擎cache淘汰策略相关的几个配置以下:
因为业务全量迁移数据是持续性的大流量写,而不是突发性的大流量写,所以eviction_target、eviction_trigger、eviction_dirty_target、eviction_dirty_trigger几个配置用处不大,这几个参数阀值只是在短期突发流量状况下调整才有用。
可是,在持续性长时间大流量写的状况下,咱们能够经过提升wiredtiger存储引擎后台线程数来解决涨数据比例太高引发的用户请求阻塞问题,淘汰涨数据的任务最终交由evict模块后台线程来完成。
全量大流量持续性写存储引擎优化以下:
db.adminCommand( { setParameter : 1, "wiredTigerEngineRuntimeConfig" : "eviction=(threads_min=4, threads_max=20)"})
更多存储引擎及mongodb内核涉及实现参考:mongodb源码分析、更多实践案例细节
4.3 业务流量读写优化
前面章节咱们提到,在容器资源评估的时候,咱们最终肯定选择单个容器套餐规格为以下:
16CPU、64G内存、5T磁盘。
全量迁移过程当中为了不OOM,预留了约1/3内存给mongodb server层、操做系统开销等,当数据迁移完后,业务写流量相比全量迁移过程小了不少。
也就是说,前量迁移完成后,cache中涨数据比例几乎不多,基本上不会达到20%阀值,业务读流量相比以前多了不少(数据迁移过程当中读流量走原Es集群)。为了提高读性能,所以作了以下性能调整(提早建好索引):
- 节点cacheSize从以前的42G调整到55G,尽可能多的缓存热点数据到内存,供业务读,最大化提高读性能。
- 天天凌晨低峰期作一次cache内存加速释放,避免OOM。
5. 迁移mongodb后性能对比
当前已有2个表从Es迁移到该mongodb集群,同时该业务新增了15亿其余业务数据到该集群,当前目的mongodb集群已有近20亿数据。
5.1 Es时延状况
因为该集群ES没有历史时延统计曲线统计,所以ES的时延统计只有如下现象(来自业务方反馈):
查询秒级耗时,对咱们业务影响挺大,感知很是明显。
5.2 mongodb集群时延曲线
从上面的监控能够看出,因为除了迁移有源Es的数据,另外还有该业务的其余业务数据流量流向该集群,所以mongodb集群流量相比Es会更高,mongodb总体时延约1.5ms左右,远远好于以前Es的有时秒级时延抖动。
6. 迁移成本收益对比
6.1 ****ElasticSearch集群规格
原Es单个集群一共26个节点,每一个节点副本容器规格:32CPU、64Gmem、2T磁盘,磁盘类型SSD,单个集群规格总结以下:
- 单集群节点总数:26
- 每一个节点规格:32CPU、64Gmem、2T磁盘
- 总数据量:25亿
- 为了实现机房多活容灾和业务高可用,实际部署了两个Es集群,实际规格成本还的在上面的基础上增长一倍。
6.2 mongodb集群规格
当前该mongodb集群已有约16亿数据(其中部分为Es集群之外数据,该集群除了存储部分Es迁移过来的数据,还存储该业务线其余业务数据),该mongodb集群规格以下:
- 分片数:2
- 单分片副本数:4
- 每一个节点规格:16CPU、64G mem、5T磁盘
- 两个分片预计存储最大数据量:预计存储Es集群中总数据量的两倍。
6.3 成本对比计算过程
- CPU、MEM内存成本对比计算过程
源Es两个集群和目的mongodb集群资源对好比下:
说明:因为集群部署方式可能有不少冗余,上面的CPU和内存成本比对比实际上不客观,可能Es部署时候规格设置浪费。固然,mongodb实际上CPU资源也很是空闲,因此CPU和内存指标对比无太大参考做用。
- 磁盘成本对比计算过程(成本比约6:1)
因为目的mongodb集群中当前除了有源ES迁移过来的部分表外,还有该业务的其余数据,为了保障磁盘对比的客观性,磁盘对比选材过程以下(说明:源Es有两个集群,这里只计算单个集群单分数据的磁盘消耗,若是按照两个集群计算,磁盘成本比为12:1,这种对比方法不客观):
- 只对比从源Es中迁移到mongodb中的表
- 只对比源Es集群和目的mongodb集群表中数据量彻底同样的表
- 排除源ES迁移到mongodb可是当前尚未迁移完的表
- 源Es集群只计算一个集群单副本的磁盘消耗
经过上面的选择算法,基本上作到了一样数据在Es和mongodb中的对比,磁盘真实消耗对比结果以下(说明:如下数据的Es磁盘占用为单个Es集群单副本方式计算结果,由我司Es运维开发人员提供;mongodb磁盘占用计算方法为2个分片主节点真实磁盘占用之和,包括数据磁盘消耗+索引磁盘消耗):
从上面的数据对比能够看出,一样的数据Es磁盘占用约为mongodb磁盘占用的6倍,和以前其余Es迁移mongodb过程的数据占用比值相似。
表1文档内容以下:
1.{ 2. "_id" : null, 3. "channel" : null, 4. "content" : "04A193398BE7xxx7E080E2C3CC7B3sxxxxxxxxxC99F9520B8CD0842638DB0F550E125xxxxxxxxxxxxxDB5D3F320642A42CECD3EB5C27adfadfasdfasfdasdfadfa669D6633A0E48C65B2623EA15E6DBB0FBF643150E18DD3D0575BDE448C03735A8841E312F8AF0D2BF67D1D357D1AB6249BF3FA4E014C5Axxxxxxxxxxxxxx30C10487667", 5. "create_time" : ISODate("2021-02-16T09:56:03Z"), 6. "duddddd" : "6F9E856EDDBB5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxF41B0676FB2DE2171C18450E683DE1E9523B518F266856E01B0D6855E29911E5D10F0FA7E4A8EE5816333D89296E7554F05A58", 7. "update_time" : ISODate("2021-02-16T09:56:03Z") 8.}
表2文档内容以下:
1.{ 2. "_id" : "F9654874C1A37F74DA5E862C408EDC45", 3. "channel" : "10001", 4. "content" : "BA7adfafadfaF5XXXXXXXXX6D7adfafafda9XXXXXXXXXXXXXXfurwt54fewwrsfgsE", 5. "create_time" : ISODate("2021-01-21T11:49:46Z"), 6. "imssses" : "F96548XXXXXXXXXXXXXXXXX2C408EDC45", 7. "update_time" : ISODate("2021-01-21T11:49:46Z") }
7. Elasticsearch和mongodb各自适用场景总结
因为业务开发对业务场景评估不到位,当前我司线上mongodb和Es适用过程存在以下现象:
- 数百亿Es数据迁移mongodb
- 也有数百亿mongodb数据迁移到Es
从线上真实业务使用状况为例,如下场景不适合mongodb,实际上也不适合mysql等数据库:
- 8字段以上的随机组合查询
有些业务场景,查询条件是由用户触发,查询条件不固定,可能存在多个字段的随机组合查询。mongodb和mysql等数据库,都须要手动建立索引,因为8字段以上的随机组合查询状况种类太多,所以很难手动建索引覆盖全部场景,因此选择Es更优,只是成本会更高。
- 全文检索
虽然mongodb也支持全文检索,mongodb-4.2如下版本全文检索能力性能和ES无法比,建议全文检索适用Es。
mongodb-4.2全文搜索已经开始支持Lucene 引擎,可能性能会有很大提高,暂时没作研究,也没作性能对比,后续有空在研究。
- 其余复杂检索,例如非前缀模糊匹配查询
例如查询db.member.find({"name":{ $regex:/XXX/ }}),查询name字段包含XXX的查询,这类查询Es更优,由于mongodb、mysql等底层都是KV存储,查找KEY的时候都是从左到右比较key字符串,若是是非前缀匹配模糊查询,就须要全表扫描。
查询以某字段为开头的文档,db.member.find({"name":{$regex:/^XXX/}})这类就比较适合用mongodb查询,前缀匹配。
mongodb和Es不一样场景性能对比(如下为真实线上数据对比):
最后,脱离业务场景评估一个数据库优劣很不合适,主流数据库都优其存在的意义,不能由于数据库在某种场景下不合适而全盘否认该数据库。
8. 对《从MongoDB迁移到ES后,咱们减小了80%的服务器》一文的不一样见解
《从MongoDB迁移到ES后,咱们减小了80%的服务器》一文中如下观点我的认不太赞同,主要以下:
8.1 文章内容的不一样见解
mongodb近几年持续排名全球前五,市值近两年已翻数倍,当前市值近200亿左右。DB-Engines Ranking排名得分持续提高,说明自己有本身得市场和应用场景,而不是浪得虚名,没《从MongoDB迁移到ES后,咱们减小了80%的服务器》一文中说的不堪一击。
- 服务器80%节省?mongodb部署架构严重资源浪费
从该文章能够看出,单个文档200多字节,mongodb存储引擎wiredtiger默认高压缩、高性能、细粒度锁。单个复制集便可存储数十亿数据,大家用了十多个容器。
一样的数据,默认mongodb磁盘占用是Es的六分之一,加上这是日志集群,mongodb能够采用1mongod+1mongod+1arbiter部署,规格8c/32gb/100gb 2个容器就能够知足要求。这样的部署才是合理的,成本会比一样数据Es减小数倍。若是用mongodb,自己2个8c/32gb/100gb+1个低规格选举节点容器便可搞定的事,你用了15台。
- 任意组合的,现有MongoDB是不支持的?
任意组合的查询不是mongodb不支持,是建索引麻烦,包括mysql、tidb等数据库都是须要手动一个一个建索引。
- 性能提升十倍?一个走索引一个不走索引,这种数据对比不客观
这自己就是个数据库选型问题,随机组合条件太多,索引很差建,你应该把索引查询条件对应索引建好后对比。不过多字段的随机组合查询,确实不适合用mongodb,建索引麻烦。
建议把mongodb对应查询索引建好,从新测试下查询性能数据。
- MongoDB单集合数据量超过10亿条,此状况下即便简单条件查询性能也不理想?
我司最大的mongodb集群单表几千亿数据,查询2ms之内。
10亿规模对咱们线上业务就是毛毛雨,咱们把mongodb集群归类为以下几档:
- 小规模集群:数据量<100亿
- 中型集群:数据量100亿到1000亿之间
- 大型集群:数据量大于1000亿
当前我司15%-20%左右集群是百亿级以上集群,已成功实现单个集群万亿级离线数据读写存储,当前正在挑战单个万亿级实时在线数据高并发在线读写。
万亿级离线数据读写优化案例详见Qcon、dbaplus、mongodb社区等分享:用最少人力玩转万亿级数据,我用的就是MongoDB!
后续将分享《单集群万亿级在线数据高并发读写优化实践》
- 没有人敢在核心项目中使用MongoDB?
我司25%-35%以上数据存储文件、图片等元数据,甚至包括少许交易集群,很是核心,当前我司规模早已超过万亿级。
8.2 文章如下回复的不一样见解
- mongodb有的,Es都有?
没有万能的数据库,正如上文所述,mongodb在高并发写、固定字段索引查询方面的性能表现。此外mongodb存储引擎高压缩高性能,在成本上面体现很明显。
业务场景很重要,不能“捧一个,踩一个”,主流数据库都有其存在的理由,排名第五毫不是浪得虚名。
- 只有用事务的才是核心数据?
不太同意这样定义核心数据,用事务的不必定是核心数据,不用事务的也未必不是核心数据。例如我司存储的几千亿文件、图片等元数据,没有用到事务,可是是很是核心的数据,丢失会形成严重后果。
8.3 业界对数据库几个错误认识(以OPPO接入业务过程真实案例分享)
结合公司内部使用mongodb为例,有时候出现如下状况:一些不适合mongodb的业务场景,例如全文检索、8字段以上的随机组合查询、非前缀匹配模糊查询,这些场景自己不适合选用mongodb,可是业务选型错误,形成使用过程当中的瓶颈。
甚至有相关研发人员由于选型错误在各类群里面说:“远离mongo,珍爱生命”(该业务有全文检索需求);此外还存在业务数组索引使用不当引发集群抖动(该业务数组用法没建索引引发),认为mongodb设计有问题,这些都是极不负责的行为。
经过这些真实业务接触过程当中的案例,总结以下几点:
- 没有万能的数据库,切记结合自身业务场景,选择最优数据库;
- 主流数据库都有其存在的理由,不要由于在某些场景不适合而全盘否认该数据库其余层面的优点。例如不能由于mongodb在全文检索等复杂检索上面的弱势而全面否认mongodb,也不能由于Es在磁盘成本、高并发读写等方面的劣势而否认Es在复杂检索上面的优点。
- 切记以偏概全,捧一个踩一个,这都是及其不客观的行为。