在 DynamoDB 命令行管理程序中对批量操作进行限

DynamoDB Shell (ddbsh) 是 亚马逊 DynamoDB 的开源命令行接口。 有关简单介绍,请参阅 使用 DynamoDB Shell 查询数据,这是亚马逊 DynamoDB 的命令行界面 ddbsh Readme.md 文件包含 详细的命令和用法 示例。ddbsh 的目标之一是为 DynamoDB 的新手提供一个简单直观的环境,使他们能够通过运行熟悉的类似 SQL 的命令开始使用。

DynamoDB 提供用于更新、删除和替换项目的 API,但所有这些仅对一个项目进行操作。但是,您可能需要对许多项目进行数据维护。为此,您必须使用 DynamoDB API 编写定制应用程序,提取要修改的项目,然后逐一执行修改。此外,您还必须实现代码,确保这些操作不会影响前台应用程序对相同表的使用。DynamoDB Shell 提供了一种简单的方法来做到这一点。它支持类似 SQL 的结构,这些结构可以对表中的许多或所有项目进行操作,并以受控的方式执行 UPDATE、DELETE 或 REPLACE 操作。

请注意,ddbsh 是按原样提供给您使用的,不支持生产用例。它只能用于非生产和实验用例。有关更多详细信息 , 请参阅许可证 第 7 节。

ddbsh 可以直接对您的 DynamoDB 表进行操作,因此删除和删除会影响您的表,并且这些操作是不可逆的。ddbsh 可以对您的数据执行扫描和查询,您执行的操作会占用您的表容量,并可能产生大量成本。在这篇文章中,我们将向您展示如何在 ddbsh 中限制批量操作的速率。

有关如何限制对特定 API 的访问权限的详细信息,请参阅 DynamoD B 中的 精细访问控制

强烈建议你了解 ddbsh 在做什么,然后先 试用 DynamoDB Local

示例

让我们来看一些简单的例子。为此,我们使用带有 全球二级索引 (GSI) 的表。 回想一下,DynamoDB Shell 提供 DDL 支持,并且在以下示例中创建的表将设置为按需计费模式(默认值,因为未指定任何内容):

us-east-1> create table bulkops ( a number, b number, c number ) primary key ( a hash ) gsi (bulkgsi on (b hash, c range) projecting all );

我们在其中填充了 2,000 个项目,每个项目看起来都像下面的代码:

insert into bulkops ( a, b, c, d ) values 
( 0, 1, 2, [… long string …] ),
[…]
( 1999, 2000, 2001, [… long string …] );

每个长字符串的长度为 5,000 个字符。读取此表中的所有数据最终会消耗 1227.5 个 读取容量单位 (RC U):

us-east-1> select * from bulkops return total;
[… 2000 rows of data …]
SELECT (bulkops, 1227.5, 0, 0)
us-east-1>

即使我们只获取了三个数字(a、b、c),它也会消耗相同数量的 RCU,因为扫描必须读取整个项目。此外,整个 SELECT 操作只用了 1 秒多:

$ /usr/bin/time ddbsh -c 'select a, b, c from bulkops return total' | tail -n 5
        1.18 real         0.06 user         0.02 sys
{a: 1076, b: 1077, c: 1078}
{a: 1209, b: 1210, c: 1211}
{a: 641, b: 642, c: 643}
{a: 735, b: 736, c: 737}
SELECT (bulkops, 1227.5, 0, 0)
$

速率限制简介

假设我们要确保此命令在一秒钟内消耗的 RCU 不会超过 50 个。为此,我们为 SELECT 操作 添加了 速率限制

$ /usr/bin/time ddbsh -c 'select a, b, c from bulkops with ratelimit (50 rcu)' | tail -n 5
       23.32 real         0.07 user         0.02 sys
{a: 99, b: 100, c: 101}
{a: 1076, b: 1077, c: 1078}
{a: 1209, b: 1210, c: 1211}
{a: 641, b: 642, c: 643}
{a: 735, b: 736, c: 737}
$

该命令现在花了 23 秒钟才完成!

以下是使用 -q (qui et)命令行选项并将结果通过管道传送到行计数器 ( wc-l ) 的类似命令。第一个没有速率限制(需要 1.05 秒),第二个有 50 个 RCU 限制(需要 23.39 秒),最后一个有 10 个 RCU 限制(需要 115.33 秒)。它们全部产生 2,000 行输出。

$ /usr/bin/time ddbsh -q -c 'select a, b, c from bulkops' | wc -l
        1.17 real         0.07 user         0.02 sys
    2000
$ /usr/bin/time ddbsh -q -c 'select a, b, c from bulkops with ratelimit (50 rcu)' | wc -l
       23.39 real         0.06 user         0.02 sys
    2000
