29

在我的数据库方案中,我需要一个自动增量主键。我怎样才能实现这个功能?

PS 为了访问 DynamoDB,我使用 dynode ,Node.js的模块。

4

10 回答 10

27

免责声明:我是 Dynamodb-mapper 项目的维护者

自动增量键的直观工作流程:

  1. 获取最后一个计数器位置
  2. 加 1
  3. 使用新数字作为对象的索引
  4. 保存新的计数器值
  5. 保存对象

这只是为了解释潜在的想法。永远不要这样做,因为它不是原子的。在某些工作负载下,您可以将相同的 ID 分配给 2+ 个不同的对象,因为它不是原子的。这将导致数据丢失。

解决方案是使用原子 ADD操作以及UpdateItemALL_NEW

  1. 原子地生成一个 ID
  2. 使用新数字作为对象的索引
  3. 保存对象

在最坏的情况下,应用程序在保存对象之前崩溃,但绝不会冒险两次分配相同的 ID。

还有一个问题:在哪里存储最后一个 ID 值?我们选择了:

{
    "hash_key"=-1, #0 was judged too risky as it is the default value for integers.
    "__max_hash_key__y"=N
}

当然,为了可靠地工作,所有插入数据的应用程序都必须知道这个系统,否则你可能(再次)覆盖数据。

最后一步是自动化该过程。例如:

When hash_key is 0:
    atomically_allocate_ID()
actual_save()

有关实现细节(Python,抱歉),请参阅https://bitbucket.org/Ludia/dynamodb-mapper/src/8173d0e8b55d/dynamodb_mapper/model.py#cl-67

说实话,我的公司不会在生产中使用它,因为大多数时候最好为用户找到另一个密钥,例如,对于用户,一个 ID,对于一个交易,一个日期时间,......

我在dynamodb-mapper 的文档中写了一些示例,它可以很容易地外推到 Node.JS

如果您有任何问题,请随时提问。

于 2012-08-19T22:59:00.263 回答
14

另一种方法是使用UUID主键生成器,因为它们不太可能发生冲突。

IMO,您更有可能在高可用DynamoDB表中合并主键计数器时遇到错误,而不是在生成UUID的 s 中发生冲突。

例如,在节点中:

npm install uuid

var uuid = require('uuid');

// Generate a v1 (time-based) id
uuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'

// Generate a v4 (random) id
uuid.v4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1'

取自 SO answer

于 2017-08-01T17:10:45.873 回答
5

如果您对递增 id 中的间隙感到满意,并且只对与添加行的顺序大致对应的情况感到满意,您可以自己滚动:创建一个名为 NextIdTable 的单独表,带有一个主键 (数字),称它为计数器。

每次你想生成一个新的 id 时,你会做以下事情:

  • 在 NextIdTable 上执行 GetItem 以读取 Counter 的当前值 --> curValue
  • 在 NextIdTable 上执行 PutItem 以将 Counter 的值设置为 curValue + 1。将此设置为条件 PutItem,这样如果 Counter 的值发生更改,它将失败。
  • 如果该条件 PutItem 失败,则意味着其他人与您同时执行此操作。重来。
  • 如果成功,则 curValue 是您的新唯一 ID。

当然,如果您的进程在实际应用该 ID 之前崩溃,您将“泄漏”它并在您的 ID 序列中出现间隙。如果您与其他进程同时执行此操作,则其中一个将获得值 39,而你们中的一个将获得值 40,并且无法保证它们将实际应用于您的数据表中的顺序;得到 40 的人可能会在得到 39 的人之前写它。但它确实给你一个粗略的排序。

node.js 中条件 PutItem 的参数在此处详细说明。 http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/frames.html#!AWS/DynamoDB.html。如果您之前从 Counter 中读取了 38 的值,那么您的条件 PutItem 请求可能如下所示。

var conditionalPutParams = {
    TableName: 'NextIdTable',
    Item: {
        Counter: {
            N: '39'
        }
    },
    Expected: {
        Counter: {
            AttributeValueList: [
                {
                    N: '38'
                }
            ],
            ComparisonOperator: 'EQ'
        }
    }
};
于 2014-06-21T21:44:40.037 回答
5

除了@yadutaf 的回答

AWS 支持原子计数器

创建一个单独的表 ( order_id),其中一行包含最新的 order_number:

+----+--------------+
| id | order_number |
+----+--------------+
|  0 |         5000 |
+----+--------------+

这将允许增加 order_number1并在 AWS DynamoDB 的回调中获得增加的结果:

