วิธีการนำข้อมูล CSV จาก Amazon S3 เข้า Amazon DynamoDB
Tags: aws, awsthai, tutorial, dynamodb
Published: 2021-09-07 05:16:20

## สารบัญ (Table of Content)
1. [บทนำ](#intro)
2. [แผนผังของระบบ](#diagram)
3. [การประเมินราคาเบื้องต้น](#cost)
4. [ข้อจำกัดของตัวอย่างที่ควรรู้](#restriction)
5. [ขั้นตอนที่ 1: นำข้อมูลตัวอย่างเข้า Amazon S3](#step1)
6. [ขั้นตอนที่ 2: สร้าง DynamoDB Table และเตรียม configuration](#step2)
7. [ขั้นตอนที่ 3: สร้าง Lambda Function และเตรียม configuration](#step3)
8. [ขั้นตอนที่ 4: Copy python code สำหรับ import ไปยัง Lambda](#step4)
9. [ขั้นตอนที่ 5: Execute Lambda Function](#step5)
10. [ขั้นตอนที่ 6: ตรวจสอบข้อมูลที่ import ที่ DynamoDB](#step6)
11. [วิธีการอื่น ๆ เพิ่มเติม](#alter)

<a name="intro"></a>
## บทนำ
หลาย ๆ ครั้ง ผู้ใช้งานมีความต้องการที่จะนำข้อมูล จากระบบฐานข้อมูลแบบตาราง (RDBMS) ซึ่งอยู่ในรูปแบบ CSV file นำเข้า (import) ไปยังระบบฐานข้อมูลแบบ NoSQL อย่างเช่น Amazon DynamoDB เป็นต้น และผู้ใช้งานพบว่า Amazon DynamoDB ไม่ได้มี feature อย่างเช่นการ import โดยตรง ทำให้ผู้ใช้งาน พบปัญหาเกี่ยวกับการเขียน application ขึ้นมาเอง และวิธีการกำหนดสิทธิต่าง ๆ

ในบทความนี้จะเป็นตัวอย่างของการนำข้อมูล CSV file เข้า Amazon DynamoDB โดยมีวิธีการ รวมถึงตัวอย่าง code ที่ผู้ใช้งานสามารถนำไปปรับใช้ให้เหมาะสมกับข้อมูลที่ผู้ใช้งานต้องการได้อีกด้วย

<a name="diagram"></a>
## แผนผังของระบบ (Architecture Diagram)
![Architect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v0311a587n0vulf9x7be.png)

<a name="cost"></a>
## การประเมินราคาเบื้องต้น (Cost Estimation)
* ราคา Storage, Write Capacity Unit (WCU) และ Read Capacity Unit (RCU) ของ DynamoDB - [Amazon DynamoDB Pricing](https://aws.amazon.com/dynamodb/pricing/)
* ราคา Storage ของ S3 - [Amazon S3 Pricing](https://aws.amazon.com/s3/pricing)
* ราคา Invocation ของ Lambda - [AWS Lambda Pricing](https://aws.amazon.com/lambda/pricing/)

> :information_source:  หมายเหตุ: อาจจะมีราคาอีกนิดหน่อยในส่วนของ Network ที่ยังไม่ได้รวม

<a name="restriction"></a>
## ข้อจำกัดของตัวอย่างที่ควรรู้
* CSV file จะต้องมี header อยู่ที่ row แถวแรกของ file
* การกำหนด partition key ของ DynamoDB จะต้องชื่อตรงกับ column header name ที่อยู่ใน file แบบ case-sensitive (ตัวเล็กตัวใหญ่มีผล)
* Lambda resource และ timeout configuration ควรปรับเปลี่ยนให้เหมาะสมกับขนาดของข้อมูล
* IAM Policy ของ Lambda Execution Role ควรกำหนดให้เหมาะสม เมื่อนำไปใช้งานจริง

<a name="step1"></a>
## ขั้นตอนที่ 1: นำข้อมูลตัวอย่างเข้า Amazon S3
* Download ข้อมูลตัวอย่าง [SaaS-Sales.csv](https://ee-assets-prod-us-east-1.s3.amazonaws.com/modules/337d5d05acc64a6fa37bcba6b921071c/v1/SaaS-Sales.csv)
* Upload ข้อมูลตัวอย่างขึ้น S3 (e.g. nutchanon-ddb-importer)
![UploadS3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06f87qk92l41e15d03jp.png)

<a name="step2"></a>
## ขั้นตอนที่ 2: สร้าง DynamoDB Table และเตรียม configuration
* สร้าง DynamoDB Table ชื่อ "quicksight-ddb" โดยมี parition key ชื่อ "Row ID" (ตัวเล็กตัวใหญ่ มีผลต่อข้อมูลตัวอย่าง) และปรับ capacity mode เป็น On-Demand
![DDBCreation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gfwbht7tmfn5v56kf6ls.png)
![DDBCapacity](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zsqcgy8jxoartr5y79gq.png)

<a name="step3"></a>
## ขั้นตอนที่ 3: สร้าง Lambda Function และเตรียม configuration
* สร้าง Lambda Function ด้วย python 3.9 (e.g. ddb-csv-importer)
* ปรับ Lambda resource และ timeout configuration ให้เป็น 256MB และ timeout 15 นาที 
![LambdaConfig](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gvtsyr2fg3uaah5szzs4.png)
* จากนั้นเพิ่ม permission ให้ Lambda Execution Role สามารถอ่านข้อมูลจาก S3 และเขียนข้อมูลเข้า DynamoDB ได้ (DDBBatchWrite)

> :warning: **สำหรับการทดสอบเท่านั้น** ในการใช้งานต้อง จะต้องมีการระบุ IAM policy ให้ restrict เฉพาะเท่าที่ใช้งานเท่านั้น

![LambdaPermission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyhem45pvhf6yskoc7k4.png)
![S3Policy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/soq8b40sdj94qtj6mwtu.png)
![DDBWritePolicy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/as81qqu616bo59wksipu.png)

<a name="step4"></a>
## ขั้นตอนที่ 4: Copy python code สำหรับ import ไปยัง Lambda
* Copy Python Lambda code ตามด้านล่าง แล้วอย่าลืมเปลี่ยน configuration "s3_csv_bucket", "s3_csv_data", "ddb_table_src" ให้เป็นของผู้ใช้งานด้วย
```python
import boto3
import os
import json
import csv
import codecs
import json

s3 = boto3.client('s3')
ddb = boto3.resource('dynamodb')
s3_csv_bucket = "nutchanon-ddb-importer" # Source Amazon S3 bucket
s3_csv_data = "SaaS-Sales.csv" # Source Amazon S3 object file
ddb_table_src = "quicksight-ddb" # Target Amazon DynamoDB Table

def lambda_handler(event, context):
    # get data
    obj = s3.get_object(Bucket= s3_csv_bucket, Key= s3_csv_data)
    
    # json data
    ddb_data = []
    for rows in csv.DictReader(codecs.getreader("utf-8-sig")(obj[u'Body'])):
        ddb_data.append(rows)
    
    # define dynamodb table
    ddb_table = ddb.Table(ddb_table_src)
    
    # batch write data to dynamodb table
    with ddb_table.batch_writer() as batch:
        for item in ddb_data:
            batch.put_item(Item=item)

    return None
```

<a name="step5"></a>
## ขั้นตอนที่ 5: Execute Lambda Function
* รัน execute บน Lambda function ด้วย test event ใด ก็ได้ รอจน return success
![LambdaResult](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pzlfqxb9uma1spy7gmq2.png)

<a name="step6"></a>
## ขั้นตอนที่ 6: ตรวจสอบข้อมูลที่ import ที่ DynamoDB
* ตรวจสอบข้อมูลใน DynamoDB Table ด้วย "Give live item count"
![DDBLiveItems](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pxlrefgvaq9ymak9d503.png)

<a name="alter"></a>
## วิธีการอื่น ๆ เพิ่มเติม
* ทาง AWS Blog เองก็มีจะวิธีการในการ import ข้อมูลจาก Amazon S3 ไปยัง DynamoDB รวมถึงตัวอย่าง code ที่สามารถใช้งานได้ ดังต่อไปนี้
    1. [Creating a scalable serverless import process for Amazon DynamoDB](https://aws.amazon.com/blogs/compute/creating-a-scalable-serverless-import-process-for-amazon-dynamodb/)
    2. [Github: S3-to-DynamoDB importer](https://github.com/aws-samples/s3-to-lambda-patterns/tree/master/ddbImporter)

![CastleArm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3pp3cc9du87hqg20ugb.jpg)