【IT168 技术文档】理论上表存储应该是我们最熟悉的存储方式,它本身也是结构化的存储,类似于我们平时的关系型数据库的存储。不过这里首先要强调的还是编程思想的变化,Windows Azure 平台是云计算的平台,所以从设计开始,它的定位是托管方式的,全开放和分布式的。体现到Azure Service的表存储上也是一样,首先整个的表存储是租户式的设计,说到这里,一定有很多人会想起Saas中的多重租赁(Multi-tenancy)概念,并开始将Azure存储和其联系在一起联想翩翩了。Windows Azure 平台的存储设计是非常精巧的,我们先把焦点转回Azure Service是如何定义和操作表存储的。其中我们需要知道哪些理念。
第一篇:Azure Service探索:存储之本地存储
第二篇:Azure Service探索:存储之队列存储
第三篇:Azure Services探索:存储之Blobs存储
规则1:Azure的表存储也是跟随和隶属于一个帐号的,一个账号下面可以建立多个表存储 ,和队列一样表名跟随在帐号名的范围内,你可以从访问的REST路径看出规律: http://<Account>.table.core.windows.net/<TableName>
规则2:数据是保存在表存储中的,一个表是一个实体的集合,保存了一组实体的信息,每一个实体记录是一个行记录;一个实体是该实体的关键属性和相关的属性的集合,保存了一组实体属性的信息,实体的每一个属性是一个名/值对,对应一列。Entities <->Rows ,Properties<-> Columns .
基本上Azure 表存储的概念如下图:
Pictures source: http://blogs.msdn.com/jnak/archive/2008/10/28/walkthrough-simple-table-storage.aspx
套用上面的规则2,你可以发现一个联系人表是一个联系人实体的集合,保存了一组联系人的信息,每一个联系人记录是一个行记录;一个联系人是该联系人的关键属性和相关的属性的集合,保存了一组联系人属性(Name,Address)的信息,实体的每一个属性是一个名/值对(名字/小王,地址/北京王府井),对应一列。
规则3:一个实体固定有两个关键属性,这两个关键属性联合唯一标示一个实体。第一个关键属性是:PartitionKey,它是实体在数据库信息中的分区标示/区域关键字,第二个属性是RowKey,它是在某个分区内能够唯一标示该实体的关键字。
下图可以清晰地看到规则3的定义
Pictures source: ES07.pptx of PDC 2008-Windows Azure Tables:Programming Cloud Table Storage
文档(Document)是一个Azure的表存储,保存了一组文档实体。每一个文档对应一行记录,每一个文档包含关键属性(Document,Version) 和一些相关属性 (ChangedOn, Description);每一个属性是一个名值对,对应到数据库的一列。
根据规则3,PartitionKey其实对文档的一个细分或说颗粒化,用来作为分类/分区的标示,这个文档是FAQ文档还是样板文档,而RowKey属性在一个分类中,可以唯一标示和定位到这个文档。比如v2.0.1加上分类就可以唯一定位到v2.0.1版本的Examples文档。
其实这如何细分来确定这个颗粒度,是由应用来决定的,从上图可以看到,你可以在一个表里定义多个不同的文档分类关键字,也可以将整个表的PartitionKey都定义成一个,一样的关键字,这样分区就扩大到整个一张表,版本号就成为真正的关键字了。从规则3可以看出Azure的表存储的设计原则,一是侧重于直接贴近业务场景和实体对象,第二是要方便查询。
规则4:Azure运行环境会根据PartitionKey来对实体数据进行聚集和索引,PartitionKey是你进行实体查询的首要关键字,相当于你SQL 查询中最主要的Where条件。从伸缩性上来看,Azure运行环境会将不同分区的实体/数据放到不同的存储节点上,它尽力会将一个分区的数据(PartitionKey关键字相同的数据)放在一个存储节点上,而不是多个存储节点上,同时会对这个存储节点优先作分区的负载均衡(Automatically load balance partitions)
Pictures source: ES07.pptx of PDC 2008-Windows Azure Tables:Programming Cloud Table Storage
从上图看,我们根据规则4可以得出下面的结论:因为只要搜索单个分区,所以你查询所有FAQ Doc类的文档速度会很快;同样,如果你查询从截至到2008年5月30日之前所有的FAQ Doc类文档则速度会很慢,因为 这个查询需要检索多个分区,而如果每个分区落在不同的存储节点上,那就需要花费更多的时间。
现在我们假设你要为中国移动设计一个Azure 版本的应用,就表存储设计来说,你将整个中国移动的客户放在一个表里可能定是不现实的,你一定会考虑分区,PartitionKey可以是地域属性,比如你按省来分区;RowKey是手机号码;你也可以将PartitionKey设定成移动业务的品牌;RowKey是手机号码;也许这样你还决得大,你会将一个省的客户数据放到一个表里,实体变成了广东省移动客户,这样PartitionKey可以变成省下面的地州市,比如广州市,深圳….RowKey是手机号码,甚至你还是觉得不够细,你可以将广州市的用户放到一个表里,实体变成了广州市移动客户,PartitionKey可以变成市下面的区,比如白云区,天河区,RowKey可以换成业务类型;甚至你还想细分……所以,上面说了数据的细分或说颗粒化取决于应用,但你应该了解Windows Azure的设计和规则。
针对规则4,这也可能涉及到实体的单表扩展还是多表扩展,也会涉及到一个实体对应多个表,多个实体对应一个表,一个实体在不同状态下有不同数量/动态的属性等等这样的设计难题和争议。我就不涉及太多。
规则5:每个实体最多拥有255个属性,但自定义的只有253个甚至更少一些,因为所有的实体都有两个固定的关键属性PartitionKey和RowKey。属性的定义和定义脚本是相当灵活和没有限制的,比如可以将两个不同属性/不同属性个数的实体定义在一个表中。
了解了上面的这些规则,只完成了我们第一项工作,Azure表存储的概念和设计概念。真正的如何定义表的操作,以及访问表存储是我们接下来要做的。
Azure 表存储的访问技术是最眩和最新的技术,比如完全兼容ADO.NET data services,使用最新的.NET 3.5 SP1中的.NET Client 类库访问,编程查询语言使用LINQ……. Windows Azure平台的开发模型涵盖了微软最新的所有的先进技术,这样不枉费各位粉丝从.NET 1.0到.NET 4.0的追随和勤奋学习,这下总算有用武之地了。
而后我就转到一个最简单的表存储的Azure 的应用,来演练和探索一些Azure表存储的基本操作和编程技术。因为如果了解了上述的规则和设计要点,我认为基本是掌握Azure表存储技术的关键,后面的过程都是看图说话和一些体力活-操作键盘和鼠标。
接下来你可以选择按照Jim Nakashima的文章Windows Azure Walkthrough: Simple Table Storage来体验,也可以继续按下面的步骤体验,我的操作选自Azure Services Training Kit - PDC Preview的一个练习,两者的差别是Jim Nakashima的文章涉及更多的一些话题Create Tables Only Once这样有关高性能的示范,因为Jim Nakashima决定他应该提供高质量的示范代码J
这个练习主要是熟悉表存储的创建和访问,运行后的界面如下图:
对于这样一个简单的聊天留言板,数据表更是简单,基本上是一个叫聊天消息的实体,实体有两个属性/字段,聊天人的名字(Name)和聊天内容(Body),当然根据上面的规则,还有ParitionKey 和RowKey这两个关键和固定字段。
首先依然是向上一篇访问队列那样,进行Azure服务的定义和配置。分别定义TableStorageEndpoint,AccountName和AccountSharedKey的属性
服务定义文件ServiceDefinition.csdef, 这个例子还没有用到Worker Role ,也可不配置
<ServiceDefinition name="WorkingWithTables" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="WebRole">
<ConfigurationSettings>
<Setting name="TableStorageEndpoint"/>
<Setting name="AccountName"/>
<Setting name="AccountSharedKey"/>
</ConfigurationSettings>
<InputEndpoints>
<!-- Must use port 80 for http and port 443 for https when running in the cloud -->
<InputEndpoint name="HttpIn" protocol="http" port="80" />
</InputEndpoints>
</WebRole>
<WorkerRole name="WorkerRole">
<ConfigurationSettings>
<Setting name="TableStorageEndpoint"/>
<Setting name="AccountName"/>
<Setting name="AccountSharedKey"/>
</ConfigurationSettings>
</WorkerRole>
</ServiceDefinition>
服务配置文件ServiceConfiguration.cscfg中,定义在服务定义文件中各个参数的值
<ServiceConfiguration serviceName="WorkingWithTables" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
<Role name="WebRole">
<Instances count="1"/>
<ConfigurationSettings>
<Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
<Setting name="AccountName" value="devstoreaccount1"/>
<Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/> </ConfigurationSettings>
</Role>
<Role name="WorkerRole">
<Instances count="1"/>
<ConfigurationSettings>
<Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002"/>
<Setting name="AccountName" value="devstoreaccount1"/>
<Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/> </ConfigurationSettings>
</Role>
</ServiceConfiguration>
Azure 服务的表存储存取API,依然是采用了SDK中的StorageClient 类库,然后分别在Web Role项目中添加StorageClient的引用
接下来的步骤是关于表存储种中比较重要的一个工作,那就是建立一个实体类与表模型的脚本。
进一步的说,我们不用写数据库的建表脚本来建立在Azure存储系统中建了表存储,而是根据上述的规则,我们告诉Windows Azure我们的实体是什么,ParitionKey 和RowKey分别是什么,怎么赋值,即满足规则2和规则3的要求,Windows Azure自动就会帮你建立表,建立索引甚至存储节点。之后你通过公开的API就可以访问这些表了。
具体的操作是,我们需要从Microsoft.Samples.ServiceHosting.StorageClient. TableStorageEntity 继承派生出我们的聊天消息实体类,这个类的定义和描述,其实就是实体类与表模型的脚本,之后我们可以通过开发工具或代码执行,告诉Windows Azure建立这个实体的表存储。差别是如果通过开发工具表存储是预先建立的,代码建立表可以在运行时,访问表存储之前建立。这也就是上面说的Create Tables Only Once的问题了,因为这存在一个什么是创建表的最好时机的问题,本练习时通过开发工具来预先建立表存储。
聊天消息实体类的定义如下:
2:
3: namespace WorkingWithTables_WebRole
4:
5: {
6:
7: public class myMessages : TableStorageEntity
8:
9: {
10:
11: public myMessages()
12:
13: {
14:
15: PartitionKey = "mymessages";
16:
17: RowKey = string.Format("{0:10}_{1}", DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid());
18:
19: // TimeSpan
20:
21: }
22:
23: public string Name { get; set; }
24:
25: public string Body { get; set; }
26:
27: }
28:
在构造函数里,我们定义了最关键的ParitionKey 和RowKey是如何定义,因为这个表非常简单,所以ParitionKey的赋值我给的是一个固定的值,没有按照规则4和规则5的要求来优化,之后是我们的自定义的属性Name和Body。
之后顺手再定义一个表的访问类,非常像DAL,因为上面的只是一个表定义类,我们的应用最终是需要访问这个表存储中的数据。这个我们需要从Microsoft.Samples.ServiceHosting.StorageClient. TableStorageDataServiceContext 继承派生一个类,就可以创建自己的数据查询和访问的类:
2:
3: public class MessageDataServiceContext : TableStorageDataServiceContext
4:
5: {
6:
7: public MessageDataServiceContext(StorageAccountInfo accountinfo)
8:
9: : base(accountinfo)
10:
11: {
12:
13: }
14:
15: public IQueryable<myMessages> Messages
16:
17: {
18:
19: get
20:
21: {
22:
23: return this.CreateQuery<myMessages>("Messages");
24:
25: }
26:
27: }
28:
29: public int AddMessage(string name, string body)
30:
31: {
32:
33: int ret = -1;
34:
35: try
36:
37: {
38:
39: this.AddObject("Messages", new myMessages { Name = name, Body = body });
40:
41: this.SaveChanges();
42:
43: ret = 0;
44:
45: }
46:
47: catch (Exception e)
48:
49: {
50:
51: throw e;
52:
53: }
54:
55: finally
56:
57: {
58:
59: }
60:
61: return ret;
62:
63: }
64:
65: }
66:
之后在Azure的开发环境中预先建立表存储
操作如下:在Visual Studio中选中的你的项目,然后右键,点击”Create Test Storage Tables”
如下图:
之后会显示下面的提示框表示表已经成功创建了。
你可以查看Visual Studio的输出窗口:
你会发现Visual Studio使用了SDK中的命令行工具DevtableGen.exe,执行了下面的命令:
C:\Program Files\Windows Azure SDK\v1.0\bin\DevtableGen.exe" /forceCreate "/server:localhost\SQLExpress" "/database:WorkingWithTables" "obj\Debug\WorkingWithTables_WebRole\bin\StorageClient.dll;obj\Debug\WorkingWithTables_WebRole\bin\WorkingWithTables_WebRole.dll;
C:\myProject\WorkingWithTables\WorkingWithTables\WorkingWithTables_WorkerRole\bin\Debug\StorageClient.dll;
C:\myProject\WorkingWithTables\WorkingWithTables\WorkingWithTables_WorkerRole\bin\Debug\WorkingWithTables_WorkerRole.dll"
我们不妨到数据库中看一下建立的这个数据库和表
最后我们在WebRole中增加一些代码,来存取访问表存储的简单代码
查询和数据绑定
2:
3: {
4:
5: string statusMessage = String.Empty;
6:
7: try
8:
9: {
10:
11: StorageAccountInfo accountInfo = StorageAccountInfo.GetAccountInfoFromConfiguration("TableStorageEndpoint");
12:
13: // dynamically create the tables
14:
15: TableStorage.CreateTablesFromModel(typeof(MessageDataServiceContext), accountInfo);
16:
17: MessageDataServiceContext context = new MessageDataServiceContext(accountInfo);
18:
19: this.messageList.DataSource = context.Messages.Take(10);
20:
21: this.messageList.DataBind();
22:
23: }
24:
25: catch (DataServiceRequestException ex)
26:
27: {
28:
29: statusMessage = "Unable to connect to the table storage server. Please check that the service is running.<br>"
30:
31: + ex.Message;
32:
向表存储中增加记录
2:
3: {
4:
5: StorageAccountInfo accountInfo = StorageAccountInfo.GetAccountInfoFromConfiguration("TableStorageEndpoint");
6:
7: MessageDataServiceContext context = new MessageDataServiceContext(accountInfo);
8:
9: context.AddMessage(this.nameBox.Text, this.messageBox.Text);
10:
11: }
12:
F5 运行后,先检查开发环境中的表存储服务是否启动。
运行成功之后,我们还可以看一下SQL Server 数据库中的情况,比如Azure 开发环境对ParitionKey 和RowKey的理解和赋值情况
通过上面的体验,我们发现Azure的表存储的定义和操作,与传统的表定义和操作还是有些不同,另外在编程体验上基本上完全整合了ADO.NET Data Service和ADO.NET Entry Framework的风格和技术。
不过我们需要有比较清晰的概念和理解,就是目前Windows Azure 可能实现的是ADO.NET Data Service和ADO.NET Entry Framework的一个子集,如果对ADO.NET Entry Framework比较熟悉的,可能非常想知道ADO.NET Entry Framework的特性有多少在Azure上实现了,这个需要我们在之后慢慢探索。
最后花一点时间,讨论一下Jim Nakashima的文章Windows Azure Walkthrough: Simple Table Storage中的要点,也就是在真正部署到正事Windows Azure运行环境时,比较流行的方法是使用上面讲的第二种方式,就是运行时刻创建表存储,而且最好是只创建一次。这个文章中讲了,主要是使用TableStorage.CreateTablesFromModel方法来实现。Jim解释说在运行环境下建表过程是程序化完成的,但是在Azure 的开发环境(Local Development Storage)使用工具预先建立表示必须的步骤,这是Azure 的开发环境的限制。 我想这非常可能是在Widnows Azure 的运行环境中,表存储不是简单理解成在某个一个SQL数据库上建立一个表这么简单,可能其过程是非常复杂和神奇的。 而Mark Seemann给出了使用 另外一方法,就是用PowerShell的方法在Azure 运行环境创建表,基本上就是用Shell 脚本调用TableStorage.CreateTablesFromModel方法。