用最少人力玩转万亿级数据,我用的就是MongoDB!

杨亚洲 2021-01-27 09:39:18

 

本文根据杨亚洲老师在〖deeplus直播第258期〗线上分享演讲内容整理而成。(文末有获取本期PPT&回放的方式,不要错过)

 

杨亚洲

OPPO MongoDB负责人

 

  • 负责OPPO数万亿级数据量文档数据库MongoDB内核研发、性能优化及运维工作,一直专注于分布式缓存、高性能服务端、数据库、中间件等相关研发。

 

本文主要分享内容如下:

 

  • MongoDB在OPPO互联网推广经验分享-如何把一个淘汰边缘的数据库逐步变为公司主流数据库;

  • 谈谈当前国内对MongoDB误解(丢数据、不安全、难维护)?

  • MongoDB跨机房多活方案-实现成本、性能、一致性"三丰收";

  • MongoDB线程模型瓶颈及其优化方法;

  • 并行迁移:MongoDB内核扩容迁移速率数倍/数十倍提升优化实践;

  • 百万级高并发读写/千亿级数据量MongoDB集群性能数倍提升优化实践;

  • 万亿级数据量MongoDB集群性能数十倍提升优化实践;

  • 磁盘800%节省-记某服务接口千亿级数据迁移MongoDB,近百台SSD服务器节省原理;

  • 展望:借助MongoDB完善的分布式、高可用、机房多活等功能,如何实现NoSQL、NewSQL融合;

  • 其他-那些年我们踩过的坑。

 

一、如何把MongoDB从淘汰边缘变为公司主流数据库?

 

 
背景

 

  • 入职前多个大数据量业务使用MongoDB,使用中经常超时抖动;

  • 多个核心业务忍受不了抖动的痛苦,准备迁移回MySQL;

  • MongoDB口碑差,新业务想用不敢用;

  • 入职1个月,业务迁移MongoDB到公司其他数据库,MongoDB总集群数减少15%。

 

 
我做了啥?

 

  • 从服务层优化、存储引擎优化、部署方式优化等方面入手,逐一解决抖业务抖动问题;

  • 总结各个集群抖动原因及优化方法,公司内部分享;

  • 收集公司所有MongoDB集群对应用户,成立MongoDB用户群。

 

 
入职2月后,MongoDB公司内部状态

 

  • 之前准备迁移到MySQL的几个核心业务继续使用MongoDB;

  • 对应业务负责人开始考虑把其他大数据量集群迁移到MongoDB;

  • 越来越多的未使用过MongoDB的部门开始使用MongoDB。

 

 
入职1年后,MongoDB相关数据增长

 

  • 总集群数增长比例:> 700%

  • 总数据量增长比例:> 2000%

  • 读写流量增长比例:> 550%

  • MongoDB用户群用户数增长比例:> 800%

 

 
总结

 

  • MongoDB赢得用户信任原因总结:口碑

 

二、当前国内对MongoDB误解(丢数据、不安全、难维护)?

 
 
业务接入过程中经常咨询的几个问题

 

  • 误解一:丢数据;

  • 误解二:不安全,网上一堆说MongoDB被黑客攻击,截图一堆新闻;

  • 误解三:DBA吐槽MongoDB太难维护。

 

 
误解原因

 

  • MongoDB本身很优秀,但是很多DBA和相应开发把控不住;

  • 国内系统性分析MongoDB内核实现原理相关资料欠缺;

  • 网络社会以讹传讹,DBA或者相关开发自身把控不住演变为MongoDB的锅。

 

三、MongoDB机房多活方案-实现成本、性能、一致性"三丰收"

 

 
社区MongoDB双向同步方案(放弃该方案)

 

 

放弃该方案原因:

 

  • 数据两份、集群两份、物理成本高。三机房、五机房等更多机房多活,成本及复杂性更高;

  • 存在一致性问题,两地集群数据不一致,balance情况下尤为突出;

  • 由于人力原因,如果开源同步工具出现问题把控不在。

 

 
方案一:同城三机房多活方案(1mongod+1mongod+1mongod方式)

 

 

  • 每个机房代理至少部署2个,保证业务访问代理高可用;

  • 如果某机房异常,并且该机房节点为主节点,借助MongoDB天然的高可用机制,其他机房2个mongod实例会自动选举一个新节点为主节点;

  • 客户端配置nearest就近访问,保证读走本机房节点;

  • 弊端:如果是异地机房,B机房和C机房写存在跨机房写场景如果A B C为同城机房,则没用该弊端,同城机房时延可以忽略。

 

 
方案二:同城两机房多活方案(2mongod+2mongod+1arbiter模式)

 

 

  • 每个机房代理至少部署2个,保证业务访问代理高可用;

  • 如果机房A挂掉,则会在B机房mongod中重新选举一个新的主节点。arbiter选举节点不消耗资源;

  • 客户端配置nearest参数,保证读走本机房节点;

  • 弊端:如果是异地机房,B机房和C机房写存在跨机房写场景。如果A B 为同城机房,则没用该弊端,同城机房时延可以忽略。

 

 
方案三:异地三机房多活方案(1mongod+1mongod+1mongod方式)-解决跨机房写

 

 

  • 每个机房代理通过打标签的方式,代理转发数据到主节点在本机房的分片上去;

  • A机房数据通过标签识别转发到分片shard-1,B机房数据转发到分片shard-2,C机房数据转发到分片shard-3。

 

四、MongoDB线程模型瓶颈及其优化方法

 

 
MongoDB默认线程模型(一个链接一个线程)

 

 

说明:

 

  • listener线程负责接受所有的客户端链接;

  • listener线程每接收到一个新的客户端链接就创建一个线程,该线程只负责处理该链接请求处理。

 

该网络线程模型缺陷:

 

  • 一个链接创建一个线程,如果10万个链接,那么就需要10万个线程,系统负责、内存消耗也会很多;

  • 当链接关闭的时候,线程销毁,频繁的线程创建和消耗进一步增加系统负载。

 

典型案例:

 

  • mysql默认方式、MongoDB同步线程模型配置,适用于请求处理比较耗时的场景,如数据库服务;

 

 
MongoDB默认线程模型(动态线程模型:单队列方式)。

 

 

说明:

 

  • 该模型把一次请求转换为多个任务:MongoDB数据读操作(网络IO)、db层数据访问(磁盘IO);

  • 任务入队到全局队列,线程池中的线程从队列中获取任务执行;

  • 同一个请求访问被拆分为多个任务,大部分情况下通过递归调用同一个请求的多个任务会由同一个线程处理;

  • 当任务太多,系统压力大的时候,线程池中线程数动态增加;当任务减少,系统压力减少的时候,线程池中线程数动态减少。

 

该网络线程模型缺陷:

 

  • 线程池获取任务执行,有全局锁竞争,这里就会成为系统瓶颈。

 

典型案例:

 

  • MongoDB动态adaptive线程模型,适用于请求处理比较耗时的场景,如数据库服务。

 

 
MongoDB优化后线程模型(动态线程模型-多队列方式)

 

 

说明:

  • 把一个全局队列拆分为多个队列,任务入队的时候按照session链接hash散列到各自的队列,工作线程获取获取任务的时候,同理通过同样的hash算法去对应的队列获取任务,通过这种方式减少锁竞争,同时提升整体性能。

 

典型案例:

 

  • MongoDB内核多队列adaptive线程模型优化,特定场景性能有很好的提升,适用于请求处理比较耗时的场景,如数据库服务。

 

五、并行迁移-集群扩容速率N倍提升优化实践

 

 
并行迁移-集群扩容速率N倍提升优化实践(高版本)

 

 

1)并行迁移过程(假设需要迁移的表名为:test, 从3节点扩容到6节点):

 

  • 选取需要迁移的块,假设源集群有M分片,扩容新增N分片,则一般情况需要迁移的块=min(M,N);

  • 迁移步骤:

1. configServer-master选出需要迁移的块;

2. config.locks表中id=test这条记录上锁;

3.通知需要迁移的源分片开始迁移;

4. 迁移完成后延时10s,重复1-4步骤实现持续性chunk数据迁移。

 

2)并行迁移步骤:

 

说明:假设需要迁移的表名为test, 源分片数M,扩容后新增分片数N

 

  • configServer-master选出需要迁移的块,一般S=min(M, N),也就是M和N中的最小值;

  • config.locks表中获取id=test这条记录对应的分布式锁;

  • 异步通知需要迁移的S个源分片开始迁移;

  • 等待S个chunk迁移完成;

  • 迁移完成后延时10秒;

  • 重复步骤1-5。

 

