DynamoDB使用扫描读取峰值

时间:2018-12-08 11:03:53

标签: javascript node.js amazon-dynamodb

我有一项小任务,每分钟运行一次,并在具有近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这十次操作)。

我的表具有以下架构:

  • 电话(PK哈希)
  • 配置:字符串格式的JSON;
  • dial_status字符串;
  • expires_on:TTL编号;
  • 名称:字符串
  • 来源:字符串;

作业应根据名称和Dial_status获取所有项目,并且每次执行(每分钟)项目的数量限制为15个元素。对于每个元素,应将其放入要处理的SQS中。

我确实需要减少这些读取单位,但不确定如何优化此功能。我已经读过有关减小页面大小或避免扫描的信息。如果我没有主键并且想返回一组行,可以避免scan的其他方法是什么?

关于如何修复此代码每分钟被调用10到15次的任何想法?

2 个答案:

答案 0 :(得分:1)

我建议您使用以下项创建GSI(全球二级索引):

  • 哈希:name_dialStatus
  • 范围:expiresOn

您已经猜到,哈希键的值是两个独立字段namedialStatus的串联。

现在,您可以在此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);
}

答案 1 :(得分:0)

始终建议根据访问模式设计动态表,以便使用键(primarykey / sortkey)轻松查询该表并避免昂贵的扫描操作。

  1. 如果还不算太晚,请重新访问表架构。
  2. 如果已经晚了,则可以创建GSI,并将“名称”作为PrimaryKey,将“ expires_on”作为具有Projected属性的SortKey,例如“ dialStatus”,以便您只能查询所需数据以降低就绪容量。
  3. 如果您仍然不希望使用选项1和选项2使用RateLimiter进行扫描操作,并且仅传递25%的读取容量,则可以避免峰值。