我们收到关于如何估算 Windows Azure存储成本,以便了解如何更好地构建一个经济有效的应用程序的问题。在本文中,我们将从带宽、事务和容量这三种存储成本的角度探讨这一问题。
使用 Windows AzureBlob、表和队列时,存在以下几方面的存储成本:
1.带宽 –从托管存储帐户的位置传入和传出的数据量
2.事务 –对您的存储帐户所执行请求的数量
3.存储容量 –持续存储的数据量
请注意,随着我们向存储系统添加更多功能,本文内容也会不时予以更新。本文将作为指导原则,使服务能够在应用程序运行于生产环境之前估算其存储带宽、事务和容量使用情况。
Azure存储服务目前在中国地区提供了一定的免费数据传输和存储事务的额度。
-
存储事务:前100亿个存储事务是免费的。
具体定价信息请参考:http://www.windowsazure.cn/zh-cn/pricing/overview/
下面概述了这三个方面的计费情况:
1. 带宽 –由于应用程序需要基于存储数据进行计算,因此我们允许托管服务与存储归置在一起。这样我们就能在归置在一起的计算和存储之间提供免费的带宽,将仅对从数据中心外部访问存储所用的带宽计费。
2. 事务 – Blob、表(Table)和队列(Queue)向存储服务提交的每一个 REST 请求均视为一个潜在的待计费事务。这样,应用程序就可以通过控制向存储服务发送请求的频率和数量来对事务成本加以控制。我们会分析收到的每一个请求,然后根据我们处理请求的能力及请求的结果将其划分为可计费或不可计费两类。
3. 容量 –我们通过汇总所存储对象(Blob、实体(Entity)和消息)及其应用程序和系统元数据的大小来测算待计费的存储容量。
接下来,我们将解释如何了解您的应用程序的这三个方面。
什么时候带宽会被计费
要访问 Blob、表和队列,首先需要通过Windows Azure开发人员门户创建一个存储帐户。在创建存储帐户时,您可以指定存储您的存储帐户的位置。我们目前在中国提供的数据中心位置包括:
1. 中国北部 (北京)
2. 中国东部 (上海)
存储帐户中的所有数据都将通过所选位置进行存储和访问。一些应用程序会选择在地理上最接近其客户群的位置,以尽可能降低对这些客户的延迟。这里一个关键方面是,您应该在开发人员门户中为您的托管服务选取与存储服务需要访问的存储帐户相同的位置。原因就在于在同一位置内部传输数据所用的带宽是免费的。相比之下,当从存储帐户指定位置传入或传出数据时,本文开始部分提及的带宽费用将会累计。
这里需要重点指出的是,在同一位置访问所用的带宽是免费的,但相关事务不是。对存储系统的每一次访问都计为一个待计费事务。此外,仅对被视为可计费的事务所使用的带宽收费,下文有对此类事务的具体定义。
带宽方面涉及的另一个概念是在您通过 Windows Azure内容分发网络 (CDN)使用 Blob时。如果在 CDN中未找到 Blob或者 Blob的生存期 (TTL)已过期,将从存储帐户(来源)中读取并进行缓存。这种情况下,将针对缓存 Blob(从来源传输至 CDN)所消耗的带宽对存储帐户收费(同时也构成一个事务)。所以要强调的是,应该对引用次数很多并足以获得缓存命中的 Blob使用 CDN,这样,在 Blob 因 TTL而在缓存中过期之前,将可以抵消掉把 Blob从存储帐户传输至 CDN产生的额外时间和成本。
下面举出了几个示例:
-
您的存储帐户和托管服务都位于“美国中北部”。由于位于同一位置,托管服务访问存储帐户数据消耗的所有带宽都是免费的。
-
您的存储帐户位于“美国中北部”,托管服务位于“美国中南部”。托管服务访问存储帐户数据消耗的所有带宽将产生本文开始时列出的带宽费用。
-
您的存储帐户位于“美国中北部”,您的 Blob 由位于欧洲的其中一个 Windows Azure CDN边缘位置进行缓存和提供服务。由于此 Windows AzureCDN边缘位置与您的存储帐户不在同一位置,当在 Windows AzureCDN中读取来自存储帐户的数据进行缓存时,将会产生上述带宽费用。
-
您的存储帐户位于“美国中北部”,但可通过世界各地的网站和服务进行访问。由于不是通过位于同一位置的 Windows Azure 托管服务进行访问,因此将收取标准带宽费用。
如何对事务计数
对于事务,我们首先要解释的是,什么才算作一个 Windows Azure存储事务。Windows Azure Blob、表和队列的每一次 REST 调用都算作 1个事务(事务是否计费取决于具体计费分类,计费分类将在本文稍后讨论)。REST调用详细信息请参见:
上述每次 REST调用都算作 1个事务,包括以下类型的请求:
- 查询/列表请求和继续令牌(continuation tokens) – 表查询以及列表 Blob容器、表和队列可以返回继续令牌。也就是说必须继续进行查询/列出操作直至任务完成。如前文提到过,对存储服务的每一个 REST 请求都算作 1个事务。而每次查询/列表继续都是对存储服务的另一个 REST 请求,因此可算作另一个事务。
-
批量操作 –我们目前支持两种批量操作:
-
获取消息 -从某个队列中一次获取多达 32条消息的能力。
-
实体组事务 – 通过 Azure表“插入”、“更新”或“删除”的任何组合针对多达 100 个实体执行原子事务的能力。要求所有实体都在同一个表中,具有相同的 PartitionKey值,而且总请求大小必须低于 4MB。
-
这两类批量操作都将产生对存储服务的单个 REST请求。因此,它们的每次请求都算作一个事务。
在使用存储客户端库时,有几个函数调用可产生对存储帐户的多个 REST请求。
-
上传 Blob – 在上传 32 MB以上的 Blob时,存储客户端库默认会将此 Blob分为多个 4 MB的块。块的大小可以通过设置CloudBlockBlob.StreamWriteSizeInBytes字段来更改。上传时,客户端库会将块作为单独的PutBlock REST 请求上传,并最终通过PutBlockList集中提交所有块。每个 PutBlock将算作 1个事务,最终的 PutBlockList也将算作 1个事务。
-
表查询 –当使用TableQuery进行查询时,它将负责处理继续令牌,因此会利用在前一个查询请求中收到的继续令牌重新发出查询,以获取其余实体。如上所述,对服务重新发出的每一个继续令牌查询都算作 1个事务。
-
表操作 –对于表服务而言,每次使用CloudTable.Execute,也就是向服务器发出一次请求,将被视为1个事务。特别指出的是,使用CloudTable.ExecuteBatch也视为1个事务,即便一次提交包含高达100个独立操作。所以开发者可以多利用CloudTable.ExecuteBatch来节省表操作中的事务量。[ZY1]
下面举出了几个示例:
-
向 Blob服务发出的单个 GetBlob请求 = 1个事务
-
向 Blob服务发出的单个 PutBlob请求 = 1个事务
-
大规模 Blob上传带来通过 PutBlock执行的 100个请求及最终 1个 PutBlockList集中提交 = 101个事务
-
使用总共 5个请求列出大量 Blob(由于有 4 个继续令牌)= 5个事务
-
表单个实体的插入请求 = 1 个事务
-
针对 100个实体的独立表操作= 100 个事务
-
针对 100个实体的组操作= 1 个事务
-
表查询指定具体 PartitionKey和 RowKey匹配(得到一个单一实体)= 1个事务
-
表查询通过单一存储请求返回 500 个实体(未遇到继续令牌)= 1个事务
-
表查询产生对表存储的 5 个请求(由于有 4个继续令牌)= 5个事务
-
队列放置消息 = 1个事务
-
队列获取单个消息 = 1个事务
-
队列获取空队列消息 = 1个事务
-
队列批量获取 32个消息 = 1个事务
-
队列删除消息 = 1个事务
现在我们了解了什么是事务,接下来看看哪些事务计费,哪些事务不计费。
当某个事务到达我们的服务时,如果属于以下分类范围,那么我们不会将其计为可计费事务,而且不对这些事务收取带宽费用:
-
预身份验证失败 –如果对于某个事务,我们甚至都没有机会去进行身份验证,那么此事务不会计入可计费事务。例如:具有错误 HTTP头的错误格式的请求、错误格式的 URL、请求的时间范围无效等。
-
身份验证失败 –如果某个事务的身份验证失败,我们不会将其计为存储帐户的可计费事务。
-
配额权限失败 –我们为每个存储帐户设定了500TB 的配额。当存储帐户达到这一限额时,我们会将存储帐户转入只有 READ+DELETE权限但没有 WRITE权限的模式。此时存储帐户还可以执行获取和删除操作,但无法执行放置/发布操作。在这种模式下,要求有写入权限的请求将会失败,我们不会将这些请求列为可计费事务。
-
共享访问签名 (SAS) HTTP动词/权限不正确 – 如果发送签名得到正确验证(身份验证),但 REST操作关键字(PUT、POST、GET、DELETE)没有正确匹配权限,那么此请求将不算作可计费事务。例如,如果权限指定只能执行 PUT 操作,但实际在有效 SAS上使用了动词 GET,那么此请求将失败,不会列为可计费事务。
-
匿名请求失败 –没有签名的请求将被视为匿名请求,我们将以下三种失败情况划分类为不计入可计费事务的事务:
-
权限不正确 –匿名请求只能是 GET请求。如果不是 GET请求,将会被拒绝,并且不列入可计费事务。
-
找不到容器 –如果针对匿名 GET请求的容器不存在,那么此请求不会计为可计费事务。
-
找不到 Blob – 如果针对匿名 GET请求的尝试访问的 Blob不存在,那么此请求不会计为可计费事务。
-
意外超时 – 如果某个请求因存储系统问题出现超时,则不会计为可计费事务。
-
满足以上任何条件的事务都不会列为可计费事务,而且不会对相关请求消耗的带宽收费。我们将其他事务列为可计费事务,这些事务是否会产生上述带宽部分中所述的带宽费用则因情况而异。
我们将可计费事务分为以下几类:
-
成功的事务 –任何针对存储系统成功完成的事务。
-
预期的失败 –预期的失败来自以下三个方面:
-
事务错误 –这些需求有正确的权限,经过了正确的身份验证,但是因对存储帐户中的数据应用了事务语义而失败。例如:
-
尝试获取或删除一个早已被删除或根本不存在的对象,导致 NotFound错误。
-
尝试创建一个已经存在的对象,导致 AlreadyExists错误。例如,我们发现一些应用程序会在每次将消息放置于队列之前,在此队列中执行 CreateIfNotExist操作。这就导致针对他们想加入队列的每个消息都会向存储系统发出两个不同的请求,导致创建队列失败。确保仅在最开始创建 Blob容器、表和队列,避免上述额外的事务成本。
-
采用 ETag Match、Non-Match、Modified-Since或 Unmodified-Since命令执行有条件的操作,即使操作失败(返回 NotModified或PreconditionFailed消息),也将算作一个事务。
-
尝试添加一个已经存在的表实体,将导致失败(冲突 - 409)。同样,尝试更新一个并不存在的实体也将会导致失败(未找到 - 404)。这些操作都将带来事务成本。这其实是想要执行 Upsert的应用程序所面临的问题,目前我们正在就此评估如何支持 Windows Azure表。在提供 Upsert之前,用户应该评估他们的使用方案,确定是先 INSERT后 UPDATE还是先 UPDATE后 INSERT,以便最小化预期失败次数及发送到 Window Azure 表的总体事务数。
-
有效共享访问签名 (SAS),但出现 ContainerNotFound 或 BlobNotFound的情况。如果 SAS签名获得正确验证,但未找到容器或未找到要访问的 Blob,此项操作也算作一个可计费事务。
-
这样的例子还有很多,原因就在于根据存储服务提供的语义(例如有条件的操作、租赁、序列号等),会有很多因素导致请求遭遇预期失败。
-
-
限制 –存在因事务率超过每个分区目标吞吐量而被限制的请求。分区目标吞吐量在“Windows Azure 存储服务概览及其可伸缩性目标”一文中有过介绍。这些被限制的请求也算作可计费事务。在这种情况下,客户端将使用指数退避算法,并重试请求,默认情况下存储客户端库会提供此选项。如果是服务的重复出现事件,则此服务应该考虑对数据结构进行额外分区,如即将发布的“Blob、表和队列”一文中所述。
-
预期超时 – 在您向服务发送请求时,您可以指定您自己的超时时间,可将其设置为小于SLA超时。如果此请求的超时时间小于 SLA 超时,在请求超时时,将被划分为预期超时,并计入可计费事务。
-
如何估算容量
客户要求了解有关 Blob、表和队列存储成本的更多详细信息,以便在运行于生产环境之前估算他们的应用程序所使用的存储容量大小。
首先需要了解存储容量的每月计费累计方式。存储容量每日至少计算和汇总一次,然后通过整个月的数据平均得出 GB/月费用。例如,如果在上半个月使用 10 GB,下半个月使用 0 GB,则月度计费基数将为 5 GB。
下面将介绍如何计算 Blob、表和队列的存储容量。
其中 Len(X)表示字符串中的字符数。
Blob容器 –下面说明了如何估算每个 Blob容器使用的存储量:
48字节 + Len(容器名称)
* 2 字节 +
每个元数据[3字节
+ Len(元数据名称) + Len(值)]
+
每个签名标识符[512字节]
以下是明细信息:
-
每个容器 48字节的开销包括最后修改时间、权限、公共设置和一些系统元数据。
-
容器名称以 Unicode形式存储,因此字节数按字符数乘以 2计算。
-
对于每个存储的 Blob容器元数据,我们会存储名称长度(以 ASCII码存储)加上字符串值的长度。
每个签名标识符的 512字节包括签名标识符名称、开始时间、到期时间和权限。
Blob –下面说明了如何估算每个 Blob使用的存储量:
对于 Block Blob(基本
Blob 或快照):
124字节
+ Len(Blob名称) * 2字节
+
每个元数据[3字节
+ Len(元数据名称) + Len(值)]
+
8字节
+已提交和未提交块数 *块
ID大小(字节) +
SizeInBytes(唯一的已提交数据块存储的数据)
+
SizeInBytes(未提交数据块中的数据)
对于 Page Blob(基本
BLob 或快照):
124字节
+ Len(Blob名称) * 2字节
+
每个元数据[3字节
+ Len(元数据名称) + Len(值)]
+
具有数据的不连续页面范围数 * 12字节
+
SizeInBytes(唯一的页面存储的数据)
以下是明细信息:
-
Blob 124字节的开销包括最后修改时间、大小、缓存控制、内容类型、内容语言、内容编码、内容-MD5、权限、快照信息、租赁和一些系统元数据。
-
Blob名称以 Unicode形式存储,因此字节数按字符数乘以 2计算。
-
对于每个存储的元数据,我们会存储名称长度(以 ASCII码存储)加上字符串值的长度。
-
对于 Block Blob
-
对块列表为 8字节
-
块数乘以块 ID大小(字节)
-
加上所有已提交和未提交块中的数据大小。注意,如果使用了快照,则大小仅包括此基本或快照 Blob的唯一数据。如果一周之后未使用未提交的块,这些块将被回收,此时它们将不再计入之后的计费。
-
-
对于 Page Blob
-
字节数按具有数据的不连续页面范围数乘以 12计算。这是在调用 GetPageRanges API时所看到的唯一页面范围数。
-
-
加上所有存储页面中的数据大小(字节)。注意,如果使用了快照,则大小仅包括所清点基本或快照 Blob的唯一页面。
表 –下面说明了如何估算每个表使用的存储量:
12字节 + Len(表名) * 2 字节
以下是明细信息:
-
每个表的 12字节开销包括最后修改时间和一些系统元数据。
-
表名以 Unicode形式存储,因此字节数按字符数乘以 2计算。
实体 –下面说明了如何估算每个实体使用的存储量:
4字节 + Len (PartitionKey
+ RowKey) * 2字节 +
每个属性(8字节
+ Len(属性名称) * 2
字节 + Sizeof(.Net属性类型))
以下是明细信息:
-
每个实体的 4字节开销包括时间戳和一些系统元数据。
-
PartitionKey和 RowKey值中的字符数,以 Unicode形式存储(乘以 2计算字节数)。
-
因此,对于每个属性,为 8字节开销 +属性名称 * 2字节 +来自以下列表的属性类型的大小。
不同类型的 Sizeof(.Net属性类型)为:
-
字符串 –字符数 * 2字节 + 4字节的字符串长度
-
DateTime – 8字节
-
GUID – 16字节
-
Double – 8字节
-
Int – 4字节
-
INT64 – 8字节
-
Bool – 1字节
-
二进制 – sizeof(值)字节 + 4字节二进制数组长度
队列 –下面说明了如何估算每个队列使用的存储量:
24字节 + Len(队列名称)
* 2 +
每个元数据(4字节
+ Len(队列名称) * 2
字节 + Len(值)
* 2 字节)
以下是明细信息:
-
每个队列的 24字节开销包括最后修改时间和一些系统元数据。
-
对于所存储的每个队列元数据,名称长度和值均以 Unicode存储,因此字节数为乘以 2。
消息 –下面说明了如何估算每个消息使用的存储量:
12字节 + Len(消息)
以下是明细信息:
-
12字节开销包括 InvisibilityTime、Creation Time、Dequeue Count、Message ID 和一些系统元数据。
-
如果您使用 REST界面,还包括代表消息的字节数。如果使用存储客户端库,则采用以下格式存储消息:UTF8(Base64(消息)),而且所使用存储将是其长度。
Brad Calder
如果您有任何疑问, 欢迎访问MSDN社区,由专家来为您解答Windows Azure各种技术问题
本文翻译自: