关于DynamoDB

AWS去年的11月份的re:Invent上,AWS发布了DynamoDB Global Table,并且宣称是“first fully-managed multi-master multi-region database in the world”。Global Table可以实现跨多个地区(Region)数据多点写入,多个地区间数据会准实时的同步,用户可以利用这个能力,较为简单的实现全球业务部署和数据访问。一直以来,Dynamo都是Amazon在分布式、数据库领域的重大创新,它曾和Google的BigTable一起开启了一个影响深远的NoSQL时代。在AWS上,Dynamo在和SimpleDB‘合体’(我也不知道用什么词比较合适)后,以产品DynamoDB在云端提供服务。本文将概述AWS上DynamoDB实例的创建、使用、以及一致性问题,具体包括DynamoDB Table、Local/Global Secondary Index、Global Table等。

体验DynamoDB

DynamoDB以Table的方式提供服务,用户在控制台新建一个表,然就可以使用相应语言的API访问它所提供的数据读写服务了。

新建表

这里新建了一个名字为“zhou”的表,包含分区字段uid和分区内排序字段itemid。

Snip20180626_179

新建用户/组

IAM中,新建了一个具有DynamoDB相关所有权限的Group,并在该Group中新建了用户zhou。

Snip20180626_183

访问DynamoDB数据表

这里使用AWS的Python SDK “boto”来访问数据表。先安装boto3,然后配置AWS相关的选项,包括ACCESS KEY、SECRET KEY、Region等。

pip install boto3 cat ~/.aws/credentials [default] aws_access_key_id = YOUR_ACCESS_KEY aws_secret_access_key = YOUR_SECRET_KEY cat ~/.aws/config [default] region=ap-northeast-1

查询单条数据

# cat dynamodbt.py import boto3 dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('zhou') response = table.get_item( Key={ 'uid': 82787, 'itemid': 746 } ) item = response['Item'] print(item) # python dynamodbt.py {u'itemid': Decimal('746'), u'num': Decimal('3'), u'uid': Decimal('82787')}

查询并遍历多条数据

# cat dynamodbt.py import boto3 import json import decimal from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('zhou') response = table.query( KeyConditionExpression=Key('uid').eq(82787) ) for i in response['Items']: print(i['uid'], ":", i['itemid'],":",i['num'])

Local/Global Secondary Index

Global Secondary Index可以在创建表之后添加;Local Secondary Index则只能在创建表的时候添加

DynamoDB索引是以“最终一致”的方式实现的二级索引。基于LSI的查询可以通过参数要求必须返回强一致的数据,但基于GSI的查询只能支持“最终一致性”(参考)。

对于GSI,索引是由DynamoDB自动维护,使用了最终一致性模型的异步的实现。当数据写入或者删除时,正常情况下,索引将会在百毫秒(fraction of a second)的时间内同步到GSI。但如果发生异常,这个时间可能会更长。

创建GSI

Snip20180627_191

使用GSI进行查询

使用索引timestamp_c-itemid-index,查询timestamp_c=1528917390 且 itemid >= 6的记录(更多示例参考):

# cat dynamodbt.py import boto3 import json import decimal from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('zhou') response = table.query( IndexName="timestamp_c-itemid-index", KeyConditionExpression=Key('timestamp_c').eq(1528917390) & Key('itemid').gte(6) ) for i in response['Items']: print(i['uid'], ":", i['itemid'],":",i['num']) # python dynamodbt.py (Decimal('82787'), ':', Decimal('9214'), ':', Decimal('70'))

创建LSI

使用LSI进行查询

# python dynamodbt.py (Decimal('297'), ':', Decimal('7369'), ':', Decimal('392')) # cat dynamodbt.py import boto3 import json import decimal from boto3.dynamodb.conditions import Key, Attr dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('zhou') response = table.query( IndexName="uid-timestamp_c-index-copy", ConsistentRead=True, KeyConditionExpression=Key('uid').eq(297) & Key('timestamp_c').gte(1530017390) ) for i in response['Items']: print(i['uid'], ":", i['itemid'],":",i['num'])

注意

LSI只能在创建表的时候同时创建,之后就不能够再创建了。

Global Table

Global Table是一组跨地区(region)相互同步的一组表,应用可在任何地区读写该表。在DynamoDB底层,是同一个AWS账号下一组互相复制的表。对其中任意一个表进行修改,变化都会同步到其他表。一个新写入的数据,一般需要几秒(within seconds,我并没有验证延迟情况)时间传递到其他节点。如果应用需要强一致读,那么必须在同一个地区进行强一致读和写。如果,写入的数据在一个地区,那么这条数据在另一个地区并不能强一致的读取。对于不同地区,写入的冲突数据,使用“last writer wins”机制去解决(说明:文档中说DynamoDB会makes a best effort to determine the last writer,但是,并没有介绍具体的机制)。

创建一个Global Table

更详细的创建流程可以参考AWS的文档:Creating a Global Table。目前只有比较少的地区支持Global Table,包括US East (N. Virginia), US East (Ohio), US West (Oregon), EU (Frankfurt), EU (Ireland), and Asia Pacific (Singapore)。这里的示例在Singapore、Virginia创建了一个Global Table。

首先,将控制台切换到Singapore地区,创建一个普通的DynamoDB的Table。然后在Table界面,选择“Global Tables”选项卡,先打开Old/new images Stream,再添加需要同步的其他的地区,参考下图:

Snip20180627_198

完成创建后,就可以在多个region写入数据了。

多个地区读写Global Table

在任何地区写入的数据,都会很快传递到其他地区。另外,在Global Table分布的地区也都可以读取到全部的数据,对于全球分布的业务来说,确实一个非常方便的能力。读写的代码和前面读写普通表时相同的,这里只是简单验证一下多个地区可以读写。

# cat getitem_dy.py import boto3 my_session = boto3.session.Session() my_region = my_session.region_name print "Read data from region:",my_region dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('zhou') response = table.get_item( Key={ 'uid': 827 } ) item = response['Item'] print(item) # vi ~/.aws/config # cat ~/.aws/config [default] region=us-east-1 # python getitem_dy.py Read data from region: us-east-1 {u'aws:rep:updatetime': Decimal('1530084809.398001'), u'aws:rep:updateregion': u'us-east-1', u'attr': u'ksiiakthisniskksuam', u'aws:rep:deleting': False, u'uid': Decimal('827')} # vi ~/.aws/config # cat ~/.aws/config [default] region=ap-southeast-1 # python getitem_dy.py Read data from region: ap-southeast-1 {u'aws:rep:updatetime': Decimal('1530084809.398001'), u'aws:rep:updateregion': u'us-east-1', u'attr': u'ksiiakthisniskksuam', u'aws:rep:deleting': False, u'uid': Decimal('827')}

Dynamo的一些技术

DynamoDB是Amazon内部Dynamo的在AWS上输出。十年前,Dynamo论文《Dynamo: Amazon’s Highly Available Key-value Store》中较为详细的介绍背后的一些技术。包括Consistent Hashing实现数据分区从而具备良好的扩展性、使用Quorum实现数据多副本下的强一致读写、Vector Clock辅助判断冲突时数据写入顺序的逻辑关系、Merkle Tree来减少数据不一致时快速定位不一致的KEY、Gossip protocal来实现增减节点时让每个节点都快速被通知到数据物理分布情况(membership)。

Leave a Reply

Your email address will not be published. Required fields are marked *