아마존 DynamoDB: 포인터 정책

13399 단어 dynamodbserverless
관계 데이터베이스에서 NosQL 세계로의 전환은 쉽지 않다.글자상으로는 모든 것이 다르다.때로는 SQL 데이터베이스 세계에서 거의 존재하지 않는 자질구레한 문제를 어떻게 해결해야 할지 몰라 당황스러울 수도 있다.
오늘, 나는 너희들에게 이런 문제를 알려주고 싶다.
데이터베이스에 당신이 표에 Order개의 기록을 저장했다고 상상해 보세요.마지막 주문 번호에 대한 정보가 필요합니다.스케줄러:왜? 이제 상관없어.

SQL의 최종 주문 번호


SQL을 사용할 때 이 문제는 매우 작다.당신은 다음과 같이 쓰기만 하면 됩니다.
SELECT orderId FROM Orders ORDER BY orderId DESC LIMIT 1;
검색은 매우 간단하고 효율적이다. (특히 우리가 색인을 가지고 있을 때)잘하는 SQL👏

DynamoDB의 최종 주문 번호


이것이 바로 문제의 골칫거리다.당장 해결책을 드리지 않겠습니다. 완전히 이해하려면 다이나마이드에 대한 지식이 필요하니까요.

DynamoDB에 대한 약간의 이론


먼저 DynamoDB에서 SQLSELECT의 등가물은 두 개의 명령scanquery이다.둘 다 테이블에서 정보를 검색하는 데 사용된다. (DynamoDB에서 우리는 전체 데이터베이스가 아니라 표의 개념만 있다.)그러나 그들 사이에는 현저한 차이가 존재한다.scan 방법은 전체 표의 내용을 스캔하여 원소 집합으로 우리에게 되돌려준다.
다음 표scan는 전체 표에 6개의 주문서가 있기 때문에 6항을 되돌려줍니다.
scan를 사용할 때 우리는 모든 주문서에 대한 정보를 검색하고 코드에서 처리할 수 있다. 예를 들어 Lambda 함수에서 데이터를 필터하고 이전 주문 번호를 되돌려준다.
이런 방법은 우리가 데이터베이스에서 검색한 데이터가 우리가 원하는 것보다 훨씬 많기 때문에 (주문 집합과 단일 주문 번호) 최악이다.이것은 응용 프로그램의 운행 시간에 영향을 미치지만, 비용에도 영향을 미칠 수 있다. 왜냐하면 DynamoDB에서 우리는 모든 조회/되돌아오는 데이터 양을 위해 비용을 지불하기 때문이다.더 심각한 것은 대량의 데이터를 처리할 때, 우리는 반드시 결과를 뒤져야 한다.
따라서 다른 선택을 고려해 보자.query 방법은 로컬 집합에서 데이터를 검색하는 데 사용된다. 즉, 데이터베이스에서 공공(같은 값) 섹션 키를 공유하는 요소이다.

I'm trying to make this article concise and simple, so I'm not going to discuss how Amazon DynamoDB is internally designed. However, I strongly encourage you to learn it on your own, because it is simply a good architecture school and it will help you understand why this database is used the way it is.


