微信号:infoqchina

介绍:有内容的技术社区媒体

使用DynamoDB打造应用,看“好奇号”在火星找到了什么?

2015-03-23 11:45 邵思华 译


DynamoDB是一个高效而灵活的NoSQL数据库服务,它的主要特色就是便于管理,因此使用者不必担心各种管理方面的任务负担,例如操作和扩展数据库等等。使用者可以更关注于应用程序的设计,并且在通过几个简单的步骤后将其发布到DynamoDB服务上。


在本文中,我们将为你展示如何使用Amazon DynamoDB创建一个应用程序。


火星探测器应用程序


我们在本文中所讲述的示例应用将为读者展示DynamoDB数据库的强大功能。这一web应用展现了NASA向公众开放的数据,即好奇号火星探测器(Curiosity Mars Rover)从火星所发回的图像,以及用JSON格式描述的图像元数据。以下是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,... )


这张表的每个项目都可以包含各种其它的属性,以下是一些属性的示例:

marsDemoImages表中的一些示例项目


请注意:在本例中,“imageid”是唯一一个必需的属性,其它所有属性都能够自动从NANA的JSON图片数据集中进行导入。实际上,在这个示例中,101这个项目并不包含“camera_model”这个属性。"Mission+InstrumentID"则是一个混合属性,在接下来的一节将对此进行解释。


主键


当你创建一张表时,你必须指定该表的主键。DynamoDB支持以下两种类型的主键:


哈希类型主键:

这种类型的主键由一个哈希属性所构成。在之前的示例中,marsDemoImages表中的哈希属性就是“imageid”,正如下图所示。

marsDemoImages中的部分项目示例,其中的主哈希键高亮显示


哈希及范围类型主键:

这种类型的主键由两个属性所构成。第一个属性是哈希属性,而第二个属性是范围属性。在火星探测器这个示例中,假设我们打算首先以“imageid”字段、随后以“votes”字段对项目进行分组。那么哈希属性就是“imageid”,而范围属性则是“votes”。

marsDemoImages中的部分项目示例,其中的主哈希与范围键高亮显示


查询、更新与扫描


除了使用主键对特定的项目进行访问与操作之外,Amazon DynamoDB也提供了多种方式对特定的数据进行搜索:即查询、更新与扫描。


查询:

查询操作仅使用主键属性的值查找某张表中的特定项目。你必须提供一个哈希键的属性-值对,并可选择地提供一个范围键的属性-值对。


举例来说,在火星探测器应用中,我们可以通过“imageid = 201”这样的键-值对来查询某张特定的图片。


更新:

更新操作与查询操作相类似,区分就在于你能够修改项目的属性了。条件式更新允许你在某个特定的条件满足之后,才能够对项目进行修改。稍后我们将看到这方面的一个示例,我们将对火星探测器应用中图片的投票数进行更新。


扫描:

一次扫描操作将对整张表中的每个项目进行分析。在默认情况下,一次扫描操作会返回每个项目中的所有数据属性。


二级索引


对整张表进行扫描在某些情况下会降低效率,为了避免这种情况,我们可以创建二级索引,以辅助查询的处理。表中的二级索引能够帮助优化对非键属性的查询。DynamoDB支持两种类型的二级索引:

本地二级索引:该索引持有一个与表相同的哈希键,但有一个不同的范围键。


全局二级索引:该索引持有一个哈希键与一个范围键,它们的值可以与表中的对应值不同。


可以将二级索引想象为额外的表,它们首先由哈希键进行、再由范围键进行分组。举例来说,在marsDemoImages表中,我们可能需要查找来自于某个特定任务与拍摄仪器的图片,并按照某个时间范围进行过滤。因此我们就可以创建一个二级索引,让它首先按照“Mission+Instrument”属性(哈希键)进行分组,随后按照“TimeStamp”属性(范围键)进行分组。下图是该索引的一个示例,在下一节关于marsDemoImage表的介绍中,我们还将详细分析二级索引的更多细节。

marsDemoImages表中某个二级索引的示例


数据类型与JSON支持


Amazon DynamoDB支持一系列新的数据类型:

标量类型:数字、字符串、二进制、布尔和Null。


多值类型:字符串集、数字集和二进制集。


文件类型:List和Map。


比方说,在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帐号下找到完全公开的源代码。


为了理解这个应用程序的运行原理,让我们来看一看下图所示的火星探测器应用程序的整体架构,我们将一步一步地为你讲解每个组件的作用。

火星探测器的设计架构


浏览器客户端从Amazon S3获取应用代码


当用户开始访问火星探测器示例的应用程序网站时,浏览器就会从Amazon S3获取应用程序代码,包括HTML、CSS和JavaScript。通过使用DynamoDB和S3,我们就能够在客户端完整地运行整个应用程序,就样就能够避免自己管理服务器的各种麻烦。


应用程序将通过Amazon Cognito对用户进行验证


在这一步骤中,应用程序将通过Amazon Cognito对用户进行授权,让用户得以访问DynamoDB表。Amazon Cognito是一个简单的用户认证与数据同步服务,它能够在未认证访客与DynamoDB之间建立关联,允许任意用户对该应用程序所对应的DynamoDB数据表进行查询,并且对表中的某些属性进行有限制的更新操作。如果你打算自己部署这个示例,那么你也可以使用DynamoDB Local将整个应用程序运行在你自己的本地机器上,以进行开发或测试工作。在GitHub上的火星探测星应用源代码的README文档中可以找到在本地运行这一应用的操作指南。


