如何在dynamodb中使用自动增量作为主键ID


Answers:


62

DynamoDB不提供此功能。您可以在应用程序中生成某些东西,例如UUID,对于大多数系统来说,“应该”足够独特。

我注意到您正在使用Node.js(删除了标签)。这是提供UUID功能的库:node-uuid

自述文件中的示例

var uuid = require('node-uuid');
var uuid1 = uuid.v1();
var uuid2 = uuid.v1({node:[0x01,0x23,0x45,0x67,0x89,0xab]});
var uuid3 = uuid.v1({node:[0, 0, 0, 0, 0, 0]})
var uuid4 = uuid.v4();
var uuid5 = uuid.v4();

2
FWIW我将这种方法(UUID作为哈希键)与Dynamo一起使用,并且效果很好。
rpmartz

8
该答案应标记为正确答案。还值得注意的原因:您想要统一分配密钥,并且自动递增会导致分配不均匀。请参阅本文的详细信息:forums.aws.amazon.com/thread.jspa?messageID=312527和AWS文档在这里:docs.aws.amazon.com/amazondynamodb/latest/developerguide/...
巷瑞特格

使用uuid,因为不建议使用node-uuid。
–node_saini

54

这是DynamoDB中的反模式,可在许多分区/分片/服务器之间进行扩展。由于缩放限制,DynamoDB不支持自动增量主键,因此无法在多台服务器之间保证。

更好的选择是从多个索引中组合主键。主键最多可以为2048个字节。有几种选择:

  1. 使用UUID作为密钥-可能是基于时间的UUID,这使其唯一,均匀分布并带有时间值
  2. 使用随机生成的数字或时间戳+随机(可能是移位),例如:ts << 12 + random_number
  3. 使用其他服务或DynamoDB本身生成增量唯一ID(需要额外的调用)

以下代码将在DynamoDB中自动增加计数器,然后您可以将其用作主键。

var documentClient = new AWS.DynamoDB.DocumentClient();
var params = {
  TableName: 'sampletable',
  Key: { HashKey : 'counters' },
  UpdateExpression: 'ADD #a :x',
  ExpressionAttributeNames: {'#a' : "counter_field"},
  ExpressionAttributeValues: {':x' : 1},
  ReturnValues: "UPDATED_NEW" // ensures you get value back
};
documentClient.update(params, function(err, data) {});
// once you get new value, use it as your primary key

我个人最喜欢的是使用时间戳和随机方法,该方法由Instagram的Sharding ID生成启发而来,网址为http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram

以下函数将为特定分片生成ID(作为参数提供)。这样,您就可以拥有唯一的密钥,该密钥是从时间戳(碎片号)组合而成的。和一些随机性(0-512)。

var CUSTOMEPOCH = 1300000000000; // artificial epoch
function generateRowId(shardId /* range 0-64 for shard/slot */) {
  var ts = new Date().getTime() - CUSTOMEPOCH; // limit to recent
  var randid = Math.floor(Math.random() * 512);
  ts = (ts * 64);   // bit-shift << 6
  ts = ts + shardId;
  return (ts * 512) + randid;
}
var newPrimaryHashKey = "obj_name:" + generateRowId(4);
// output is: "obj_name:8055517407349240"

1
您能否在末尾添加有关第二点和代码的更多详细信息?被subId认为是一个碎片ID或东西吗?
安德拉姆(Andrhamm)

@andrhamm它虽然看起来像是碎片ID 4?参考文献使用公式userId%shardTotal(13位)。
伊莱·彼得斯

1
请解释一下移位的用法?
rangfu

2
@vladaman使用 var randid = Math.floor(Math.random() * 512); ... randid % 512 它的目的是在第一行提供0到511之间的数字。对此类数字使用模512不会更改该数字。
BennyHilarious

请记住,由于您不知道随机部分,因此无法通过id(如instagram的示例)检索时间戳。
Mark Hkr

1

您可能可以使用AtomicCounters

借助AtomicCounters,您可以使用UpdateItem操作来实现原子计数器-一种无条件递增的数字属性,而不会干扰其他写入请求。(所有写入请求都按照接收顺序进行应用。)使用原子计数器,更新不是幂等的。换句话说,每次调用UpdateItem时,数值都会增加。

您可能使用原子计数器来跟踪网站的访问者数量。在这种情况下,您的应用程序将增加一个数字值,而不考虑其当前值。如果UpdateItem操作失败,则应用程序可以简单地重试该操作。这可能会导致两次更新计数器的风险,但是您可能可以容忍网站访问者的计数过高或计数过低的情况。


1
这可能会导致热键问题,因为一个分区最多可具有3000 IOPS。1个RCU = 1 IOPS 1 WCU = 3 IOPS。同样,将AtomicCounter用作ID生成器会很慢,因为增量是串行执行的。
沉广通

1
总结一下(对我自己和对他人有帮助):@vladaman的答案实际上是在显示这种AtomicCounter技术。就像@ guangtongShen提到的那样,此技术不可扩展!(我只在低强度操作中使用它。例如,“创建项目”的情况很少发生。通常应避免这种方法,而应使用UUID(在vladaman的要求中也提到过)
Dimitry K

0

如果使用的是NoSQL Dynamo DB,然后使用Dynamoose,则可以轻松设置默认唯一ID,这是简单的用户创建示例

// User.modal.js

const dynamoose = require("dynamoose");
const { v4: uuidv4 } = require("uuid");

const userSchema = new dynamoose.Schema(
  {
    id: {
      type: String,
      hashKey: true,
    },
    displayName: String,
    firstName: String,
    lastName: String,
  },
  { timestamps: true },
);

const User = dynamoose.model("User", userSchema);

module.exports = User;

// User.controller.js

exports.create = async (req, res) => {
  const user = new User({ id: uuidv4(), ...req.body }); // set unique id
  const [err, response] = await to(user.save());
  if (err) {
    return badRes(res, err);
  }
  return goodRes(res, reponse);
};
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.