Як використовувати автоматичне збільшення для ідентифікатора первинного ключа в dynamodb


81

Я новачок у dynamodb. Я хочу автоматично збільшити значення ідентифікатора, коли я використовую putitemз dynamodb.

Чи можна це зробити?


Можливий дублікат Як зробити UUID в DynamoDB?
Clarkie

Відповіді:


65

DynamoDB не надає цього нестандартно. Ви можете створити щось у своєму додатку, наприклад UUID, що "повинно" бути досить унікальним для більшості систем.

Я помітив, що ви використовуєте Node.js (я видалив ваш тег). Ось бібліотека, яка забезпечує функціональність UUID: node-uuid

Приклад з README

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 як хеш-ключ) з "Динамо", і він чудово працював.
rpmartz

8
Цю відповідь слід позначити як правильну відповідь. Також варто зазначити, чому: ви хочете рівномірний розподіл ключів, а автоматичне збільшення призведе до нерівномірного розподілу. Дивіться цю статтю для отримання додаткової інформації: forums.aws.amazon.com/thread.jspa?messageID=312527 та документи AWS тут: docs.aws.amazon.com/amazondynamodb/latest/developerguide/…
Lane Rettig

Використовуйте uuid, оскільки node-uuid вже застаріло.
node_saini

56

Це анти-шаблон у DynamoDB, який будується для масштабування на багатьох розділах / осколках / серверах. DynamoDB не підтримує автоматичне збільшення первинних ключів через обмеження масштабування і не може бути гарантована на декількох серверах.

Кращим варіантом є складання первинного ключа з декількох індексів. Первинний ключ може мати до 2048 байт. Варіантів небагато:

  1. Використовуйте UUID як свій ключ - можливо, часовий UUID, що робить його унікальним, рівномірно розподіленим і несе значення часу
  2. Використовуйте випадково сформоване число або позначку часу + випадковий (можливо, зміщення бітів), наприклад: ts << 12 + random_number
  3. Використовуйте іншу службу або сам DynamoDB для створення додаткового унікального ідентифікатора (потрібен додатковий дзвінок)

Наступний код автоматично збільшить лічильник у 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

Моїм улюбленим є використання позначки часу + випадковий натхнення від генерації ідентифікаторів Sharding ID за адресою http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram

Наступна функція згенерує ідентифікатор для конкретного осколка (надається як параметр). Таким чином, ви можете отримати унікальний ключ, який збирається з позначки часу, номер шранда. і деяка випадковість (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повинен бути ідентифікатор осколка чи щось інше?
andrhamm

@andrhamm Це, звичайно, схоже на ідентифікатор осколка, хоча 4? У статті посилання використовується формула userId% shardTotal (13 біт).
Eli Peters

1
Будь ласка, поясніть, як застосовується зміна бітів?
rangfu

2
@vladaman, в чому сенс його використання, var randid = Math.floor(Math.random() * 512); ... randid % 512 слід вказати число від 0 до 511 у першому рядку. Використання модуля 512 для такого числа не змінює число.
BennyHilarious

Майте на увазі, що при такому підході ви не можете отримати позначку часу з ідентифікатора (як приклад instagram), оскільки ви не знаєте випадкової частини.
Mark Hkr

2

Можливо, ви можете використовувати AtomicCounters .

За допомогою AtomicCounters ви можете використовувати операцію UpdateItem для реалізації атомного лічильника - числового атрибута, який збільшується, безумовно, без втручання в інші запити на запис. (Усі запити на запис застосовуються в тому порядку, в якому вони були отримані.) За допомогою атомного лічильника оновлення не є ідемпотентними. Іншими словами, числове значення збільшується кожного разу, коли ви викликаєте UpdateItem.

Ви можете використовувати атомний лічильник для відстеження кількості відвідувачів веб-сайту. У цьому випадку ваш додаток збільшить числове значення, незалежно від його поточного значення. Якщо операція UpdateItem не вдається, програма може просто повторити спробу. Це може загрожувати оновлення лічильника двічі, але ви, мабуть, можете допустити незначний перерахунок або недолік відвідувачів веб-сайту.


1
Це може спричинити проблему гарячих клавіш, оскільки один розділ може мати не більше 3000 IOPS. 1 RCU = 1 IOPS. 1 WCU = 3 IOPS. Також буде повільно використовувати AtomicCounter як генератор ідентифікаторів, оскільки прирости виконуються послідовно.
Guangtong Shen

1
Щоб підсумувати (для себе та для допомоги іншим): відповідь @vladaman насправді показує цю техніку AtomicCounter. І як @ guangtongShen згадував, цей прийом НЕ є масштабованим! (Я використовую його лише для операцій з низькою інтенсивністю. Наприклад, коли "створення предмета" трапляється дуже рідко. І зазвичай цього підходу слід уникати на користь UUID-кодів (як це також згадується в запиті владимана)
Димитрій К

1

Якщо ви використовуєте NoSQL Dynamo DB, то, використовуючи Dynamoose, ви можете легко встановити унікальний ідентифікатор за замовчуванням, ось простий приклад створення користувача

// 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);
};

0

Натрапив на подібну проблему, коли я вимагав автоматичного збільшення первинного ключа в своїй таблиці. Ми могли б використовувати деякі методи рандомізації, щоб створити випадковий ключ і зберегти його, використовуючи це. Але це не буде поступовим способом.

Якщо вам потрібно щось поступово, ви можете використовувати Unix Time як свій основний ключ. Не впевнений, що ви можете отримати точний приріст (по одному), але так, кожен запис, який ви ставите, був би поступовим щодо різниці в тому, скільки часу кожен запис вставлений.

Не повне рішення, якщо ви не хочете читати всю таблицю і отримувати її останньою, idа потім збільшувати її.

Нижче наведено код для вставки запису в DynamoDB за допомогою NodeJS:

.
.
        const params = {
            TableName: RANDOM_TABLE,
            Item: {
                ip: this.ip,
                id: new Date().getTime()
            }
        }
    
        dynamoDb.put(params, (error, result) => {
            console.log(error, result);
        });
.
.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.