DynamoDB считывает пики с помощью сканирования - PullRequest
0 голосов
/ 08 декабря 2018

У меня есть небольшое задание, которое выполняется каждую минуту и ​​выполняет сканирование в таблице, содержащей около 3000 строк:

async execute (dialStatus) {
  if (!process.env.DIAL_TABLE) {
    throw new Error('Dial table not found')
  }

  const params = {
    TableName: process.env.DIAL_TABLE,
    FilterExpression: '#name = :name AND #dial_status = :dial_status AND #expires_on > :expires_on',
    ExpressionAttributeNames: {
      '#name': 'name',
      '#dial_status': 'dial_status',
      '#expires_on': 'expires_on'
    },
    ExpressionAttributeValues: {
      ':name': { 'S': this.name },
      ':dial_status': { 'S': dialStatus ? dialStatus : 'received' },
      ':expires_on': { 'N': Math.floor(moment().valueOf() / 1000).toString() }
    }
  }

  console.log('params', params)

  const dynamodb = new AWS.DynamoDB()
  const data = await dynamodb.scan(params).promise()
  return this._buildObject(data)
}

Я сталкиваюсь с проблемой единиц чтения и тайм-аутов на DynamodB.Сейчас я использую 50 единиц чтения, и это становится дороже по сравнению с RDS.

Имена атрибутов, используемые в функции сканирования, не являются моим первичным ключом: name является вторичным индексом, а dial_statusявляется обычным атрибутом в моем json, но каждая строка имеет этот атрибут.

Это задание выполняется каждую минуту для списка параметров (т. е. если у меня есть 10 параметров, я выполню это scan 10 раз вминуты).

Моя таблица имеет следующую схему:

  • телефон (PK Hash)
  • конфигурация: JSON в формате String;
  • dial_status String;
  • expires_on: номер TTL;
  • name: String
  • origin: String;

Задание должно получать все элементы на основеname и dial_status и количество элементов ограничено 15 элементами в каждом исполнении (каждую минуту).Для каждого элемента он должен быть поставлен в очередь для обработки SQS.

Мне действительно нужно уменьшить эти единицы чтения, но я не уверен, как оптимизировать эту функцию.Я читал о том, как уменьшить размер страницы или избежать сканирования.Каковы мои альтернативы, чтобы избежать scan, если у меня нет первичного ключа и я хочу вернуть группу строк?

Любая идея о том, как исправить этот код, будет вызываться примерно 10-15 раз каждыйминут

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Всегда рекомендуется создавать таблицу динамод на основе шаблонов доступа, чтобы легко запрашивать ее с помощью ключей (primarykey / sortkey) и избегать дорогостоящих операций сканирования.

  1. Пересмотрите схему таблицы, если еще не слишком поздно.
  2. Если уже поздно, то создайте GSI с «name» в качестве PrimaryKey и «expires_on» в качестве SortKey с атрибутами Projected, например «dialStatus», чтобы можно было запрашивать только необходимые данные для уменьшения готовности.
  3. Если вы по-прежнему не хотите использовать опцию 1 и опцию 2, отсканируйте операцию с RateLimiter и пропустите только 25% емкости чтения, чтобы избежать скачка.
0 голосов
/ 10 декабря 2018

Я предлагаю вам создать GSI (Глобальный вторичный индекс) с ключами:

  • HASH: name_dialStatus
  • RANGE: expiresOn

Как вы уже догадались, ключ хеша имеет в качестве значения объединение двух независимых полей name и dialStatus.

Теперь вы можете использовать запрос к этому GSI, что гораздо большеэффективный, поскольку он не сканирует всю таблицу, а исследует только те элементы, которые вас интересуют:

async execute(dialStatus) {
  if (!process.env.DIAL_TABLE) {
    throw new Error('Dial table not found')
  }

  const params = {
    TableName: process.env.DIAL_TABLE,
    IndexName: 'MY_GSI_NAME',
    // replace `FilterExpression`
    // always test the partition key for equality!
    KeyConditionExpression: '#pk = :pk AND #sk > :skLow', 
    ExpressionAttributeNames: {
      '#pk': 'name_dialStatus', // partition key name
      '#sk': 'expires_on' // sorting key name
    },
    ExpressionAttributeValues: {
      ':pk': { 'S': `${this.name}:${dialStatus || 'received'}` },
      ':skLow': { 'N': Math.floor(moment().valueOf() / 1000).toString() }
    }
  }

  console.log('params', params)

  // Using AWS.DynamoDB.DocumentClient() there is no need to specify the type of fields. This is a friendly advice :)
  const dynamodb = new AWS.DynamoDB();
  // `scan` becomes `query` !!!
  const data = await dynamodb.query(params).promise();
  return this._buildObject(data);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...