테이블 및 인덱스 스캔: .NET - Amazon DynamoDB

테이블 및 인덱스 스캔: .NET

Scan 작업은 Amazon DynamoDB에서 테이블 또는 인덱스의 모든 항목을 읽어옵니다.

다음은 AWS SDK for .NET 하위 수준 API를 사용하여 테이블을 스캔하는 단계입니다.

  1. AmazonDynamoDBClient 클래스의 인스턴스를 만듭니다.

  2. ScanRequest 클래스 인스턴스를 생성하여 스캔 작업 파라미터를 입력합니다.

    이때는 테이블 이름 파라미터만 있으면 됩니다.

  3. Scan 메서드를 실행하고 이전 단계에서 생성한 QueryRequest 객체를 입력합니다.

다음은 포럼 스레드 댓글을 저장하는 Reply 테이블입니다.

>Reply ( <emphasis role="underline">Id</emphasis>, <emphasis role="underline">ReplyDateTime</emphasis>, Message, PostedBy )

이 테이블에는 다양한 포럼 스레드의 댓글이 모두 저장됩니다. 따라서 기본 키도 Id(파티션 키)와 ReplyDateTime(정렬 키), 두 가지로 구성됩니다. 다음은 테이블 전체를 스캔하는 C# 코드 예제입니다. ScanRequest 인스턴스는 스캔할 테이블 이름을 지정합니다.

