落地Azure CosmosDb的一个项目分享

我们遇到了什么?

我们有这么一个业务场景,就是某供应商会去爬取某些数据,爬到后会发到一个FTP上,然后我们定时去获取这些数据

这个数据有大有小,小的30多M数据量百万级,大的数据量能到数百M上千万数据

然后当这些数据到达的时候,我们需要在有限的时间内把这些数据写入到我们自己的数据库里.

而写入的过程包括先进行一轮数据清洗,按照指定的规则映射到我们自己的数据后在写进去并不是简单无脑的数据来了就batch insert那么简单

原来的实现方案

传统的Sql Server硬抗,DBA叫苦连天,经常投诉,但是你投诉我也无能为力

由于数据量大小问题也只能存7天有效数据

经常遇到要查历史数据什么的,我未来数据都存的够吃力了你还想要历史?

现在的方案

后面就想着基于Azure用云上的方案来彻底解决下这个问题

如下是我们整个方案的架构图(视情况抹掉了一些敏感信息)

其中核心就是CosmosDb

image

上面红色字的标号是整个流程的执行顺序

先简单说下整个步骤:

1.使用VM运行一个windows service,然后定时检测供应商的FTP,如果有文件的话就拉取回来

2.将获取回来的原始文件原封不动的先存储到Azure Storage里的Data Lakge Gen2

3.将数据写入到对口的存储系统,其中分为几个子步骤

  3.1 用一个Azure SqlDatabase产生一个批次日志

  3.2 将数据写入到Azure CosmosDb

  3.3 将数据写入到Azure Table Storage

4.通过Azure Service Bus发送一个队列消息,内网一个站点订阅该队列,获取消息后去获取消息处理数据

5.如果后续内网里的系统要用相关数据的时候通过一个Web App去访问云上的数据

各步骤解释:

步骤1:没什么好说就一个传统爬虫的样子

步骤2:将原始数据先存储下来,而之所以存储到Data Lake Gen2是考虑到后续如果我们团队有大数据能力的话,就可以分析这里面的内容,也因此选择了Date Lake而不是Blob来作为存储

步骤3: 这里可能会比较好奇3.2和3.3为什么数据要写入2份,规划里业务系统主力用的是CosmosDb,后面会简单介绍下这个,而Table Storage更多相当于是一个快照形式(稍后也会介绍这个),2个存储各自有各自的侧重点

步骤4: 没专线情况下的曲线救国方案

步骤5: Web App统一对外入口,云上数据存储原则上不应该对外网暴露,这样可以更好控制安全

在整个方案中用的几个核心技术:

1.CosmosDB

具体的可以看微软官方解释: Azure Cosmos Db 

我用站在我开发的角度以我们使用的视角简单说一下,想详细了解的请具体参考上面链接里微软官方的介绍

首先我们给它的定位是:费用较高的高性能存储,用于直接给业务系统对接使用

这是微软一个支持多种模式(Sql/Mongodb/图数据库/或者其他几个形式)的能自动分库分表(设计好Partition)全局索引(不用坑爹的考虑索引优化)性能和你掏的钱成正比的数据库

由于这个玩意支持Sql Api,我们之前系统用了EfCore来做Orm,而Ef Core 3是支持以CosmosDb作为Provider

所以这个切换不用太爽 

image

我们在上云的时候发生一个小插曲就是预订要上线的日期结果上面还有些功能不ok,但是别的业务又急着要上线,怎么办呢?UseCosmos改回UseSqlServer切回老的数据源先呗

然后按照自己的业务逻辑,对CosmosDb设置好分区键的话,他背后就能自动帮你分库分表,在全命中你分区键的前提下将会提供较高的效率保证,而如果没命中的话效率可能会下降但是结果也能出来(相当于要跨库查询了)

具体可以参考 Azure Cosmos DB 中的分区

CosmoDb的费用主要有2部分构成,一个是你存储的数据大小,另一个就是你购买的RU

关于RU

CosmosDb的性能是以RU(request unit)作为计量单位,具体可以参考 Azure Cosmos DB 中的请求单位

RU看起来蛮复杂的,我怎么知道要设置多少RU? 

RU配置是可以支持动态配置的,你设置好一个你允许的最大RU,然后它平时会维持较低的RU运作,只有当当前RU不能满足请求的前提下它会自动扩充直到你设置的最大值为止

不过这个功能有点小限制,就是只能扩充10倍,比如你设置最大RU是10000,那么它平常运作的最小值只能是1000,且自动扩充的话每RU的费用是比手动指定RU贵了50%

关于这个我自己感觉可能是先用自动RU来观察下程序平日运作的模式,以后用Azure Function来自己调整可能会比较能节省费用

当前我们是全自动

2.Table Storage

上面那个CosmosDb是费用较高的高性能存储

那么这个就是费用较低的廉价大容量存储 

Azure Table Storage 

说起这个表存储,大概2013年时候那会Microsoft Azure还叫Windows Azure的时候我就用过,对这玩意还是有点儿亲切的,而这个服务在Azure里也算是老字号了 

Table Storage有3个特殊字段Partitionkey,RowKey,Timestamp,其中Timestamp是自动生成不用管,重点是Partitionkey和RowKey

Partitionkey分区键

用数据库分库分表的思路来说就是你存在哪个库里

Table的效率和这个是强相关,一个好的Partitionkey的话应该满足:绝大多数查询都需要查这个,同一个分区下数据量不能太多,不要存在热分区(比如同时大批量写入,因此一般时间做分区就不是那么好)

RowKey行键

用数据库的角度理解相当于主键,但是由于PartitionKey的存在,所以是在同一个PartitionKey下RowKey不能重复,但是跨PartitionKey的话(相当于不同库了)是可以重复的

Table的查询效率最高效的是同时命中PartitionKey和RowKey,其次是只命中PartitionKey,再次是只命中RowKey,如果PartitionKey和RowKey都不命中则会相当缓慢

Table一个优势在于它存储比较廉价,世纪互联版是4毛5一个GB你还想要什么飞机?在我们内部广泛用于各种乱七八糟的日志存储

成果

我们整个方案9月初上的,然后就迎来了国庆大考

就说CosmoDb

image

上图是我们监控的情况

蓝色线是当前系统实际使用的RU

橙色线是请求量

注:RU和请求并不是1:1的关系所以不用在意蓝色线比橙色先更高,主要看趋势

可以看到当请求量上涨的时候,RU(性能)也跟着上涨

要明白这是一个数据库,数据库也能做到和Web一样弹性伸缩

当请求量下去的时候我们RU也能下去维持较低的运行成本

整体国庆期间整个系统运作平稳,新的架构能有效满足我们业务需要

回想下我们这个场景是属于典型瞬时并发高,常规并发较低的一个场景,如果用传统自建数据库来处理的话

起码也要达到峰值80%以上的性能把?但是就算这样也意味着绝大多数的闲时是浪费的,而高峰时刻也不是那么够用

而CosmosDb的特性则很好满足我们的需要,通过动态调整RU的形式满足我们波峰波谷的需要

后续

后面几个规划要演进的功能

1.尝试使用Web App里的Web Job替换掉VM里的windows service,纯粹为这个开个vm台浪费了,而且vm真的一点都不cloud

2.考虑能否自己控制cosmosdb的缩放(通过Azure Function)以便实现更大倍率的自动伸缩以及降低成本

3.琢磨Azure Databricks看看能否从Data Lake Gen2里存的数据分析出个啥