3)并行迁移瓶颈:

 

  • 获取分布式锁时间太长,原因:config.locks表中id=test表的分布式锁可能被其他操作锁住;

  • configServer异步通知源分片中的S个分片同时开始迁移数据到目的分片,任一个chunk迁移慢会拖累整个迁移过程;

  • 本批次确认迁移完成后,还需要延时10s;一般SSD服务器,一个chunk迁移都在几百ms内完成。

 

4)优化方法:

 

  • 避免其他操作占用分布式锁,例如splite我们可以关闭autoSplite功能,或者调大chunksize;

  • configServer并行迁移不把多个分片的并行迁移放到同一个逻辑,而是放到各自的逻辑;

  • 延时放到各自分片迁移逻辑里面控制,不受全局延时控制;

  • 分片延时可配置,支持实时动态命令行调整。

 

六、性能优化案例

 

 
案例1.千亿级数据量MongoDB集群性能数倍提升优化实践-背景

 

1)业务背景:

 

  • 核心元数据;

  • 数据量千亿级;

  • 前期写多读少,后期读多写少;

  • 高峰期读写流量百万级;

  • 时延敏感;

  • 数据增长快,不定期扩容;

  • 同城多活集群。

 

2)优化策略1:部署及使用方式优化

 

  • 预分片,写入负载均衡;

  • WriteConcern:{ w: "majority"},写大部分节点成功才返回客户端OK;

  • 读写分离,读从优先;

  • enableMajorityReadConcern关闭,有性能损耗。

 

 

3)优化策略2:存储引擎cache淘汰策略优化

 

wiredtiger存储引擎cache淘汰策略相关的几个配置如下:

 

 

wiredtiger存储引擎cache淘汰策略优化后配置: 

 

eviction_target: 75%,eviction_trigger:97%,eviction_dirty_target: %3,eviction_dirty_trigger:25%,evict.threads_min:4,evict.threads_max:16

 

总体思想:evict线程尽早淘汰脏页page到磁盘,增加evict淘汰线程数加快脏数据淘汰,避免用户请求线程进行脏数据淘汰。

 

4)优化策略3:存储引擎checkpoint优化

 

存储引擎checkpoint检测点,把当前存储引擎脏数据全部记录到磁盘。触发条件如下:

 

  • 固定周期做一次checkpoint快照,默认60s;

  • 增量journal日志达到2G。

 

少部分实例存在如下现象:一会儿磁盘IO几乎空闲0%,一会儿磁盘IO短暂性100%。进行如下优化后可以缓解该问题:

 

checkpoint=(wait=30,log_size=1GB)

 

该优化总体思路:缩短checkpoint周期,减少checkpoint期间积压的脏数据,缓解磁盘IO高问题。

 

遗留问题:SSD盘只有极少数节点有该问题,原因未知,后续继续跟踪。

 

 

瓶颈点:

 

  • 代理缓存所有客户端的链接信息到内存中,并定期更新到config库的system.sessions表中;

  • 大流量大数据量集群客户端链接众多,大量更新sessions表,最终主分片性能下降引起整个集群性能瞬间数倍下降。

 

 

优化方法:

 

  • config库的system.sessions表启用分片功能;

  • mongos定期更新优化为散列到不同时间点进行更新。

 

5)优化策略4:sharding集群system.session优化

 

该优化总体思路:

 

  • 之前代理集中式更新单个分片,优化为散列到不同时间点更新多个分片;

  • 该优化后system.sessions表更新引起的瞬间性能数倍降低和大量慢日志问题得到了解决。

 

6)优化策略5:tcmalloc内存优化

 

  • db.serverStatus().tcmalloc监控发现部分mongod实例pageheap、内存碎片等消耗过高。通过系统调用分析得出:内存碎片率、pageheap过高,会引起分配内存过程变慢,引起集群性能严重下降。

 

该优化总体思路:

 

借助gperftools三方库中tcmalloc内存管理模块,实时动态调整tcmalloc内存Release Rate,尽早释放内存,避免存储引擎获取cache过程阻塞变慢。

 

 
案例2.万亿级数据量MongoDB集群性能数倍提升优化实践

 

1)业务背景:

 

  • 集群存储离线数据;

  • 集群总数据量万亿级;

  • 前期主要为数据写入,要求万亿级数据几周内尽快全部写入集群;

  • 后期主要是读流量,单次查询数据条数比较多,要求快速返回;

  • 每隔一定时间周期(周为单位)会有持续性大量写入。

 