AmazonDynamoDBClient client = new AmazonDynamoDBClient(); var request = new ScanRequest { TableName = "Reply", }; var response = client.Scan(request); var result = response.ScanResult; foreach (Dictionary<string, AttributeValue> item in response.ScanResult.Items) { // Process the result. PrintItem(item); }

옵션 파라미터 지정

Scan 메서드는 여러 가지 선택적 파라미터를 지원합니다. 예를 들어 옵션으로 스캔 필터를 사용하여 스캔 결과를 필터링할 수 있습니다. 스캔 필터를 사용할 때는 필터 조건과 평가할 조건의 속성 이름을 지정합니다. 자세한 내용은 스캔을 참조하세요.

다음 C# 코드는 ProductCatalog 테이블을 스캔하여 가격이 0보다 작은 항목을 찾습니다. 이 샘플에서 지정하는 옵션 파라미터는 다음과 같습니다.

  • 가격이 0보다 작은 항목만 가져오는 FilterExpression 파라미터(오류 조건)

  • 쿼리 결과로 가져올 항목 속성을 지정하는 ProjectionExpression 파라미터

다음 C# 코드 예제에서는 ProductCatalog 테이블을 스캔하여 가격이 0보다 작은 모든 항목을 찾습니다.

var forumScanRequest = new ScanRequest { TableName = "ProductCatalog", // Optional parameters. ExpressionAttributeValues = new Dictionary<string,AttributeValue> { {":val", new AttributeValue { N = "0" }} }, FilterExpression = "Price < :val", ProjectionExpression = "Id" };

또한 선택에 따라 옵션인 Limit 파라미터를 추가하여 페이지 크기 또는 페이지당 항목 수를 제한할 수 있습니다. Scan 메서드를 실행할 때마다 지정된 수의 항목이 있는 결과 한 페이지를 가져옵니다. 다음 페이지를 가져오려면 이전 페이지의 마지막 항목의 기본 키 값을 제공하여 Scan 메서드가 다음 항목 집합을 반환할 수 있도록 Scan 메서드를 다시 실행합니다. ExclusiveStartKey 속성을 설정하여 요청 시 이 정보를 제공합니다. 이 속성의 초기 값은 null이 될 수 있습니다. 연이어 다음 페이지까지 가져오려면 이 속성 값을 이전 페이지에서 마지막 항목의 기본 키로 업데이트해야 합니다.

다음은 ProductCatalog 테이블 전체를 스캔하는 C# 코드 예제입니다. 요청에서 옵션으로 LimitExclusiveStartKey 파라미터를 지정합니다. do/while 루프는 LastEvaluatedKey가 null 값을 반환할 때까지 계속해서 한 페이지를 스캔합니다.

Dictionary<string, AttributeValue> lastKeyEvaluated = null; do { var request = new ScanRequest { TableName = "ProductCatalog", Limit = 10, ExclusiveStartKey = lastKeyEvaluated }; var response = client.Scan(request); foreach (Dictionary<string, AttributeValue> item in response.Items) { PrintItem(item); } lastKeyEvaluated = response.LastEvaluatedKey; } while (lastKeyEvaluated != null && lastKeyEvaluated.Count != 0);

예제 - .NET을 사용한 스캔

다음 C# 코드는 ProductCatalog 테이블을 스캔하여 가격이 0보다 작은 항목을 찾는 작업 예제입니다.

다음 샘플을 테스트하기 위한 단계별 지침은 .NET 코드 예제 단원을 참조하세요.

/** * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * This file is licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. A copy of * the License is located at * * http://aws.amazon.com/apache2.0/ * * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ using System; using System.Collections.Generic; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Amazon.Runtime; namespace com.amazonaws.codesamples { class LowLevelScan { private static AmazonDynamoDBClient client = new AmazonDynamoDBClient(); static void Main(string[] args) { try { FindProductsForPriceLessThanZero(); Console.WriteLine("Example complete. To continue, press Enter"); Console.ReadLine(); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine("To continue, press Enter"); Console.ReadLine(); } } private static void FindProductsForPriceLessThanZero() { Dictionary<string, AttributeValue> lastKeyEvaluated = null; do { var request = new ScanRequest { TableName = "ProductCatalog", Limit = 2, ExclusiveStartKey = lastKeyEvaluated, ExpressionAttributeValues = new Dictionary<string, AttributeValue> { {":val", new AttributeValue { N = "0" }} }, FilterExpression = "Price < :val", ProjectionExpression = "Id, Title, Price" }; var response = client.Scan(request); foreach (Dictionary<string, AttributeValue> item in response.Items) { Console.WriteLine("\nScanThreadTableUsePaging - printing....."); PrintItem(item); } lastKeyEvaluated = response.LastEvaluatedKey; } while (lastKeyEvaluated != null && lastKeyEvaluated.Count != 0); Console.WriteLine("To continue, press Enter"); Console.ReadLine(); } private static void PrintItem( Dictionary<string, AttributeValue> attributeList) { foreach (KeyValuePair<string, AttributeValue> kvp in attributeList) { string attributeName = kvp.Key; AttributeValue value = kvp.Value; Console.WriteLine( attributeName + " " + (value.S == null ? "" : "S=[" + value.S + "]") + (value.N == null ? "" : "N=[" + value.N + "]") + (value.SS == null ? "" : "SS=[" + string.Join(",", value.SS.ToArray()) + "]") + (value.NS == null ? "" : "NS=[" + string.Join(",", value.NS.ToArray()) + "]") ); } Console.WriteLine("************************************************"); } } }

예제 - .NET을 사용한 병렬 스캔

다음은 병렬 스캔을 설명하는 C# 코드 예제입니다. 프로그램이 ProductCatalog 테이블을 삭제했다가 재생성한 후 테이블에 데이터를 로드합니다. 데이터 로드가 끝나면 프로그램이 다수의 스레드를 생성하여 병렬 Scan 요청을 실행합니다. 마지막으로 프로그램은 런타임 통계를 요약하여 출력합니다.

다음 샘플을 테스트하기 위한 단계별 지침은 .NET 코드 예제 단원을 참조하세요.

/** * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * This file is licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. A copy of * the License is located at * * http://aws.amazon.com/apache2.0/ * * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Amazon.Runtime; namespace com.amazonaws.codesamples { class LowLevelParallelScan { private static AmazonDynamoDBClient client = new AmazonDynamoDBClient(); private static string tableName = "ProductCatalog"; private static int exampleItemCount = 100; private static int scanItemLimit = 10; private static int totalSegments = 5; static void Main(string[] args) { try { DeleteExampleTable(); CreateExampleTable(); UploadExampleData(); ParallelScanExampleTable(); } catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); } catch (AmazonServiceException e) { Console.WriteLine(e.Message); } catch (Exception e) { Console.WriteLine(e.Message); } Console.WriteLine("To continue, press Enter"); Console.ReadLine(); } private static void ParallelScanExampleTable() { Console.WriteLine("\n*** Creating {0} Parallel Scan Tasks to scan {1}", totalSegments, tableName); Task[] tasks = new Task[totalSegments]; for (int segment = 0; segment < totalSegments; segment++) { int tmpSegment = segment; Task task = Task.Factory.StartNew(() => { ScanSegment(totalSegments, tmpSegment); }); tasks[segment] = task; } Console.WriteLine("All scan tasks are created, waiting for them to complete."); Task.WaitAll(tasks); Console.WriteLine("All scan tasks are completed."); } private static void ScanSegment(int totalSegments, int segment) { Console.WriteLine("*** Starting to Scan Segment {0} of {1} out of {2} total segments ***", segment, tableName, totalSegments); Dictionary<string, AttributeValue> lastEvaluatedKey = null; int totalScannedItemCount = 0; int totalScanRequestCount = 0; do { var request = new ScanRequest { TableName = tableName, Limit = scanItemLimit, ExclusiveStartKey = lastEvaluatedKey, Segment = segment, TotalSegments = totalSegments }; var response = client.Scan(request); lastEvaluatedKey = response.LastEvaluatedKey; totalScanRequestCount++; totalScannedItemCount += response.ScannedCount; foreach (var item in response.Items) { Console.WriteLine("Segment: {0}, Scanned Item with Title: {1}", segment, item["Title"].S); } } while (lastEvaluatedKey.Count != 0); Console.WriteLine("*** Completed Scan Segment {0} of {1}. TotalScanRequestCount: {2}, TotalScannedItemCount: {3} ***", segment, tableName, totalScanRequestCount, totalScannedItemCount); } private static void UploadExampleData() { Console.WriteLine("\n*** Uploading {0} Example Items to {1} Table***", exampleItemCount, tableName); Console.Write("Uploading Items: "); for (int itemIndex = 0; itemIndex < exampleItemCount; itemIndex++) { Console.Write("{0}, ", itemIndex); CreateItem(itemIndex.ToString()); } Console.WriteLine(); } private static void CreateItem(string itemIndex) { var request = new PutItemRequest { TableName = tableName, Item = new Dictionary<string, AttributeValue>() { { "Id", new AttributeValue { N = itemIndex }}, { "Title", new AttributeValue { S = "Book " + itemIndex + " Title" }}, { "ISBN", new AttributeValue { S = "11-11-11-11" }}, { "Authors", new AttributeValue { SS = new List<string>{"Author1", "Author2" } }}, { "Price", new AttributeValue { N = "20.00" }}, { "Dimensions", new AttributeValue { S = "8.5x11.0x.75" }}, { "InPublication", new AttributeValue { BOOL = false } } } }; client.PutItem(request); } private static void CreateExampleTable() { Console.WriteLine("\n*** Creating {0} Table ***", tableName); var request = new CreateTableRequest { AttributeDefinitions = new List<AttributeDefinition>() { new AttributeDefinition { AttributeName = "Id", AttributeType = "N" } }, KeySchema = new List<KeySchemaElement> { new KeySchemaElement { AttributeName = "Id", KeyType = "HASH" //Partition key } }, ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 5, WriteCapacityUnits = 6 }, TableName = tableName }; var response = client.CreateTable(request); var result = response; var tableDescription = result.TableDescription; Console.WriteLine("{1}: {0} \t ReadsPerSec: {2} \t WritesPerSec: {3}", tableDescription.TableStatus, tableDescription.TableName, tableDescription.ProvisionedThroughput.ReadCapacityUnits, tableDescription.ProvisionedThroughput.WriteCapacityUnits); string status = tableDescription.TableStatus; Console.WriteLine(tableName + " - " + status); WaitUntilTableReady(tableName); } private static void DeleteExampleTable() { try { Console.WriteLine("\n*** Deleting {0} Table ***", tableName); var request = new DeleteTableRequest { TableName = tableName }; var response = client.DeleteTable(request); var result = response; Console.WriteLine("{0} is being deleted...", tableName); WaitUntilTableDeleted(tableName); } catch (ResourceNotFoundException) { Console.WriteLine("{0} Table delete failed: Table does not exist", tableName); } } private static void WaitUntilTableReady(string tableName) { string status = null; // Let us wait until table is created. Call DescribeTable. do { System.Threading.Thread.Sleep(5000); // Wait 5 seconds. try { var res = client.DescribeTable(new DescribeTableRequest { TableName = tableName }); Console.WriteLine("Table name: {0}, status: {1}", res.Table.TableName, res.Table.TableStatus); status = res.Table.TableStatus; } catch (ResourceNotFoundException) { // DescribeTable is eventually consistent. So you might // get resource not found. So we handle the potential exception. } } while (status != "ACTIVE"); } private static void WaitUntilTableDeleted(string tableName) { string status = null; // Let us wait until table is deleted. Call DescribeTable. do { System.Threading.Thread.Sleep(5000); // Wait 5 seconds. try { var res = client.DescribeTable(new DescribeTableRequest { TableName = tableName }); Console.WriteLine("Table name: {0}, status: {1}", res.Table.TableName, res.Table.TableStatus); status = res.Table.TableStatus; } catch (ResourceNotFoundException) { Console.WriteLine("Table name: {0} is not found. It is deleted", tableName); return; } } while (status == "DELETING"); } } }