——持续更新中
1. 背景说明
目前因业务量激增,系统已将订单存储从原有的mysql迁至新的mongo集群,在试运营过程中,主要遇到以下问题:
- 单机磁盘资源瓶颈,目前单表已经占用20g
- 单机读写能力瓶颈,读能力可以在复制集里加 secondary 节点来扩展,但CPU、内存或者网卡等资源遭遇瓶颈,导致读写能力无法扩展
我们目前已经是3主3从的mongocluster,因上线时间较短,订单表目前仅为副本集模式,还没来得及sharding模式,考虑年底500K/日的下单指标,推进订单的sharding操作刻不容缓。
2. 优化目标
sharding后对系统对读性能将有非常大的影响,这种场景和传统数据的sharding是相同的。目前我们主要以场景为维度来确定此次优化的预期目标:
- c端用户查询我的订单列表:rtt<=100ms,tps>=2000
- b端用户查询商户订单列表:rtt<=500ms,tps>=500
- 其它条件范围查询:rtt<=500ms,tps>=1000
- 其它条件单条查询:rtt<=100ms,tps>=2000
3. sharding方案
3.1 分片说明
mongo主要支持2种数据分布的策略,范围分片(Range based sharding)和hash分片(Hash based sharding)。
- 查询请求
- 查询请求不包含shard key,则必须将查询分发到所有的shard,然后合并查询结果返回给客户端
- 查询请求包含shard key,则直接根据shard key计算出需要查询的chunk,向对应的shard发送查询请求
- 写请求
- 写操作必须包含shard key,mongos根据shard key算出文档应该存储到哪个chunk,然后将写请求发送到chunk所在的shard。
- 更新/删除请求
- 必须包含shard key或者_id
- 包含shard key,则直接路由到指定的chunk
- 只包含_id,则需将请求发送至所有的shard
- 其它命令请求
- 除增删改查外的其他命令请求处理方式都不尽相同,有各自的处理逻辑,比如listDatabases命令,会向每个Shard及Config Server转发listDatabases请求,然后将结果进行合并。
3.2 方案对比
在两种分片的基础上我们讨论以下几个方案的优劣:
- 按时间范围分片
- 数据将按时间段连续的写入同一个shard上
- 大跨度时间查询时,无论c端维度或b端维度均要全分片扫描 X
- 单记录查询需要指定时间+订单id X
- 实施成本:低
特点:适用于连续日志类型
- 按地域hash分片
- 同一个地域的订单会落在同一个shard上
- 同一商户只在同一地域方便查询
- 同一商户在不同地域全shard扫描
- 同一用户在不同地域充电的场景下需要全shard扫描
- 实施成本:低
特点:适用于地域独立性强,不同地域无交集场景
- 按商户hash分片
- 同一个商户的订单会落在同一个shard上
- b端商户性能:优
- c端客户查询:弱,客户深度分页时可能崩溃
- 实施成本:低
- 按客户hash分片
- 同一个用户的订单会落在同一个shard上
- b端商户性能:弱,商户深度分页时可能崩溃
- c端客户查询:优
- 实施成本:低
- 商户和客户冗余分片
- 此方案下需要同时准备两张相同的订单表,一张商户hash,另一张客户hash
- 以商户维度查询时查商户订单表
- 以客户维度查询时查客户订单表
- 性能问题:优
- 实施成本:极高
3.3 方案确认
目前mongo的主要场景即为客户查询和商户查询,
下方估计场景:
- 客户查询频率约:90%,商户查询频率10%
- 客户订单查询时长:约3个月,商户订单汇总时长:约1年
- 客户订单3月数量:200记录/月,商户订单1年,数十万级别
方案 | 商户查询性能 | 客户查询性能 | 查询频率 | 写入性能 | 综合评估 |
商户hash | 高 | 深度分页时级低,但客户数据量少,出现深度分页的机率非常低 | 非常高 | 影响小 | 待定 |
客户hash | 深度分页时极低 | 极高 | 较低 | 影响小 | 待定 |
目前能确定的两个焦点:
#考虑到的简单的性能影响计算规则:
shard_count*0.9+order_count/10*0.1
#商户分片时,
#c端范围查询的劣势:全shard扫描,并且频率非常高,约90%
#性能影响度:因单个客户的订单列表量较小,不会引起深度分页
#计算:
3*0.9+200=200+
#客户分片时,
#b端范围查询的劣势:全shard扫描,但频繁低,约10%
#性能影响度:单个商户订单列表量极大,会引起深度分页
#计算:
3*0.1+100000/10*0.9=10000000+
4. 优化说明
在满足业务要求的前提下,灵活根据分片原理、索引以最达到大性价比
- 分片范围查询时尽量加上shardkey,若以B端hash,此时可以加商户id,但以c端而不能加
- 查询记录查询时尽量加shardkey,若不加则会全shard扫描,在一个shard找到即返回
- 在数据深分页时,无论分不分片,页数较深时使用skip方式仍然影响性能。此时需要通过>lastelement方式实现
- 在分片场景下,深分页压力较大时,应该在缩小范围的前提下再进行分页操作。如商户订单上百万,分页的话查询后面的页数系统压力非常大,可以先通过把时间段缩小,这样订单量缩小至10万级别,页数没那么多,此时问题即从产品方面解决了