2)优化策略1:基础性优化

 

分享主题六中读写分离、预分片、wiredtiger存储引擎优化、session优化、tcmalloc使用优化等基础性优化策略同样适用于该集群,具体详见《分享主题六:百万级高并发读写/千亿级数据量MongoDB集群性能数倍提升优化实践》

 

3)优化策略2:

 

存储模型优化前状况

 

优化前数据模型结构如下:

 

 

1.{  

2.    "_id": ObjectId("5fh2ebd18856960dbac31abc"),  

3.    "characteristic": "xxxx",  

4.    "key1": "***",  

5.    ......  

6.    "keyn": "***",  

7.}  

 

  • 以上为单条数据的数据模型,该集群总数据量万亿级;

  • 数十万条数据拥有同样的characteristic特性,总特性数总计数百万个;

  • 一次性查询数十个characteristic很慢。

 

瓶颈点:一次性查询数十个characteristic特征条件的数据,每个特征拥有数百万数据,一次查询总计千万行数据。由于数据量很大,每行数据几乎全在磁盘,一次查询需要千万次IO操作,查询成为瓶颈。  

 

 

第一轮数据存储模型优化:

 

 1.{  

2.    "_id": ObjectId("5f29ebd18856960dbac31abc"),  

3.    "characteristic": "xxxx"  

4.    "group": [  

5.           {  

6.            "key1": "***"  

7.            ......  

8.            "keyn": "***"  

9.        },   #该characteristic下第一条数据  

10.            ......  

11.        {  

12.            "key1": "***"  

13.            ......  

14.            "keyn": "***"  

15.        } #该characteristic下第n条数据  

16.    ]  

17.} 

 

该数据模型把相同characteristic特性的数十万数据合并到为一条数据,减少磁盘IO操作,整个读性能会有近百倍提升。

 

瓶颈点:该轮优化解决了读瓶颈,却引入了新的写瓶颈。

 

  • 通过$addToSet方式向group数组中去重追加数据,数据长度越来越长,磁盘IO压力越来越大、写性能成为新的瓶颈。

 

 

第二轮数据存储模型优化:

 

1.{  

2.    "_id": ObjectId("5f29ebd18856960dbac31abc"),  

3.    "characteristic": "xxxx"  

4.    "group": [  

5.           {  

6.            "key1": "***"  

7.            ......  

8.            "keyn": "***"  

9.        },   #该characteristic下第一条数据  

10.            ......  

11.        {  

12.            "key1": "***"  

13.            ......  

14.            "keyn": "***"  

15.        } #该characteristic下第n条数据  

16.    ]  

17.} 

 

 

1.{  

2.    "_id": ObjectId("5f29ebd18856960dbac31abc"),  

3.    "characteristic": "xxxx",  

4.   "hashNum": num,     

5.    "group": [  

6.           {  

7.            "key1": "***",  

8.            ......  

9.            "keyn": "***",  

10.        },   #该characteristic下第一条数据  

11.            ......  

12.        {  

13.            "key1": "***",  

14.            ......  

15.            "keyn": "***",  

16.        } #该characteristic下第n条数据  

17.    ]  

18.}  

 

如上,把同一个characteristic特征的数十万/数百万数据散列为500份,这样合并后group数组中也就只包含数百条数据信息,这样合并后单条数据过大、MongoDB单条数据64M限制问题、磁盘IO过高等瓶颈问题都可以得到解决。

 

总体数据模型优化思路:通过合理的数据合并操作来减少网络IO、磁盘IO、MongoDB内核处理时间,最终使读和写达到平衡。

 

七、成本节省-记某服务千亿级数据迁移MongoDB,百台SSD服务器节省优化实践

 

 
成本节省-记某服务千亿级数据迁移MongoDB,百台SSD服务器节省优化实践

 

1)迁移背景:

 

  • 需要迁移的数据量数千亿级;

  • 源集群磁盘紧张,业务写入快,需要快速完成数据迁移;

  • 源集群数据存储于高io ssd服务器;

  • 业务对性能没太高要求;

  • 目的MongoDB集群采用低io 大容量sata盘。

 

2)迁移难点:如何快速完成数据迁移?

 

 

