Windows Azure Storage性能调优(二)

上一篇我们提出的方法都是通过减少网络传输时间来提升性能。接下来,我会简介一下WAS的系统结构,然后解释如何设计程序来减少WAS端执行时间。 

 

WAS架构

Windows Azure Storage是一个三层架构,分为前端(FE)、分区层(Partition)、分布式存储层(DFS)。

前端负责接受客户端HTTP(S)连接,验证身份,计费以及写日志。

分布式存储层为实际的物理存储。无论Blob,File,Queue还是Table,其最终都已Binary形式存放在此层。此层确保数据在三个独立的物理节点上保存备份,避免数据丢失。

分区层提供了逻辑数据结构的访问能力。它理解数据的逻辑抽象结构,能够按照用户的请求来正确存取DFS层数据。同时,分区层实现了跨数据中心的备份同步功能。

 

WAS的设计目标是存储海量数据,实现大并发访问。海量存储由分布式存储层来实现,而大并发的目标是如何实现的呢?

从物理存储来看,大并发的压力来自于磁盘I/O和网络带宽。在DFS层,数据的物理存储单位是Extent,100MB-1GB不等。每个对象(Blob,Table或Queue)都由一个或多个Extent拼接而,除此之外,数据还有三个备份。这些Extent离散分布在多台独立的服务器上,因此,用户对对象的访问被分摊在多台服务器上,以达到高吞吐量。

数据逻辑结构的处理需要耗费Memory和CPU资源。Partition层将逻辑数据拆分为多个Partitions,比如:每个Blob, Queue即为一个Partition,Table中PartitionKey相同的所有实体存放在一个Partition。Partition层每台服务器负责服务一组Partitions,Master服务器会检测每台服务器的负载,并动态调整Partitions的分配。由于所有数据都存放在DFS层,因此负载调整会非常迅速。通过动态负载均衡,Partition层确保有足够的资源来响应客户请求。

 

WAS性能目标

微软在MSDN上公布了WAS的性能目标,这些数据是WAS的性能上限,若用户使用WAS超过指标时,WAS会返回给客户503(Server Busy)或500(Operation Timeout)。当用户接到此类错误时,建议用户采用回退的方式重试操作,这样可以缓解短暂高峰造成的WAS服务压力。

简要来说,性能目标有两层:Partition级和Storage Account级。Storage Account级的目标如下:

Total Account Capacity

Total Request Rate (assuming 1KB object size)

Total Bandwidth for a Geo-Redundant Storage Account

Total Bandwidth for a Locally Redundant Storage Account

500 TB

Up to 20,000 entities or messages per second

*Ingress: Up to 10 gigabits per second

*Egress: Up to 20 gigabits per second

*Ingress: Up to 20 gigabits per second

*Egress: Up to 30 gigabits per second

* Ingress refers to all data (requests) being sent to a storage account.

* Egress refers to all data (responses) being received from a storage account.

Partition级别的性能目标为:

Target Throughput for Single Blob

Target Throughput for Single Queue (1 KB messages)

Target Throughput for Single Table Partition (1 KB entities)

Up to 60 MB per second, or up to 500 requests per second

Up to 2000 messages per second

Up to 2000 entities per second

若要了解更多细节,可以访问如下官方文档

https://msdn.microsoft.com/en-us/library/azure/dn249410.aspx

 

分散工作负载

了解到WAS的性能目标后,开发者在设计时就要尽量避免达到性能上限。

举个实例。两个Role通过WAS Queue来做消息中转,消息吞吐量在高峰时段可能达到10K,那么如果设计人员只使用单Queue,则Queue会成为性能瓶颈,无论增加多少Role虚拟机数量,吞吐量被始终限制在2000 messages/sec左右。解决方案就是使用多个Queue来分散工作负载。

 

同样的道理,用户要避免使用一个WAS Account来放置过多的虚拟机磁盘,否则有可能达到WAS Account级的性能上限20,000事务/s。建议每个WAS Account最多放置40块虚拟机磁盘。

 

避免Table热区

前面提到过,一个Table中PartitionKey相同的实体放置在同一个Partition里,而Partition的性能上限是2000实体访问/s。如果应用程序设计不当的话,有可能出现Table的某些Partition访问过于频繁,造成WAS响应慢或者报错(500,503)。举个具体的例子:一个日志系统,使用日期做PartitionKey,那么所有的写操作都会hit当日的Partition。如下通过测试,给大家一个直观的感觉。

场景: 一个博客系统,使用WAS Table来存储博客内容。现有两种设计:

设计1 PartitionKey=date, RowKey=BlogThreadID+PostID

代码如下:

 

设计2 PartitionKey=date+BlogThreadID, RowKey=PostID

代码和设计1类似,唯独修改了PartitionKey

 

两种设计保存的数据内容是一致的,只是PartitionKey和RowKey的格式不同。最终执行效率能有多大区别呢?我这里使用压力测试来检验,结果差别巨大!

设计1,随着用户负载增加,服务延迟越来越高,吞吐量维持在1380/s

 

设计2,随着用户负载增加,服务延迟平稳,而吞吐量达到了2600/s

可见,开发者在设计PartitionKey,RowKey时要避免Hot Partition,否则其造成的性能差距是巨大的。

设计2仍然存在问题:Partition按照PartitionKey字符串排序,编号相近的Partition很有可能被分配在同一台Partition服务器。设计2可能造成Partition服务器压力过载,从而影响WAS响应速度。为了提高性能,用户可以考虑倒置日期字符串来避免Partition集中。

 

优化Table查询

Table的实体有多个属性,通过筛选属性值,可以返回需要的实体。筛选条件的选择会直接影响查询效率。

数据库用户都知道索引能够避免扫描操作,减少I/O,从而提升效率。Table也支持索引,不过每个Table只有一个索引,即PartitionKey+RowKey。指定了PartitionKey和RowKey的查询叫做Point Query,他的查询效率也最高。

如果没有指定PartitionKey和RowKey,而是单单筛选其它属性,则查询退化为全表的扫描操作,效率会大大降低。不仅如此,之前WAS架构提到过,Table数据保存在多个Partition中,而Partitions分布在多台Partition服务器上。如果遍历的数据散布在多台服务器,则WAS会在遍历完一台Partition服务器后返回客户端一个Token,客户端凭Token来再次请求,WAS会将请求导向到下一个服务器,多次Roundtrip过程会耗费更多时间。

 

以访问WAD Table为例,我测试了用全表查询,局部范围查询,指定Partition查询和Point查询来搜索同一个实体。

 

结果表明PartitionKey 范围越小,查询速度就越快。

 

因此,用户的查询操作要尽量指定PartitionKey或者一个PartitionKey范围,避免大数据范围的扫描。而设计者需要根据业务需求考虑如何组合PartitionKey,使查询语句尽量多的使用到PartitionKey。

Table Storage 设计模式

最近微软公布了一份官方文档,列举了使用Azure Table Storage下的一些设计技巧,感兴趣的读者可以直接访问如下文档

https://azure.microsoft.com/en-us/documentation/articles/storage-table-design-guide/?rnd=1