우리의 주문서(예를 들어 상)에서 모든 주문서는 서로 다른 구역 키 값을 가지고 있기 때문에 여기서 query 방법을 사용하는 것은 근본적으로 의미가 없다. 왜냐하면 우리는 로컬 집합이 없기 때문이다.
다른 한편, 주문서의 현지 집합은 단일 주문 중의 상품 목록일 수 있다.이 경우 각 항목의 주 키는 두 개의 값(복합 키)으로 구성됩니다.
  • 파티션 키
  • 정렬키
  • 테이블이 다음 상태로 변경됩니다.그리고 파라미터query로 호출order#2하면 두 항목을 되돌려줍니다. 이 주문서에서 두 가지 제품을 구매한 사람이 있기 때문입니다. DataLake Training과 Consulting입니다.

    그러나 DynamoDB에서 이것은 서로 다른 데이터 접근 모델이기 때문에 저는 이를 방주로 여러분께 소개합니다.
    우리들은 주요 문제로 돌아갑시다.scanquery 모두 최신 주문 번호를 반환하기에 적합하지 않은 이상 어떻게 해야 합니까?

    GetItem 메서드


    또 하나의 방법GetItem은 특정한 키 값을 정할 때 데이터베이스에서 항목을 되돌려준다.이것은 getById(id)나 SQL과 비슷합니다.
    SELECT * FROM Orders WHERE orderId = 'orderNumber';
    
    이것은 매우 멋있다. 그러나 만약 이것이 우리가 먼저 데이터베이스에서 추출하고 싶은 내용이라면 우리는 어떻게 이것orderNumber을 얻을 수 있겠는가?🤔

    포인터 정책


    이것이 바로 지침책략의 용무의 땅이다.
    우리는 알 수 없는 값을 알 수 있는 상수로 바꿀 수 있으며, 이 상수를 통해 데이터베이스를 인용할 수 있다.상수 구분 키를 가진 이 요소는 전체 테이블에 하나뿐입니다.따라서 우리는 그것을 지침으로 삼아 최신 주문서의 값을 저장할 수 있다.
    첫 번째 표에서, 우리는 구역 키가 항상 LAST_ORDER (우리의 상수 문자열) 과 같은 다른 요소를 추가했다.이런 원소는 OrderId라는 속성이 있는데 그 값은 가장 가까운 순서이다.테이블에 새 주문서를 추가할 때마다 우리는 LAST_ORDER 요소의 값을 새로운 orderId 요소의 값으로 업데이트합니다.

    지금 우리가 해야 할 일은 간단한 getItem('LAST_ORDER') 방법을 호출해서 가장 좋은 방법으로 우리를 위해 이전 주문 번호를 되돌려 주는 것이다.

    선진 기술


    DynamoDB 테이블이 통상적으로 더 큰 시스템의 일부라는 것을 감안하여 여러 프로세스가 병렬적으로 (직접 또는 SQS 대기열을 통해) 데이터베이스에 쓸 때 어떤 상황이 발생할지 생각해 봅시다. 이것은 중요하지 않습니다.물론 데이터베이스에 기록된 마지막 주문은 마지막 주문이 아니다.그런 다음 LAST_ORDER 포인터가 이전 순서를 잘못 가리킵니다.
    우리는 SQS FIFO 대기열을 사용하여 이 문제를 해결할 수 있지만, 더욱 간단하고 저렴한 해결 방안이 있다.
    우리는 ConditionExpression 요소의 새 버전을 저장할 때 LAST_ORDER 적용하기만 하면, 새 orderId 값이 데이터베이스에 현재 저장된 값보다 큰지 확인할 수 있습니다.만약 그렇다면, 그것은 업데이트될 것이다. 만약 그렇지 않다면, 그것은 업데이트되지 않을 것이다.
    덕분에 데이터베이스에 한 번만 쓰면 이 값을 업데이트할 수 있고 다운로드하고 코드 쪽에서 검사할 필요가 없다.그 밖에 이 방법은 幂 등이다. (만약 우리가 여러 번 같은 사건을 얻게 된다면 후속 호출에서 데이터베이스 상태를 바꾸지 않을 것이다.)

    어떻게?


    코드를 좀 쓸 때가 됐어.내 JavaScript 예제 구현:
      async createPointer(orderId) {
        const pointer = new Pointer({ orderId })
        const params = {
          Item: pointer.toItem(),
          ReturnConsumedCapacity: 'TOTAL',
          TableName: process.env.ordersTableName,
          ConditionExpression: 'attribute_not_exists(#orderId) OR #orderId < :newId',
          ExpressionAttributeNames: {
            '#orderId': 'orderId'
          },
          ExpressionAttributeValues: {
            ':newId': { N: `${orderId}` }
          }
        }
        log('createPointer params', params)
        try {
          await this.dynamoDbAdapter.create(params)
        } catch (error) {
          if (error.code === 'ConditionalCheckFailedException') {
            log(`LAST_ORDER pointer already exists and is greater than ${orderId}. Skipping update.`)
          } else {
            log('Error', error)
            throw error
          }
        }
        return pointer
      }
    
    코드 목록 설명:

  • 4행-포인터 클래스는 Pointer 대상을DynamoDB API가 원하는 JSON으로 변환하는 방법을 실현했다.다음은 이 방법의 실현이다.

  • 7행 - 조건 표현식: 데이터베이스에 쓸 시간, 쓰지 않을 시간?필요attribute_not_exists(#orderId) 데이터베이스가 비어 있을 때 코드도 처음으로 이 항목을 실행하고 기록합니다.그런 점에서 조건의 두 번째 부분#orderId < :newId만이 의미가 있다.

  • 열아홉 번째 줄 - 조건이 충족되지 않으면 DynamoDB API는 aConditionalCheckFailedException로 되돌아갈 것이다. 우리의 예에서 이것은 조만간 있을 것이다.
  • 다음은 Pointer류 실현이다.
    class Pointer {
      constructor({ orderId, createdAt = new Date() }) {
        this.orderId = parseInt(orderId)
        this.createdAt = createdAt instanceof Date ? createdAt : new Date(createdAt)
      }
    
      key() {
        return {
          PK: { S: 'LAST_ORDER' }
        }
      }
    
      static fromItem(item) {
        return new Pointer({
          orderId: item.orderId.N,
          createdAt: item.createdAt.S
        })
      }
    
      toItem() {
        return {
          ...this.key(),
          orderId: { N: this.orderId.toString() },
          createdAt: { S: this.createdAt.toISOString() },
        }
      }
    }
    
    이 수업의 콘셉트는 알렉스 데브리에게서 표절한 것이니 주의하세요😃
    그는 DynamoDB 책의 저자로 이 데이터베이스를 어떻게 사용하는지 가르치는 가장 좋은 방법이다.내가 그 책을 읽은 이후로, 나는 그가 묘사한 많은 선진 기술을 사용하기 시작했다.나는 그의 책을 극력 추천한다.

    요약


    나는 이 간단한 예를 원하지만, 자신의 진실한 시스템을 취하여, 당신들이 다이나마이드를 어떻게 사용하는지 더욱 잘 이해하도록 돕는다.나는 단지 일부분의 지식만을 소개했을 뿐이지만, 동시에 나는 당신에게 프로젝트에서 직면할 수 있는 특정한 문제를 어떻게 해결하는지 보여 드리겠습니다.
    나는 DynamoDB에 가파른 학습 곡선이 있다는 것을 안다. 이것은 부인할 수 없는 것이다. 그러나 솔직히 말해서, 나는 Amazon DynamoDB 데이터베이스가 없는 서버 시스템이 없다는 것을 상상할 수 없다.10가지 중 9가지 경우 AWS RDS(Aurora Serverless 포함) 대신 DynamoDB를 선택하겠습니다.

    좋은 웹페이지 즐겨찾기