让我们回到这个在线示例的认证环节,通过使用Amazon Cognito,我们就能够轻易地管理访问对DynamoDB数据表的访问,并收集访问者的数量等相关的统计信息。

Cognito统计数据界面的截图示例


通过使用Amazon Cognito,你就可以使用各种公开的登录提供者,例如Amazon、Facebook和Google,或是使用自己的用户身份系统为访问用户创建独立的用户身份信息,以访问AWS云服务。用户也可以选择作为未认证的访客身份访问你的应用。我们在这里使用了未认证访客访问特性,为web浏览器提供AWS身份信息,并对每个用户进行唯一识别。我们按照下面所列出的步骤将应用程序部署到生产环境中,你也可以用同样的步骤将你自己的应用程序进行部署:

1,为该应用程序创建一个Amazon Cognito身份池,这一步可以在Amazon Cognito的管理控制台中完成。你可以选择使用默认设置,只需确保“允许非认证身份访问”被选中即可。


2,对AWS身份与访问管理(IAM)进行配置,对于该示例应用运行所需的最小权限进行授权:


3,对marsDemoImages表进行读取


4,使用date-gsi与votes-gsi进行查询


5,GetItem


6,对marsDemoImages进行写入


7,更新votes字段


8,对userVotes表进行读取


9,查询属于用户自己的项目,但不可查询其他人的项目


10,对userVotes表进行写入


11,对属于用户自己的项目进行PutItem操作,但不可操作其他人的项目


12,对应用的配置进行修改,以使用Amazon Cognito服务


对DynamoDB进行查询与更新


用户可以根据日期、投票数和已保存图片等不同选项对图片进行选择。每次选择都是对DynamoDB数据表和索引的一次查询。为了充分理解这个过程的工作原理,我们需要深入了解DynamoDB的某些方面。


表schema和GSI的配置


让我们首先来创建一张DynamoDB数据表!你可以通过AWS管理控制台,或是AWS开发SDK来完成这一任务。我们在这个示例中使用了由CoffeeScript生成的JavaScript文件,可在/viewer/lib/prepare_tables.coffee找到源代码。其中最重要的部分是对DynamoDB 数据表的schema的描述,以及GSI的配置,该数据表用于保存图片数据:

marsDemoImages的表schema


我们决定将“Mission”与“InstrumentID”这两个数据字段进行组合,以允许对多个属性进行同时查询。由于应用程序中的每个视图通常都对应着某个特别任务的一个仪器,因此可以选择专注于“Mission”与“InstrumentID”,使用这个组合属性作为GSI的哈希键,并且另外选择一个属性作为GSI的范围键。举例来说,用户可以在火星探测器的进行探险任务时,从“前端避险摄像头”仪器获取所有的图片,并根据日期进行过滤。GSI就能够提供这种类型的查询,该表中的GSI如下图所示:

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:

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帐号下的代码库中与修改项目属性相关的文档。


从Amazon DynamoDB中获取缩略图


对DynamoDB中的数据表进行查询之后,JSON结果就返回给浏览器端,此时可以从Amazon S3中获取图片的缩略图。我们目前在在线的示例网站中采取的就是这种实现方式。不过,我们也可以选择将所有的缩略图二进制数据保存在DynamoDB里,在每个项目的“data”属性中。DynamoDB能够保存各种二进制数据,而无需指定它的类型、限制或schema,只要该数据不作为哈希键或范围键属性,并且满足每个项目最大400KB的限制就可以了。我们之所以在这个公开的在线示例中选择从S3中获取图片,而不是直接从DynamoDB获取,原因是为了减少大量读取所带来的成本。不过DynamoDB Free Tier版本为用户提供了25GB的免费存储空间,并且读取与定入的最大限制为25。如果你能够动手实验一下,创建属性自己的web应用程序和DynamoDB,那是再好不过了。


总结


本文为读者介绍了如何使用Amazon DynamoDB服务创建一个火星探测器应用。你也可以应用本文中所介绍的概念来打造你自己的web应用程序。让我们来回顾一下整个应用开发的过程:

设计你自己的DynamoDB表,包括schema、主哈希键、以及(可选的)范围键,以及二级索引。


使用AWS管理控制台,或使用我们的AWS SDK创建表与索引。在这个示例中,我们所使用的是JavaScript SDK。


选择你打算使用的语言和web开发框架,我们所选择的是JavaScript语言及AngularJS框架。


开始为应用进行编码,编写对DynamoDB数据表进行查询或更新的各种函数。如果你选择使用JSON并使用我们提供的文档级别的SDK,那么这一过程将变得非常容易。


最后,发布你的应用吧!




如果想要评论本篇文章,想看下其他读者都有什么话想说,欢迎点击“阅读原文”参与讨论。

 
InfoQ 更多文章 Facebook如何实现PB级别数据库自动化备份 学术派Google软件工程师Matt Welsh谈移动开发趋势 Spotify为什么要使用一些“无聊”的技术? 妹纸们放假了,汉纸们做啥? 大多数重构可以避免
猜您喜欢 已有4万多技术员抢到新年红包!没抢的赶紧啦~ 40岁的程序员你该怎么办? PHP语言基础简单整理 年轻人为什么要奋斗 敏捷破冰之旅(七)