DynamoDB作为AWS上使用最广泛的NoSQL数据库之一,其按需计费、自动扩容的特性让它非常适合处理高并发的键值型数据。在Ruby生态里,官方提供的aws-sdk-dynamodb gem封装了完整的API,使用起来比直接签名HTTP请求省心得多。这篇文章将从环境搭建开始,逐步讲解如何用Ruby完成DynamoDB的建表、写入、查询、更新和删除等操作,并分析一些容易踩坑的细节。

安装与客户端初始化
首先在Gemfile中加入依赖。aws-sdk v3版本按服务拆分成了多个gem,如果只用DynamoDB,安装aws-sdk-dynamodb即可,不必引入庞大的aws-sdk全家桶,这样可以显著减少加载时间和依赖体积。
gem 'aws-sdk-dynamodb' # 然后 bundle install
初始化客户端时需要处理凭证和区域两个问题。凭证的加载顺序是:环境变量、共享凭证文件(~/.aws/credentials)、IAM角色等。在开发环境推荐使用共享凭证文件,生产环境则尽量依赖IAM角色,避免把AccessKey硬编码进代码。区域必须显式指定,否则会抛出ArgumentError。
require 'aws-sdk-dynamodb'
client = Aws::DynamoDB::Client.new(
region: 'ap-northeast-1',
# 本地开发时可以显式传入凭证,生产环境建议省略,交给IAM角色处理
credentials: Aws::Credentials.new('ACCESS_KEY', 'SECRET_KEY')
)除了Client层面的直接调用,sdk还提供了Aws::DynamoDB::Table和Aws::Record两种更高层的抽象。前者偏面向资源的操作方式,后者类似ORM,可以定义模型映射属性。小项目直接用Client最直观,本文也以Client为主线。
创建表与基本数据结构
DynamoDB的表必须定义主键,主键有两种形式:简单的分区键(Partition Key),或者分区键加排序键(Sort Key)的复合主键。创建表时需要指定键的类型,S代表字符串,N代表数字,B代表二进制。下面的例子创建一张以用户ID为分区键、创建时间为排序键的表。
client.create_table(
table_name: 'orders',
attribute_definitions: [
{ attribute_name: 'user_id', attribute_type: 'S' },
{ attribute_name: 'created_at', attribute_type: 'N' }
],
key_schema: [
{ attribute_name: 'user_id', key_type: 'HASH' },
{ attribute_name: 'created_at', key_type: 'RANGE' }
],
# 按需计费模式,无需预置吞吐量
billing_mode: 'PAY_PER_REQUEST'
)注意attribute_definitions只需要列出作为键使用的属性,非键属性不用也无法在这里声明,这是DynamoDB无模式(schemaless)设计的体现。写入数据时,每个属性都要带类型前缀,字符串用s,数字用n。数字在协议层是按字符串传输的,这一点写代码时要留意。
数据的写入与读取
写入单条数据用put_item,它按主键整体覆盖旧值,如果希望保留旧数据,可以先配置return_values为ALL_OLD让服务端返回被覆盖的内容。
client.put_item(
table_name: 'orders',
item: {
'user_id' => 'u_1001',
'created_at' => 1718000000,
'amount' => 259,
'status' => 'paid'
}
)按主键精确读取用get_item,必须提供完整的复合主键,返回结果在item字段里,键不存在时该字段为nil而不是抛异常,判断空值时不要写错。
resp = client.get_item(
table_name: 'orders',
key: { 'user_id' => 'u_1001', 'created_at' => 1718000000 }
)
if resp.item
puts resp.item['status'] # => "paid"
end查询:query与scan的区别
query和scan都能取回多条数据,但底层机制完全不同。query必须指定分区键的等值条件,可以配合排序键做范围过滤,只会访问单个分区,效率高、消耗的读取容量小;scan则会遍历整张表的所有分区,数据量大时既慢又贵。除非是需要导出全表或者做后台统计,业务代码中应尽量用query。
resp = client.query(
table_name: 'orders',
key_condition_expression: '#u = :uid AND #t >= :ts',
expression_attribute_names: {
'#u' => 'user_id', '#t' => 'created_at'
},
expression_attribute_values: {
':uid' => 'u_1001', ':ts' => 1710000000
},
limit: 20
)
resp.items.each { |item| puts item['amount'] }这里的expression_attribute_names用#开头占位符代替属性名,是为了避免属性名与DynamoDB保留字冲突,status、size等常见单词都是保留字,直接写会报验证错误,用占位符是最稳妥的做法。当结果超过1MB时,响应会带last_evaluated_key,把它作为下次请求的exclusive_start_key传入即可实现分页。
条件更新、删除与本地调试技巧
update_item支持原子性的条件更新,比如只在订单还是待支付状态时才允许改成已发货,条件不满足时会抛出ConditionalCheckFailedException,配合begin rescue处理即可实现乐观锁语义。
begin
client.update_item(
table_name: 'orders',
key: { 'user_id' => 'u_1001', 'created_at' => 1718000000 },
update_expression: 'SET #s = :new_status',
condition_expression: '#s = :old_status',
expression_attribute_names: { '#s' => 'status' },
expression_attribute_values: { ':new_status' => 'shipped', ':old_status' => 'paid' }
)
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException
puts '状态已被其他请求修改'
end批量场景下,batch_write_item一次最多写25条,batch_get_item一次最多读100条,比循环调用单条接口节省大量网络往返。删除单条用delete_item,同样支持条件表达式。
本地开发时不必连接线上环境,官方提供了DynamoDB Local,用Docker一行命令即可启动,然后把endpoint指向本地端口并使用假凭证即可,这样测试既快又不产生任何费用。
# docker run -p 8000:8000 amazon/dynamodb-local
client = Aws::DynamoDB::Client.new(
region: 'us-east-1',
endpoint: 'http://127.0.0.1:8000',
credentials: Aws::Credentials.new('local', 'local')
)掌握这些核心接口之后,绝大多数DynamoDB操作都能顺手完成。建议在项目里把Client封装成统一的仓库类,把表名和表达式细节集中管理,代码可维护性会好很多。
DynamoDBaws-sdk-rubyRuby修改时间:2026-09-04 17:12:40