config={
  region: 'us-east-1',
  endpoint: "http://localhost:8000"
};
const docClient = new AWS.DynamoDB.DocumentClient(config); 

let param = {
            TableName: 'order_id',
            Key: {
                "id": 0
            },
            UpdateExpression: "set order_number = order_number + :val",
            ExpressionAttributeValues:{
                ":val": 1
            },
            ReturnValues: "UPDATED_NEW"
        };
        
       
docClient.update(params, function(err, data) {
   if (err) {
                console.log("Unable to update the table. Error JSON:", JSON.stringify(err, null, 2));
   } else {
                console.log(data);
                console.log(data.Attributes.order_number); // <= here is our incremented result
    }
  });

请注意,在极少数情况下,它们可能是调用方点和 AWS API 之间的连接问题。这将导致 dynamodb 行增加,而您将收到连接错误。因此,可能会出现一些未使用的递增值。

您可以在表格中使用增量data.Attributes.order_number,例如插入{id: data.Attributes.order_number, otherfields:{}}表格order

于 2017-04-26T10:20:07.510 回答
5

对于那些使用 Java 编码的人,DynamoDBMapper现在可以代表您生成唯一的 UUID。

DynamoDBAutoGeneratedKey

将分区键或排序键属性标记为自动生成。DynamoDBMapper 将在保存这些属性时生成一个随机 UUID。只有字符串属性可以标记为自动生成的键。

像这样使用DynamoDBAutoGeneratedKey注释

@DynamoDBTable(tableName="AutoGeneratedKeysExample")
public class AutoGeneratedKeys { 
    private String id;

    @DynamoDBHashKey(attributeName = "Id")
    @DynamoDBAutoGeneratedKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; } 

正如您在上面的示例中看到的,您可以将 DynamoDBAutoGeneratedKey 和 DynamoDBHashKey 注释应用于同一属性以生成唯一的哈希键。

于 2018-01-10T17:00:47.163 回答
4

我不相信 SQL 风格的自动增量是可能的,因为这些表是在多台机器上分区的。我在 PHP 中生成自己的 UUID 来完成这项工作,我相信你可以在 javascript 中 提出类似的东西。

于 2012-07-30T21:01:13.790 回答
2

我遇到了同样的问题,并为此目的创建了一个小型 Web 服务。请参阅此博客文章,其中解释了我如何将stateful.co与 DynamoDB 一起使用以模拟自动增量功能:http ://www.yegor256.com/2014/05/18/cloud-autoincrement-counters.html

基本上,您在stateful.co注册一个原子计数器,并在每次需要新值时通过 RESTful API 递增它。该服务是免费的。

于 2014-05-19T08:41:38.697 回答
1

从性能的角度来看,自动增量并不好,因为它会使特定的分片过载,同时让其他分片保持空闲,如果您将数据存储到 Dynamodb,它不会进行均匀分布。

awsRequestId看起来它实际上是 V.4 UUID(随机),下面的代码片段尝试一下:

exports.handler = function(event, context, callback) {
    console.log('remaining time =', context.getRemainingTimeInMillis());
    console.log('functionName =', context.functionName);
    console.log('AWSrequestID =', context.awsRequestId);
    callback(null, context.functionName);
};

如果您想自己生成,可以使用https://www.npmjs.com/package/uuidUlide根据RFC-4122生成不同版本的 UUID

  • V1(基于时间戳)
  • V3(命名空间)
  • V4(随机)

对于 Go 开发人员,您可以使用来自Google 的 UUIDPbormanSatori的这些包。Pborman 的性能更好,查看这些文章和基准以了解更多详细信息。

可以在此处找到有关通用唯一标识符规范的更多信息。

于 2019-05-14T05:20:36.627 回答
0

创建新的file.js并输入以下代码:

exports.guid = function () {
    function _p8(s) {
        var p = (Math.random().toString(16)+"000000000").substr(2,8);
        return s ? "-" + p.substr(0,4) + "-" + p.substr(4,4) : p ;
    }
    return (_p8() + _p8(true) + _p8(true)+new Date().toISOString().slice(0,10)).replace(/-/g,"");
}

然后您可以将此功能应用于主键ID。它将生成 UUID。

于 2016-12-13T11:57:19.697 回答
0

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

// 用户.modal.js

const dynamoose = require("dynamoose");

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

const { v4: uuidv4 } = require("uuid");    
const User = require("./user.model");

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);
};
于 2020-07-15T19:08:31.720 回答