3)瓶颈点:

 

  • 由于目的集群为低io大容量sata盘,迁移太慢,源集群磁盘有写满风险。

 

 

4)优化策略:

 

  • 同步数据到大容量SSD中转集群;

  • 拷贝中转集群数据到目标大容量SATA盘服务器;

  • 加载数据。

 

5)成本节省:

 

  • MongoDB默认的snappy压缩算法压缩比约为2.2-3.5倍;

  • zlib压缩算法压缩比约为4.5-7.5倍(本次迁移采用zlib高压缩算法)。

 

6)千亿级数据迁移MongoDB收益:

 

  • 源集群磁盘消耗:目的集群磁盘消耗 = 8:1(即使目的mongo集群也用SSD服务器,成本也可以节省七倍);

  • 源集群物理资源:百台SSD服务器;

  • 目的MongoDB集群资源消耗:6台SATA盘服务器。

 

八、展望-如何实现MongoDB与SQL融合

 

 
问题背景

 

随着MongoDB-4.2版本中对分布式事务的支持,以及MongoDB-4.4版本产品规划路线图可以看出,MongoDB除了保持nosql特性外,还在朝着newSql方向前行。但是在实际业务接入中发现以下现象:

 

  • 开发习惯了SQL,转MongoDB语法各种不习惯;

  • 运营和数据分析岗位人员只会写SQL,不会mongo语句。

 

 
我们能做什么?

 

  • mongos代理增加MongoDB协议和SQL转换支持,用最小开发成本满足业务SQL需求;

  • 5%-10%左右的SQL协议支持,满足90%的用户需求。

 

九、其他-那些年我们踩过的坑

 

实际业务接入MongoDB数据库过程中,我们踩过很多坑,包括业务不合理使用、不合理运维、集群不合理配置、MongoDB内核踩坑、误操作等,甚至出现过同一个核心业务几次抖动。

 

本次分享中集群优化只列举了主要的优化过程,实际优化过程比本次分享内容更加复杂,集群更多优化细节及数十例典型踩坑过程将在今后逐步分享出来。

 

踩坑不可怕,在踩坑过程中学习,学习过程中减少踩坑。

 

十、2021规划

 

国内真正拥有企业级分布式数据库自研能力的公司主要集中在阿里、腾讯头部几家,即使二三线互联网公司也无法做到真正意义上的企业级分布式数据库研发能力,拥抱开源是一个明智的选择。

 

MongoDB拥有天然的高可用、分布式扩缩容、机房多活容灾、完善的负载均衡及一致性策略等功能,可以做到最少人力成本满足业务快速增长的需求,个人认为MongoDB绝对是国内互联网企业对分布式数据库需求的一个值得信赖的选择。 

 

>>>>

Q&A

 

Q1:性能优化有推荐的分析和监控工具么?

 

A1:《MongoDB》常用性能分析主要如下:

 

1.1 MongoDB自带性能分析工具

 

1)MongoDB官方对外工具mongostat

 

命令行使用方法(ip:port为代理ip和端口):

mongostat  -h  ip:port  -u用户名 -p密码 --authenticationDatabase=admin --discover

 

 

mongostat工具带上--discover,可以把所有分片节点信息一起打印出来,直观查看整个集群所有节点实例级监控信息。mongostat统计信息中最核心的几个影响性能的统计项:

 

  • dirty:存储引擎脏数据比例,默认该值为5%的时候,wiredtiger存储引擎自带的evict现成开始选择脏数据page淘汰到磁盘;如果该值达到20%,客户端请求对应MongoDB处理现成将会选择脏数据page淘汰到磁盘,等page淘汰腾出内存空间后,才会处理客户端请求的DB访问,所以如果阀值达到20%客户端访问将会变慢;

  • used:存储引擎cacheSize配置占用百分比,如果配置cacheSize=10G,存储引擎实际使用了7G,则used赞比为70%。当该统计值达到80%,evict线程将会触发选择涨数据淘汰,如果这个占比提高到95%,用户请求线程将会触发淘汰,客户端请求将会变慢;

  • qrw arw:等待队列数,如果该值越大,说明会引起客户端请求排队处理。一般该值会再dirty占比超过20%,used占比过高超过95%,或者磁盘IO慢会出现;

  • vsize res:虚拟内存和物理内存真实占用,如果vsize过高,远远超过res,或者res过高,远远超过cachesize配置,则说明内存碎片,pageheap等问题,这时候可以通过加速tcmalloc内存释放速率来解决问题。

 

