BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

从eBay购物车丢失看处理网络I/O

| 作者 麦克周 关注 22 他的粉丝 发布于 2017年7月21日. 估计阅读时间: 9 分钟 | ArchSummit北京2018 共同探讨机器学习、信息安全、微服务治理的关键点

eBay的购物车信息存储依赖于两个不同的数据存储介质,MongoDB存储用户完整的购物车信息,Oracle仅存储购物车的大致信息,但是可以通过关键信息查找所有的购物车信息。在eBay的这套系统里,MongoDB更多被用来充当“缓存”,Oracle数据库作为存储副本。如果数据在MongoDB里面找不到了,服务会从Oracle里面重新抽取(恢复)数据,然后重新计算用户的购物车。

所有的购物车数据都是JSON格式的,JSON数据在Oracle里被存储在BLOB格式的字段里。这些Oracle里面的数据只能被用于OLTP交易。

这篇文章并不是讨论数据库技术的选择(Oracle vs MongoDB,或者其他数据),而是希望能够让大家在巨量访问系统(每天上百万次调用)中找到技术债,理解如何解决问题。

问题描述

2016年秋天开始,购物车服务出现了缓存层丢失数据的情况,同时,运维团队报告MongoDB的备份机制多次出现失败(MongoDB运行在主从模式)。eBay的这个服务已经运行了5年时间,一直没有出现问题,没有做过任何架构调整和大规模代码改变,需要尽快找到原因和防治办法。针对实际问题进行反复检查,发现MongoDB的oplog(实时性要求极高的写日志记录)正在达到网络I/O限制。每一次的数据丢失,都会触发保护措施(再次从Oracle读取数据后重复计算),并进一步加长用户的等待时间。

解决方案

在我们具体讨论特定的解决方案前,我们希望去尽可能多地讨论解决方案。例如,一旦备份机制没有启用,是否可以通过隐藏一些副本方式让系统能够正常运行,而不要在系统特别繁忙的时候去尝试重新备份。我们可以尝试超时机制和阶段性副本方式,但是这些方式并不会引起我们本文说的问题发生。

方案一:切片(MongoDB)

团队成员提出对JSON数据进行切分,即对原先存储在MongoDB里的原子化的购物车信息(一个JSON字符串),切分为多个字符串,这样做的好处是可以减少单一MongoDB中心节点的写入次数和网络开销。

对于数据切分后的关联方式,远比数据切分、负载均衡复杂,因此,第1种方案的选择会引入其他技术难点,需要我们自己能够寻找被切分后的数据的关联性,这就是为什么eBay放弃了这个方案。

方案二:有选择的写入

使用MongoDB的set命令,只针对当特定值发生更改后,才启动写入操作。这种方式理论上也是可行的。

但是如果你真正考虑一下,这种做法没有从根本上确保减少oplogs写入次数,但是它很有可能会造成整个文档的更新。

了解一下MongoDB的Set操作模式。Set操作可以用于使用特定值替换字段值:

{$Set{:,…}}

假如你考虑一下描述产品的文档如下所示:


_id:100, sku:”abc123”, quantity:250, instck:true, reorder:false, details:{model:”14Q2”,make:”xyz”}, tags:[“appeal”,”clothing”], 
ratings:[{by:”ijk”,rating:4}] }

对于满足_id等于100的文档,执行set操作更新quantity字段、details字段和tags字段的值。

db.products.update( {_id:100}, {$set: 

quantity:500, 
details:{model:”14Q3”,make:”xyz”}, 
tags:[“coats”,”outerwear”,”clothing”] 
} } )

以上这个操作替换quantity的值为500,details字段的值为一个新的嵌入式文档,tags值为一个数组。

方案三:客户端压缩

考虑到需要尽快解决问题,所以需要尽量避免重写业务逻辑,压缩方式看起来是比较好的一中了。减少进入MongoDB的Master节点的数据量,这样可以减少写入oplog的数据规模。但是,这种方式会将JSON字符串转变为二进制文章,操作时也需要解压缩。

常用的压缩算法主要有:deflate、gzip、bzip2、lzo、snappy等。差别如下所示:

  1. deflate、gzip都是基于LZ77算法与哈夫曼编码的无损数据压缩算法,gzip只是在deflate格式上增加了文件头和文件尾;

  2. bzip2是Julian 
    Seward开发并按照自由软件/开源软件协议发布的数据压缩算法,Apache的Commons-compress库中进行了实现;

  3. LZO致力于解压速度,并且该算法也是无损算法;

  4. LZ4是一种无损数据压缩算法,着重于压缩和解压缩速度;

  5. Snappy是Google基于LZ77的思路用C++语言编写的快速数据压缩与解压程序库,2011年开源。它的目标并非最大程度地压缩,而是针对最快速度和合理的压缩率。

目标和考虑

在我们开始做这一功能性测试之前,我们需要明确几个目标。

  • 允许购物车被压缩并持久化到MongoDB(数据不会有改变)。
  • 允许压缩编码方式的选择,支持采用一种编码方式读取,另一种编码方式写入。
  • 允许读到老的、新的、中间状态的购物车信息,新老前后可以互相兼容。
  • 压缩和解压缩的操作可以同时进行。
  • 确保没有针对MongoDB数据库的实时JSON数据检索查询请求。

JSON字符串例子

这是老的JSON字符串:

{ "_id" : ObjectId("560ae017a054fc715524e27a"), "user" : "9999999999", 
"site" : 0, "computeMethod" : "CCS_V4.0.0", "cart" : "...JSON cart 
object...", "lastUpdatedDate" : ISODate("2016-09-03T00:47:44.406Z") }

这是压缩之后的JSON字符串:

{ "_id" : ObjectId("560ae017a054fc715524e27a"), "user" : "9999999999", 
"site" : 0, "computeMethod" : "CCS_V4.0.0", "cart" : "...JSON cart 
object...", "compressedData" : { "compressedCart" : "...Compressed 
cart object..." "compressionMetadata" : { "codec" : "LZ4_HIGH", 
"compressedSize" : 3095, "uncompressedSize" : 6485 }, }, 
"lastUpdatedDate" : ISODate("2016-09-03T00:47:44.406Z") }

测试结果

通过使用相同的购物车数据进行测试,观察CPU或者I/O情况,数据如图所示:

 

结论

oplog的写入速率,从150GB/小时下降为大约11GB/小时,下降了1300%!文档的平均对象大小从32KB下降为5KB,600%的下降。此外,服务的响应时间也有所改善。数据如图所示:

下面这张图显示的是MongoDB的Ops Manager UI工具信息,特别标注了压缩和解压缩数据的耗时,以及文档的平均对象大小的下降数据。

最终,对于生产环境下的随机一小时数据压缩,eBay团队也收集了一些指标图,用于更多的深入观察。


感谢杜小芳对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT