DynamoDB是一个高效而灵活的NoSQL数据库服务,它的主要特色就是便于管理,因此使用者不必担心各种管理方面的任务负担,例如操作和扩展数据库等等。使用者可以更关注于应用程序的设计,并且在通过几个简单的步骤后将其发布到DynamoDB服务上。
在本文中,我们将为你展示如何使用Amazon DynamoDB创建一个应用程序。
我们在本文中所讲述的示例应用将为读者展示DynamoDB数据库的强大功能。这一web应用展现了NASA向公众开放的数据,即好奇号火星探测器(Curiosity Mars Rover)从火星所发回的图像,以及用JSON格式描述的图像元数据。以下是NASA JSON数据的一个小片段,以及该示例应用的一张屏幕截图。你也可以自己尝试一下在线的示例应用!
图 1:火星探测器示例应用的屏幕截图
以下是包含了图片细节的JSON数据集。
(单击图片以放大)
图 2:来自NASA的一段图片JSON数据
在这个示例应用上线之前,我们特意收集了NASA的所有数据,所上图所示。并把所有图片的JSON数据都导入到一张DynamoDB表中,为将来查询做准备。在所有数据都导入DynamoDB之后,我们对数据表进行了大量的查询与更新,才最终得到了这个火星探测器应用,让它能够显示一个精美的图片浏览器,正如上图所示。
该应用程序的默认视图是来自于好奇号的摄像头、或摄像仪器中所返回的所有图像的时间线,以时间顺序倒序排列。用户可以投票选出他们最喜欢的图片,每张图片的投票数量都是实时维护的。此外,用户可以打开“任务控制”侧边菜单,以改变所使用的摄像仪器、时间范围、或是根据投票数量对图片进行排序。最好,用户也可以在“我的最爱”选项中查看所有投过票的图片。
以上所有这些特性的实现,都是通过对存储了图片数据的DynamoDB表进行查询而实现的。为了创建这样一个应用程序,你通常需要考虑多种功能组件,例如访问控制、用户追踪、数据序列化/反序列化,等等。我们将通过对创建火星控制器这一示例的解释,为你展现用DynamoDB实现以上这些功能是多么简单,并且你也可以使用DynamoDB创建你自己的应用程序!不过,在我们深入讲解这个示例之前,让我们先快速地了解一下DynamoDB。
DynamoDB的数据模型概念中包含了表、项目和属性。一张表是一系列项目的集合,而每个项目又是一系列属性的集合。
与关系型数据库不同,DynamoDB是一种无schema的NoSQL数据库。一张DynamoDB表中的每个项目都可以拥有不同数量的属性。每个项目中的属性是一个键-值对。每个属性既可以是单值的,也可以是多值的集合,稍后将讨论数据类型的细节。此外,最近发布的JSON文档支持功能允许JSON对象以项目的形式直接保存在DynamoDB中,最大可达每个项目400KB。举例来说,NASA以JSON对象的方式提供火星探测器传回的每张图片,因此每张图片都能够保存为DynamoDB中的一个独立的项目,而地点和时间等属性则能够被直接导入。
考虑一下将一系列从火星探测器所传回的图片保存在DynamoDB中的情形。你可以创建一张名为marsDemoImages的表,为每一张图片分配一个唯一的imageid属性(这也被称为表的主(哈希)键):marsDemoImages ( imageid,... )
这张表的每个项目都可以包含各种其它的属性,以下是一些属性的示例:
表1:marsDemoImages表中的一些示例项目
请注意:在本例中,“imageid”是唯一一个必需的属性,其它所有属性都能够自动从NANA的JSON图片数据集中进行导入。实际上,在这个示例中,101这个项目并不包含“camera_model”这个属性。"Mission+InstrumentID"则是一个混合属性,在接下来的一节将对此进行解释。
当你创建一张表时,你必须指定该表的主键。DynamoDB支持以下两种类型的主键:
· 哈希类型主键:这种类型的主键由一个哈希属性所构成。在之前的示例中,marsDemoImages表中的哈希属性就是“imageid”,正如下图所示。
表2:marsDemoImages中的部分项目示例,其中的主哈希键高亮显示
· 哈希及范围类型主键:这种类型的主键由两个属性所构成。第一个属性是哈希属性,而第二个属性是范围属性。在火星探测器这个示例中,假设我们打算首先以“imageid”字段、随后以“votes”字段对项目进行分组。那么哈希属性就是“imageid”,而范围属性则是“votes”。
表3:marsDemoImages中的部分项目示例,其中的主哈希与范围键高亮显示
除了使用主键对特定的项目进行访问与操作之外,Amazon DynamoDB也提供了多种方式对特定的数据进行搜索:即查询、更新与扫描。
查询:查询操作仅使用主键属性的值查找某张表中的特定项目。你必须提供一个哈希键的属性-值对,并可选择地提供一个范围键的属性-值对。
举例来说,在火星探测器应用中,我们可以通过“imageid = 201”这样的键-值对来查询某张特定的图片。
更新:更新操作与查询操作相类似,区分就在于你能够修改项目的属性了。条件式更新允许你在某个特定的条件满足之后,才能够对项目进行修改。稍后我们将看到这方面的一个示例,我们将对火星探测器应用中图片的投票数进行更新。
扫描:一次扫描操作将对整张表中的每个项目进行分析。在默认情况下,一次扫描操作会返回每个项目中的所有数据属性。
对整张表进行扫描在某些情况下会降低效率,为了避免这种情况,我们可以创建二级索引,以辅助查询的处理。表中的二级索引能够帮助优化对非键属性的查询。DynamoDB支持两种类型的二级索引:
可以将二级索引想象为额外的表,它们首先由哈希键进行、再由范围键进行分组。举例来说,在marsDemoImages表中,我们可能需要查找来自于某个特定任务与拍摄仪器的图片,并按照某个时间范围进行过滤。因此我们就可以创建一个二级索引,让它首先按照“Mission+Instrument”属性(哈希键)进行分组,随后按照“TimeStamp”属性(范围键)进行分组。下图是该索引的一个示例,在下一节关于marsDemoImage表的介绍中,我们还将详细分析二级索引的更多细节。
表4:marsDemoImages表中某个二级索引的示例
Amazon DynamoDB支持一系列新的数据类型:
比方说,在marsDemoImages表中,imageid是数字类型的属性,而camera_model则是字符串类型的属性。
其中最值得注意的是最新发布的数量类型:List和Map,这两种类型非常适合用于JSON文档的保存。List数据类型与JSON数组非常相似,而Map数据类型则类似于一个JSON对象。List或Map元素能够保存的数据类型是没有限制的,只是每个项目最多不能超过400KB,并且最多支持32个级别的内嵌属性。此外,DynamoDB还允许你访问list和数组中的每个元素,即使这些元素的嵌套层数相当多。DynamoDB的这个特性相当令人兴奋,它让开发使用JSON数据的web应用程序变得相当简单直接,接下来就让我们看看火星控制器这个示例的后台工作原理是什么。
火星探测器应用的实际工作原理是什么?在这一节中,我们将让你了解,DynamoDB中的JSON文档支持功能让这一应用的创建变得非常简便与直接。我们使用了AngularJS来创建这个应用程序,这是一个非常流行的JavaScript web应用框架,不过本文中的概念也适用于其它任何编程语言。如果你希望预览一下该应用的源代码,可以在GitHub上的awslabs帐号下找到完全公开的源代码。
为了理解这个应用程序的运行原理,让我们来看一看下图所示的火星探测器应用程序的整体架构,我们将一步一步地为你讲解每个组件的作用。
图 3:火星探测器的设计架构
当用户开始访问火星探测器示例的应用程序网站时,浏览器就会从Amazon S3获取应用程序代码,包括HTML、CSS和JavaScript。通过使用DynamoDB和S3,我们就能够在客户端完整地运行整个应用程序,就样就能够避免自己管理服务器的各种麻烦。
在这一步骤中,应用程序将通过Amazon Cognito对用户进行授权,让用户得以访问DynamoDB表。Amazon Cognito是一个简单的用户认证与数据同步服务,它能够在未认证访客与DynamoDB之间建立关联,允许任意用户对该应用程序所对应的DynamoDB数据表进行查询,并且对表中的某些属性进行有限制的更新操作。如果你打算自己部署这个示例,那么你也可以使用DynamoDB Local将整个应用程序运行在你自己的本地机器上,以进行开发或测试工作。在GitHub上的火星探测星应用源代码的README文档中可以找到在本地运行这一应用的操作指南。
让我们回到这个在线示例的认证环节,通过使用Amazon Cognito,我们就能够轻易地管理访问对DynamoDB数据表的访问,并收集访问者的数量等相关的统计信息。下图是一个截图示例:
(单击图片以放大)
图 4: Cognito统计数据界面的截图示例
通过使用Amazon Cognito,你就可以使用各种公开的登录提供者,例如Amazon、Facebook和Google,或是使用自己的用户身份系统为访问用户创建独立的用户身份信息,以访问AWS云服务。用户也可以选择作为未认证的访客身份访问你的应用。我们在这里使用了未认证访客访问特性,为web浏览器提供AWS身份信息,并对每个用户进行唯一识别。我们按照下面所列出的步骤将应用程序部署到生产环境中,你也可以用同样的步骤将你自己的应用程序进行部署:
用户可以根据日期、投票数和已保存图片等不同选项对图片进行选择。每次选择都是对DynamoDB数据表和索引的一次查询。为了充分理解这个过程的工作原理,我们需要深入了解DynamoDB的某些方面:
让我们首先来创建一张DynamoDB数据表!你可以通过AWS管理控制台,或是AWS开发SDK来完成这一任务。我们在这个示例中使用了由CoffeeScript生成的JavaScript文件,可在/viewer/lib/prepare_tables.coffee找到源代码。其中最重要的部分是对DynamoDB 数据表的schema的描述,以及GSI的配置,该数据表用于保存图片数据,以下是该表的具体形式:
表5:marsDemoImages的表schema
我们决定将“Mission”与“InstrumentID”这两个数据字段进行组合,以允许对多个属性进行同时查询。由于应用程序中的每个视图通常都对应着某个特别任务的一个仪器,因此可以选择专注于“Mission”与“InstrumentID”,使用这个组合属性作为GSI的哈希键,并且另外选择一个属性作为GSI的范围键。举例来说,用户可以在火星探测器的进行探险任务时,从“前端避险摄像头”仪器获取所有的图片,并根据日期进行过滤。GSI就能够提供这种类型的查询,该表中的GSI如下图所示:
表6:marsDemoImages表中的全局二级索引的schema
创建date这个GSI的作用是允许用户根据基于某个特定的仪器和任务,根据图片的创建日期对图片进行过滤。GSI通过索引哈希和范围键对项目进行分组,这意味着date GSI所包含的图片数据首先会根据“Mission+Instrument”属性进行分组,随后再根据“Timestamp”进行分组。这就允许应用程序能够快速地根据特定日期查找图片,例如找到于10/04/2014,通过“Curiosity+Front Hazcam”这个任务-仪器组合所拍摄的图片。
与之类似,创建vote这个GSI的作用是为了火星探测器示例应用的“投票最多”这一视图所用。在这一情景下,索引哈希键依然是“Mission+Instrument”,而范围键则是“votes”。该索引首先通过“Mission+Instrument”对项目进行分组,接下来再使用“votes”进行分组,这意味着它能够优化这样的查询:基于某个特定的任务和仪器,并根据投票数进行排序的图片结果。
接下来,我们需要一张额外对数据表,以追踪用户为哪些图片进行了投票,这样可以避免用户对同样的图片进行多次投票。这张表的schema很简单,也不需要用到GSI:
表7:userVotes表的schema
最后,我们将调用createTable方法,以创建DynamoDB中的所有表和二级索引。这项任务由/viewer/lib/prepare_tables.coffee这个脚本所完成,如果你遵照源代码中的README文件的指令进行操作,就会自动调用这个脚本。
火星探测器应用程序使用了目前非常流行的web开发框架AngularJS。从本质上说,该web应用的每个视图都是由对应的controller所创建的:timeline视图对应着一个timeline controller,而favorites视图对应着一个favorites controller,等等。这些controller都会使用一个通用的Amazon DynamoDB服务与DynamoDB中的数据表进行通信。这个MarsPhotoDBAccessservice服务位于viewer/app/scripts/services目录下,其中包含了应用程序中所有的查询与更新操作。queryWithDateIndex函数则用到了文档级别的JavaScript SDK,使得对项目的访问更加简便与直接:
与之类似的是,可以对vote GSI进行查询请求,以允许用户对图片按照投票数量进行倒序排列:
更新执行
对图片进行投票的工作方式与查询非常类似,区别仅在于我们需要对表中的现有项目进行更新操作。不过在那之前,我们首先需要检查一下,该用户之前是否已经为同样的图片投过票了。可以通过对DynamoDB中的第二张表userVotes进行一行条件式写入实现这一功能,创建这张表的目的是对已经为图片投过票的用户信息进行追踪。如下图所示,可以使用Expected参数进行条件设置。
在以上代码片段中,我们设定的期望是在表中不存在这个指定的imageid与userid的组合,因为这应该是该用户首次对某张图片进行投票。之后,在满足了该条件的情况下,我们就尝试将该项目加入userVotes表中。
当检查过程结束之后,我们可以通过JSON文档SDK对“marsDemoImages”表中的投票总数进行更新,该SDK允许用户以一种简便且直接的方法对个别的JSON字段进行更新。让我们来看一下incrementVotesCount这个函数是如何运行的:
请注意:“UpdateExpression”和“ExpressionAttributeValues”这两个参数是由JSON文档SDK所引入的,它们提供了对JSON数据更多的访问方式。要想了解更多细节信息,请参考GitHub上的awslabs帐号下的代码库中与修改项目属性相关的文档。
对DynamoDB中的数据表进行查询之后,JSON结果就返回给浏览器端,此时可以从Amazon S3中获取图片的缩略图。我们目前在在线的示例网站中采取的就是这种实现方式。不过,我们也可以选择将所有的缩略图二进制数据保存在DynamoDB里,在每个项目的“data”属性中。DynamoDB能够保存各种二进制数据,而无需指定它的类型、限制或schema,只要该数据不作为哈希键或范围键属性,并且满足每个项目最大400KB的限制就可以了。我们之所以在这个公开的在线示例中选择从S3中获取图片,而不是直接从DynamoDB获取,原因是为了减少大量读取所带来的成本。不过DynamoDB Free Tier版本为用户提供了25GB的免费存储空间,并且读取与定入的最大限制为25。如果你能够动手实验一下,创建属性自己的web应用程序和DynamoDB,那是再好不过了。
本文为读者介绍了如何使用Amazon DynamoDB服务创建一个火星探测器应用。你也可以应用本文中所介绍的概念来打造你自己的web应用程序。让我们来回顾一下整个应用开发的过程:
Daniela Miao是一位负责Amazon Web Services的软件开发工程师,在DynamoDB开发者生态系统团队中工作。该团队的工作目标是为了改善DynamoDB的客户体验,通过编写各种类库与工具以简化DynamoDB应用程序的编写。她希望能通过诸如示例讲解、示例应用、博客帖子等等方式为开发者传授知识,减少他们在使用DynamoDB时可能会遇到的各种阻碍。如果你在DynamoDB的使用方法有任何疑问和建议,或者希望了解更多的相关信息,请通过[email protected]这个邮件与她取得联系。
Kenta Yasukawa是Amazon Web Services (AWS)的一位高级解决方案架构师。他专注于为游戏商、移动应用后台、社交网络服务等客户设计基于云的解决方案。他对于利用AWS云的各种功能,设计具备高伸缩性和可靠性的架构有着很高的热情。Amazon DynamoDB是整个架构设计中的关键组件,他已经看到有许多客户通过使用Amazon DynamoDB成功地创建了各种具备高伸缩性和可靠性的架构。如果你希望了解这些成功的故事,欢迎与我们进行联系。
查看英文原文:Building a Mars Rover Application with DynamoDB