星際曆 209 年,5年一次的跨星際軍團開發者大會即將在明年舉行。
這次輪到玫瑰星系的 B-613 與 331 聯合舉行。然而這兩個星球對於首次承辦如此大型的星際活動都覺得壓大甚大,在進行技術評估之後,覺得他們熟悉的關聯式資料庫如果要應付這樣的規模,投入成本會高到嚇人,而且系統的穩定度、回應速度還未必能符合期待。
活動負責人莉莎指揮官在綜合各方情報後,決定使用 DynamoDB 來解決這個問題,根據各方資料,DynamoDB 可以在毫秒內回應,而且不受資料量大小的影響,對於高併發系統是絕佳的選擇。
此外情報指出,在星際聯盟的邊緣,太陽系的地球,有位 DynamoDB 大師諾斯克,不但精於處理高併發的系統,而且非常擅長因材施教,72 星系、三千星球都有他的門徒。
莉莎指揮官決定在兩個星球中挑選出他們最有潛力的開發人員,希望在一個月的時間內迅速學成 DynamoDB。
在海選過程中脫穎而出的上尉洛基,帶著星球的托負與期盼,前往地球。
「咚咚咚」門外傳來敲門聲。
諾斯克大師從冥想的狀態中轉換過來。近年來由於他潛心整理自己在 DynamoDB 的見解,除了不收學生之外,也很少見客了。雖然知道今天有遠道而來的訪客,不過諾斯克看了一下時間,提早了快半個小時。
「請進」諾斯克輕聲回應。
走進門的洛基上尉,身材精壯挺拔,雙腿用力靠併,但左腿微抖,右手舉到眉緣恭敬地行軍禮,大聲喊著「上尉洛基向諾斯克大師報到」。
大師看著這位年輕人,輕聲地問道:「這趟航行,應該對你在玫瑰星系內戰時,受傷的左腿帶來蠻大的壓力吧?」
上尉洛基驚訝地說:「大師是怎麼知道的?我的個人簡歷只有寫現職的資訊而已,也沒有提到受傷的事情?」
諾斯克微笑地說:「很多事情不需要透過文字也都會寫出來。你的身形很明顯是前線戰鬥人員的體格,行軍禮的俐落感更是玫瑰星系聯防軍特種軍團常見的樣子。但是你的資料卻告訴我你在後勤單位從事程式開發工作,因此很可能因傷退下。然後你的左腿在併攏時有些抖動,也是旁證,那是受過傷的後遺症。」
「大師觀察和推理能力非常敏銳,說的都沒錯。」洛基剛到不到5分鐘,就已經感受到大師之所以為大師的原因了。「但你為什麼會知道是在星系內戰時受的傷?」
「從你任職的時間和星系內戰最後戰役的時間去推算,加上你有這麼優秀的戰鬥人員的姿質,然後卻比預約時間還早到...」
洛基臉上一紅,當年他過於冒進的襲擊,雖然成功打下了據點,卻也造成隊上不少人員受傷,而他自己是最嚴重的一個,左腿差點要截肢。這些事彷彿都被看穿了。
「思考是網狀的,資訊會落在各個點上,如果只用線性的方式思考,你的迴路就會受限。你是來學習 DynamoDB 的,你要學習的就是這種網狀的思考模式。」
諾斯克帶著洛基到他的茶室,同時也是他的工作室。在上尉坐定之後,大師沖了一杯茶給他。
「來,試試這款茶,只有地球才有,可以安定你的心神,放鬆一下。」
洛基試一口,微燙的茶中有一股芳香,令人感到神清氣爽。
諾斯克問起洛基:「不如說說看,你這次的任務要建立開發者大會報名系統,你會如何規劃?」
洛基在航程中其實已經在腦海中規劃了一些架構,於是他說:「這個系統因為有多個活動,因此有許多的註冊行為,我們在規劃時應該要有 events、users、resgistrations......這些表格,後續要查詢時可以利用 JOIN 的方式來......」
諾斯克這時舉手示意洛基暫停,「我理解你還停留在舊的思維。但在 DynamoDB 的世界裡,我們不這樣思考。」
「Hippo 請展示兩種思維的圖」,接到指令的 Hippo 迅速地在白板上投出影像。
「在你熟悉的關聯式資料庫中,」大師解釋,「通常會先設計出理想的資料結構,然後透過 JOIN 組合查詢。但在 DynamoDB 裡,我們先想:『我需要回答什麼問題?』」
洛基困惑地看著白板:「什麼意思?」
「舉例來說,」大師繼續,「如果你經常需要查詢『某個使用者參加了哪些活動』,在 SQL 中你會 JOIN events 和 registrations 表格。但在 DynamoDB 中,不是透過關聯,而是把需要的資訊直接存在一筆資料中,一次查詢就能取得所有資料。」
洛基露出疑惑的表情:「這樣不會有很多重複的資料嗎?」
大師微笑:「這就是網狀思考的轉換─在 DynamoDB 中,適度的資料冗餘 (Data Redundancy) 是智慧,不是罪惡。」
諾斯克大師接著說:「讓我們從實作中學習。Hippo 請幫我們設定 DynamoDB 的初學環境」。
空中響起的自動應答聲響,「馬上準備。」Hippo 是大師自行開發的教學人工智慧。
諾斯克跟洛基說:「我是個老派的人,相信自己輸入才是學習的好方法,我不確定你習不習慣,不過接下來你不習慣的事還會有很多,所以也不差這一件。」
Hippo 在洛基的桌上升起了一個實體鍵盤與投射影螢幕,螢幕上輸出著:
Name Command State Ports
dynamodb-local java -jar DynamoDB... Up 0.0.0.0:8000->8000/tcp
「你現在看到的是 Hippo 已經幫我們在本地端建好了一個 DynamoDB 的資料庫。它並非生產環境完整的功能複製,但對我們學習的目的來說,已經夠用了。」
大師說,「現在我們使用 AWS CLI 在命令列來建立第一個 Table。」
# 建立 DynamoDB Table
aws dynamodb create-table \
--table-name IntergalacticEvents \
--attribute-definitions AttributeName=PK,AttributeType=S \
--key-schema AttributeName=PK,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--endpoint-url http://localhost:8000
「執行成功!」Hippo 回報著。
大師跟洛基說明,billing-mode 和 endpoint-url 這兩個參數晚一點 Hippo 會跟他詳細說明,目前先專注在前面幾個語法即可,看看有什麼他不懂的地方可以提出來。
洛基仔細觀察語法:「為什麼我們只定義了一個屬性 PK
?其他欄位呢?這看起來像是 Events 的資料表,至少也應該要有活動名稱、時間、地點這些基本欄位吧。還有,這個 AttributeType=S 是什麼意思?」
大師點頭:「很好的問題。在 DynamoDB 中,我們只需要定義 Key 的結構,其他屬性可以隨時新增。這就是 NoSQL 的靈活性。至於 S,代表 String 字串型別——DynamoDB 需要知道 Key 是什麼資料型別。」
「但是 PK
是什麼?」洛基問道。
「PK 是主鍵 (Primary Key) 的簡寫,」大師解釋,「它是資料表中每個 Item 的唯一識別碼。但這不只是 ID,它決定了資料如何分散儲存,以及你能如何查詢。但是先別急,我們會逐漸展開,從做中學習。」
大師繼續示範:「現在來存入第一筆資料。注意看每個值前面的 S 或 N——S 代表字串,N 代表數字。」
# 第一個活動資料(簡化版本,只顯示關鍵欄位)
aws dynamodb put-item \
--table-name IntergalacticEvents \
--item '{
"PK": {"S": "EVENT#B613#2024-03-15#DEV-001"},
"title": {"S": "星際軍團開發者大會-DynamoDB工作坊"},
"organizer": {"S": "B-613星球技術委員會"},
"maxParticipants": {"N": "100"},
"currentParticipants": {"N": "0"},
"location": {"S": "B-613星球中央會議廳"}
}' \
--endpoint-url http://localhost:8000
「資料成功寫入!」Hippo 確認著。
「注意看,」大師指著投影螢幕上的程式碼,「我們剛剛使用了 DynamoDB 的 put-item
命令來寫入資料。這是 DynamoDB 最基本的寫入操作。每個欄位都要標記型別,雖然看起來有點囉嗦,但這確保了資料的精確性。」
洛基看著資料結構:「主鍵EVENT#B613#2024-03-15#DEV-001
看起來很複雜??」
大師停下來說:「這是個有意的設計,上尉。讓我分解給你看:
EVENT
- 告訴我們這是什麼類型的資料B613
- 哪個星球的活動2024-03-15
- 日期資訊DEV-001
- 特定的活動編號這個結構包含了豐富的資訊,讓我們能精確定位每個活動。主鍵必須保持唯一性,這點與關聯式資料庫相同。但是...」大師停頓了一下,「這裡的主鍵為什麼要設計得這麼複雜?一切都是為了查詢。」
洛基若有所思:「所以 Primary Key 不只是 ID,還包含了查詢的線索?」
「完全正確」大師讚許地說,「在 DynamoDB 中,Key 的設計就是查詢能力的設計。不過,目前這個設計只是為了展示。很快你就會親身體驗到它的問題所在......」
「現在讓我們把資料讀回來,」大師說,「在 DynamoDB 中,如果你知道主鍵,就可以使用 get
方法直接取得資料。」
# 直接透過 Primary Key 讀取
aws dynamodb get-item \
--table-name IntergalacticEvents \
--key '{"PK": {"S": "EVENT#B613#2024-03-15#DEV-001"}}' \
--endpoint-url http://localhost:8000
終端機立即顯示:
{
"Item": {
"location": {
"S": "B-613星球中央會議廳"
},
"currentParticipants": {
"N": "0"
},
"PK": {
"S": "EVENT#B613#2024-03-15#DEV-001"
},
"organizer": {
"S": "B-613星球技術委員會"
},
"maxParticipants": {
"N": "100"
},
"title": {
"S": "星際軍團開發者大會-DynamoDB工作坊"
}
}
}
「不用事先做一堆規劃,這麼簡單就可以開始工作!」洛基驚嘆,腦中興奮地想著各種可能性。
「你這個說法既對又錯」大師補充,「DynamoDB 允許你不用事先規劃就可以工作,但工作很少是不用事先規劃的。別忘了你的左腿為你上的課」
洛基靜默不語。
「我來寫下今天的重點,你先試著自己建幾筆資料,增加你的肌肉記憶感。」
大師走到白板前,寫下今天的重點:
洛基看著大師寫下的的重點「我有感覺到差異了!DynamoDB 比起關聯式資料庫是一種完全不同的思維方式。但我也必須說,對 DynamoDB 究竟是什麼,我還是很模糊」
大師點頭:「很好,第一天這樣子就夠了,認識到這是一個完全不同的東西,放掉你慣性的 SQL 思維,用全新的眼光來看它,這就是我們今天最關鍵的事情。不懂在此時此刻才是懂。」
洛基想了一下:「請讓我再自己建個 Table,練習一下 put 和 get 的方法。」
「Hippo 你聽到客人的要求了,請再為他準備個沙箱環境讓他盡情練習吧。」
「沒問題,馬上就好」Hippo 幹練地回應著。
大師:「上尉,你可以留著繼續練習,但也不用太拼命,這是 30 天的第一天,把精神、力氣平均分配,在最佳狀態維持到最後一天吧。」
「我會注意的」上尉恭敬地回答。
「明天我們會從主鍵這個話題繼續,畢竟靈魂是最重要的,不是嗎?」
在 AWS CLI 中,每個值都需要標記型別:
http://localhost:8000
連接 DynamoDB Local# 拉取 DynamoDB Local 映像
docker pull amazon/dynamodb-local
# 啟動 DynamoDB Local
docker run -p 8000:8000 amazon/dynamodb-local
# 下載 DynamoDB Local
wget https://s3.ap-northeast-1.amazonaws.com/dynamodb-local-tokyo/dynamodb_local_latest.tar.gz
# 解壓縮
tar -xzf dynamodb_local_latest.tar.gz
# 執行 (需要 Java 8 以上)
java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
# 使用 Homebrew
brew install awscli
# 或使用官方安裝程式
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
# 下載安裝程式
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
下載並執行 MSI 安裝程式:
https://awscli.amazonaws.com/AWSCLIV2.msi
# 設定本地端認證 (使用假的認證資訊)
aws configure
# AWS Access Key ID: fake
# AWS Secret Access Key: fake
# Default region name: local
# Default output format: json
# 測試連線
aws dynamodb list-tables --endpoint-url http://localhost:8000