$ /usr/bin/time ddbsh -q -c 'select a, b, c from bulkops with ratelimit (10 rcu)' | wc -l
      115.33 real         0.06 user         0.02 sys
    2000
$

速率限制的工作原理

DynamoDB Shell 中的速率限制是使用简单的令牌桶算法实现的。代币桶以预先确定的速率累积代币。只有当存储桶中的代币数为正数时,才允许执行操作。操作完成后,将计算其消耗的资源,并将相应数量的令牌从存储桶中移除(允许剩余令牌的数量变为负数)。代币的数量绝不能超过1秒的价值。有关令牌存储桶的完整实现,请参阅 GitHub 存储库

对于每条命令,DynamoDB Shell 都会实现两个令牌存储桶。一个用于读取令牌,一个用于写入令牌。因此,您可以按如下方式进行限速更新:

$ /usr/bin/time ddbsh -d -q -c "update bulkops set updated = true with ratelimit ( 10 rcu, 30 wcu )"
UPDATE (2000 read, 2000 modified, 0 ccf)
      666.66 real         4.03 user         1.77 sys
$

写作花了 11 分钟以上,更新了所有 2,000 个项目。没有速率限制的相同更新耗时不到 5 秒:

$ /usr/bin/time ddbsh -d -q -c "update bulkops set updated = true"
UPDATE (2000 read, 2000 modified, 0 ccf)
      4.85 real         1.20 user         0.51 sys
$

查询可以指定读取限制、写入限制或同时指定这两个限制。速率限制条款的语法如下:

ratelimit := WITH RATELIMIT ( RR RCU, WW WCU ) |
             WITH RATELIMIT ( RR RCU ) |
             WITH RATELIMIT ( WW WCU )

使用索引更新

假设我们要更新表格 bulkops 并将 b = 30 的所有项目设置 如下:

UPDATE bulkops
SET found = true
WHERE b = 30;

此查询需要对表进行扫描。但是有办法让这变得更容易,因为我们有 GSI。这是 SQL 的扩展——您可以将索引指定为更新目标。实际上,这只会更新表,但它会使用索引来查找要更新的项目,如以下代码所示。bulk ops.bulkgsi 的更新 允许 DynamoDB Shell 对索引执行查询,并使用在那里找到的值来执行单次更新。

us-east-1> explain update bulkops.bulkgsi set found = true where b = 30;
Query({
   "TableName":   "bulkops",
   "IndexName":   "bulkgsi",
   "ConsistentRead":   false,
   "ReturnConsumedCapacity":   "NONE",
   "ProjectionExpression":   "#afaa1",
   "KeyConditionExpression":   "#afaa2 = :vfaa1",
   "ExpressionAttributeNames":   {
      "#afaa1":   "a",
      "#afaa2":   "b"
   },
   "ExpressionAttributeValues":   {
      ":vfaa1":   {
         "N":   "30"
      }
   }
})
UpdateItem({
   "TableName":   "bulkops",
   "Key":   {
      "a":   {
         "N":   "29"
      }
   },
   "UpdateExpression":   "SET #aeaa1 = :veaa1",
   "ConditionExpression":   "attribute_exists(#aeaa2) AND #aeaa3 = :veaa2",
   "ExpressionAttributeNames":   {
      "#aeaa1":   "found",
      "#aeaa2":   "a",
      "#aeaa3":   "b"
   },
   "ExpressionAttributeValues":   {
      ":veaa1":   {
         "BOOL":   true
      },
      ":veaa2":   {
         "N":   "30"
      }
   }
})
us-east-1> update bulkops.bulkgsi set found = true where b = 30;
UPDATE (1 read, 1 modified, 0 ccf)
us-east-1> select a, b, c, found from bulkops.bulkgsi where b = 30;
{a: 29, b: 30, c: 31, found: TRUE}
us-east-1>

观察到,针对索引的查询会投射表的关键属性,该属性在随后的 updateItem 调用中使用。

完成后记得删除表格。

us-east-1> drop table bulkops;
DROP
us-east-1>

结论

DynamoDB Shell 提供了一些类似 SQL 的结构和扩展,允许您执行具有速率限制的批量更新、删除和替换操作。这种速率限制可确保操作消耗的 RCU 和 WCU 不会超过一定数量。当你想在不影响可能进入这些表的其他流量的情况下执行这些操作时,这可能很有用。

如果您对 DynamoDB Shell 有任何疑问或改进建议,请联系我们或提供反馈。如果您想进一步了解如何使用 DynamoDB 进行经济实惠的批量处理,请参阅 此博客文章


作者简介

Amrith Kumar 是亚马逊网络服务的高级首席工程师,在亚马逊 DynamoDB 上工作。


*前述特定亚马逊云科技生成式人工智能相关的服务仅在亚马逊云科技海外区域可用,亚马逊云科技中国仅为帮助您发展海外业务和/或了解行业前沿技术选择推荐该服务。