2)慢日志分析

 

通过以下命令分析日志文件

 

  • 找出文件末尾1000000行中存在扫表的操作,不包含oplog,getMore

 
 

tail  mongod.log  -n 1000000 | grep ms |grep COLLSCAN |grep -v "getMore" | grep -v "oplog.rs"

 

  • 找出文件末尾1000000行中所有的慢日志,不包含oplog,getMore

 

 

tail mongodb.log -n 1000000 |grep ms | grep op_msg | grep find | grep -v "oplog.rs" |grep -v "getMore" 

 

  • 找出文件末尾1000000行中执行时间1-10s的请求,不包含oplog,getMore

 
 

tail mongodb.log -n 1000000 |grep ms | grep op_msg | grep find | grep -v "oplog.rs" |grep -v "getMore" | egrep [1-9][0-9][0-9][0-9]ms

 

3)currentOp正在执行的慢操作分析

 

慢日志只有当请求执行完毕才会,如果一个表很大,一个查询扫表,则整个执行过程可能需要数小时,可能还没记录慢日志,则可以通过如下命令获取当前执行时间超过5s的所有请求,查询请求,command请求:

 

db.currentOp({"secs_running":{"$gt":5}})

db.currentOp({"secs_running":{"$gt":1}, "op":"query"})

db.currentOp({"secs_running":{"$gt":5}, "op":"command"})

 

kill查询时间超过5s的所有请求:

 

 

db.currentOp().inprog.forEach(function(item){if(item.secs_running > 5 )db.killOp(item.opid)})

 

4)节点存储引擎监控信息

 

db.serverStatus().wiredTiger可以获取mongod节点对应存储引擎的各自详细统计信息,里面可以完整获取时延消耗在存储引擎哪一个环节。

 

下面是空余时间分析的wiredtiger源码,分析不是很完整,后续等mongodb server层单机、复制集、分片等完整模块化分析后,会回头继续分析。

 

wiredtiger存储引擎源码详细注释分析

https://github.com/y123456yz/reading-and-annotate-wiredtiger-3.0.0

 

1.2 操作系统性能瓶颈分析

 

系统层面性能分析工具主要有:top、iostat、pstak、ptress、perf、iotop、isof等,具体请参考对应工具说明。

 

1.3 开源MongoDB详细监控套记

 

开源方案可以参考以下组件: 

Grafana+Prometheus+node_exporter+mongodb_exporter

 

  • 服务端组件:

Prometheus #服务端

Grafana #前端展示

 

  • 客户端组件:

node_exporter

mongodb_exporter

 

Q2:会话加标签是怎么指定服务器?

 

A2:举一个例子形象说明:我们把用户分为三组,20 岁以下(junior),20 到 40 岁(middle)和 40 岁以上(senior),按照下面的几条命令执行以后,我们的数据会按照用户年龄段拆分成若干个 chunk,并分发到不同的 shard cluster 中。

 

如果对下面的命令不熟悉,可以查看 MongoDB 官方文档关于 Shard Zone/Chunk 的解释。

 

sh.addShardTag('shard01', 'junior')

sh.addShardTag('shard02', 'middle')

sh.addShardTag('shard03', 'senior')

sh.addTagRange('test.users', {'user.age': MinKey}, {'user.age':20}, 'junior')

sh.addTagRange('test.users', {'user.age': 21}, {'user.age':40}, 'middle') 

sh.addTagRange('test.users', {'user.age': 41}, {'user.age': MaxKey}, 'senior')

 

通过上面的6个命令给'test库的user表加标签,20以下对应标签为'junior',21-40对应标签为'middle',41以上对应标签为'senior'。同时把'junior'标签分配给'shard01',也就是0-20岁的user会全部写到'shard01',21-40岁的user会全部写到'shard01',41岁以上的user会全部写到'shard01'。

 

这样就可以解决跨机房写的问题,只要对应分片主节点在对应机房即可。

 

Q3:脏数据比例多少算高?

 

A3:默认20%算高,如果脏数据比例持续性超过20%,可以试着提高wiredtiger存储引擎后台淘汰线程数:

 

 

db.adminCommand( { setParameter : 1, "wiredTigerEngineRuntimeConfig" : "cache_size=35GB, eviction=(threads_min=4, threads_max=12)"})

 

Q4:写分开,会有时延吗,是不是有一致性问题?

 

A4:一致性默认完全由MongoDB复制集自带的主从同步机制来保证最终一致性,不存在双向同步两集群的一致性问题。

 

如果要实现复制集中主从节点的强一致性,可以通过客户端配置writeconcern策略来解决。

 

Q5:比如想定位详细的慢查询呢?

  

A5:和问题1雷同,可以通过分析currentop、日志文件或者system.profile慢日志表来获取详细的慢日志信息。建议平台化收集慢日志,这样界面展示分析更加直观。

 

Q6:如何快速定位Mongodb的问题发生在集群中的哪些节点?  在启用读写分离的情况下?

 

A6:我们用mysql_fdw进行迁移,这也是很方便的一种方式。关于迁移的注意事项,除了数据类型是适配,需要注意PG一定要使用UTF8编码。另外PG不支持\0000这个特殊的Unicode字符。当然这个字符也没是什么实际意义,主要是有一些应用系统数据入库不规范,在以前的DB2或MySQL库里不小心写入的这个字符,后面迁移到PG的时候就需要规范化,把这个非法字符删掉。  

 

主要通过如下几个步骤来分析:

 

  • db.serverStatus().opLatencies监控mongod实例时延 ;

  • 如果由运维研发能力,可以自己收集时延展示,如果没有。则可以借助开源工具系统实现,参考《1.3 开源MongoDB详细监控套记》;

  • 充分利用mongostat监控集群所有节点实时脏数据、队列、内存信息;

  • 参考《1.1 MongoDB自带性能分析工具》;

  • 慢日志分析。

 

参考问题5的回答。

 

Q7:杨老师,就您经验来讲,您觉得如何保证MongoDB的安全性呢?

 

A7:安全性方面主要由以下几方面保证:

 

  • 账号鉴权认证,一个库一个账号;

  • readWrite权限去除删库、删表等危险操作权限;

  • 不同业务不混用同一个集群;

  • 启用黑白名单功能;

  • 我司MongoDB内核增加审计、流量控制、危险操作控制等功能(注:部分功能是MongoDB企业级功能,需要付费,可以使用percona mongodb版本);

  • 数据定期备份,我司MongoDB内核增加有热备功能。

 

注意:如果数据量很大,建议不要使用mongodump备份,mongodump备份会很慢,同时通过mongorestore恢复也是一条数据一条数据恢复,同样很慢。如果有内核研发能力,可以增加热备功能。如果没有内核研发能力,可以通过如下步骤备份:1. 隐藏节点;2. 锁库;3. 拷贝数据文件。或者采用percona mongodb版本来备份。

   

Q8:MySQL和MongoDB双写的话怎么保证事务呢?

 

A8:MySQL我不是很了解,MongoDB不推荐搭两集群双向同步来备份,直接利用MongoDB原生的复制集功能来完成多活容灾,成本、性能、一致性都可以得到保证。即使是4.2分布式事务功能也可以直接利用MongoDB自身的机制来保证,具体方案参考我在Qcon全球软件开发大会的分享:

 

万亿级数据库MongoDB集群性能优化及机房多活容灾实践

https://zhuanlan.zhihu.com/p/343524817

 

Q9:hashnum 的方式来讲数组中的方式来拆分成多个表?没太明白 。

 

A9:分享的案例2:万亿级数据量MongoDB集群性能数倍提升优化实践https://zhuanlan.zhihu.com/p/343524817,不是拆分数据到多个表,而是把一条数据(该数据保护一个数组,数组中包含数百万个子文档)通过hash的方式散列为多条数据。也就是之前数百万个子文档归属于一条数据,现在把他拆分为归属到多条数据。

 

通过这样合理的数据合并和拆分,最终平衡磁盘IO,实现读和写达到一种平衡态,既能满足业务读需求,同时也能满足业务写需求。

 

Q10:对分片键设计要求高吗?

 

A10:分片集群片建选择非常重要,对分片模式集群性能起着核心至关重要的作用,分片集群片建选择遵循以下几个原则:

 

1)首先需要考虑集群部署是否需要分片?

 

只有以下情况才需要分片功能:1. 数据量太大,一个分片撑不住;2. 写流量太大,写只能走主节点,一个主节点撑不住,需要扩分片分担写流量。

 

2)片建选择原则?

 

片建选择原则如下: 

 

  • 保证数据尽量离散;

  • 尽量保证更新和查询到同一个分片(如果同一次更新或者查询到多个分片,只要任何一个分片慢,该操作都会慢;同时部分查询会进一步加剧代理聚合负担)。

 

此外,如果查询注意是范围查询,建议选择范围分片,这样有利于范围数据集中到同一个分片。

 

Q11:大表分片后,写表还是会跨机房吗?

 

A11:机房多活打标签方式解决跨机房写问题,同样可以对对应tag表启用分片功能,保证数据到指定的多个分片,每个分片主节点在指定机房,可以解决跨机房问题。详情参考:《会话加标签是怎么指定服务器?》

 

Q12:老师您好,想请问下MongoDB适合做商城app数据库吗?一般在哪些场景使用呢?谢谢!

 

A12:个人觉得完全可以满足要求,同时还有利于业务的快速迭代开发。MongoDB天然的模式自由(加字段方便)、高可用、分布式扩缩容、机房多活容灾机制,可以快速推进业务迭代开发。以我的经验,至少90%以上使用mysql的场景,MongoDB同样可以满足要求。MongoDB唯一缺点可能是生态没mysql健全,研究MongoDB的人相当少。

 

Q13:老师能讲讲你们容量预警是怎么做的吗?

 

A13:容量水位我们分为以下几种:

 

1)磁盘容量限制

 

当一个分片中磁盘使用率超过80%,我们开始扩容增加分片。

 

2)流量超过阀值

 

读写流量阀值水位如下:1. 如果是分片的写流量持续性超过3.5W/s(ssd服务器)则扩容分片;2. 如果是读流量单节点持续性超过4W/s(ssd服务器,所有读走磁盘IO),则扩容从节点来解决读流量瓶颈,注意需要配置读写分离。

 

3)CPU阀值

 

我们所有实例容器部署,实例如果CPU使用率持续性超过80%,考虑增加容器CPU。

 

Q14:数据一致性在迁移过程中同步你们是怎么保证的呢?

 

A14:如果通过mongoshake等工具迁移集群,需要提前关闭blance功能,否则无法解决一致性问题。

 

我们线上集群只有把数据从集群迁移到另一个集群的时候才会使用mongoshake,我们机房多活不是多个集群双写方式,而是同一个集群,通过夫直接的主从同步拉取oplog机制实现一致性,所以不存在一致性问题。可以参考《万亿级数据库MongoDB集群性能优化及机房多活容灾实践》

 

Q15:我们数据体量不太大,主要是杂,这种环境想做好数据治理,老师你建议把重点放在哪些方面?然后有没有一些比较常见的坑?

 

A15:数据量不大,比较杂的场景,一般集群搞一个复制集即可满足要求,无需分片模式部署。

 

我猜测你们的比较杂可能是利用MongoDB的模式自由,造成每条数据的字段各不相同,数据长度大小各不一致。建议在使用模式自由这一功能的时候,一定不要”滥用”、”乱用”,在使用时代码逻辑需要简单控制。我重节线上遇到的对模式自由的”滥用”、”乱用”引起的集群问题:

 

  • 同一个表数据字段各不相同,建议同一个表所有数据的字段保持一致,即使新数据增加字段也需要在老数据中增加该字段,保持字段一致;

  • 同一个表的数据的字段控制在50个KV以内,这样对应更新、查询等性能分析有利,减少磁盘IO消耗;

  • 如果数据字段过多,查询的时候不要返回所有字段,只获取对本次查询有用的字段,减少忘了IO开销;

  • 数组别乱用,数组中的文档保持格式统一;

  • 数组中的子文档如果需要查询指定字段,一定记得对数组中嵌套的字段添加子索引;

  • 数组字段中的文档一定要控制在一定范围,避免该数组过大,数组过大有遍历、磁盘IO过高等问题;

  • 嵌套子文档层数不宜过多;

  • .......

 

Q16:现在有多大数据量?

 

A16:公司内部MongoDB规模已经很大了,总体超过万亿级,具体数据不太方便透露。

 

Q17:你们对这个大数据平台有多少开发人员?

 

A17:我们研发+运维人员很少,MongoDB拥有天然的高可用、分布式扩缩容、机房多活容灾等功能,保证了可以用很少的人力来满足公司快速增长的业务需求。

↓点这里可回看本期直播

阅读原文

活动预告