iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0

洛基忍不住分享他的心得:「昨天的多視角設計真是太精妙了!我覺得好像掌握住 DynamoDB 的核心觀念,之後就可以為任何查詢需求建立對應的視角。」

諾斯克大師望著洛基,知道這個階段的他正處於什麼狀態:「很好,看得出你對新學到的技巧很滿意。今天我們繼續擴展星際活動系統,看看前面會有什麼事等著我們。」

他在白板上寫下新的需求:

星際活動系統擴展需求:

已支援 (Day06 完成):
✓ 按星球查詢活動
✓ 按主題查詢活動

新增需求:
3. 按講者查詢活動 (例如:Asimov 的所有講座)
4. 按時間查詢活動 (例如:SY210-03-15 這天的所有活動)
5. 按報名狀態查詢 (例如:所有尚未額滿的活動)

洛基信心滿滿:「讓我按照昨天學到的方法,為每個查詢需求建立視角。」

應用多視角設計解決新需求

洛基開始設計新的視角:

設計方案:為每個查詢建立專用視角

視角1: 按星球查詢 (Day06 已有)
{PK: "PLANET#MARS", SK: "EVENT#001", ...}

視角2: 按主題查詢 (Day06 已有)
{PK: "TOPIC#Foundation", SK: "EVENT#001", ...}

視角3: 按講者查詢 (新增)
{PK: "SPEAKER#Asimov", SK: "EVENT#001", ...}

視角4: 按時間查詢 (新增)
{PK: "DATE#SY210-03-15", SK: "EVENT#001", ...}

視角5: 按報名狀態查詢 (新增)
{PK: "STATUS#AVAILABLE", SK: "EVENT#001", ...}

「看起來很完美!」洛基說,「每個查詢都有對應的高效視角。」

實際建立多視角資料

洛基開始實作他的設計方案:

# 為一個活動建立 5 個視角的資料
EVENT_001_DATA='{
  "name": "火星防禦研討會",
  "planet": "MARS",
  "topic": "Foundation",
  "speaker": "Asimov",
  "date": "SY210-03-15",
  "capacity": 100,
  "registered": 75,
  "status": "AVAILABLE"
}'

# 視角1: 按星球查詢
aws dynamodb put-item \
  --table-name IntergalacticEvents \
  --item '{
    "PK": {"S": "PLANET#MARS"},
    "SK": {"S": "EVENT#001"},
    "name": {"S": "火星防禦研討會"},
    "topic": {"S": "Foundation"},
    "speaker": {"S": "Asimov"},
    "date": {"S": "SY210-03-15"},
    "status": {"S": "AVAILABLE"}
  }' \
  --endpoint-url http://localhost:8000

# 視角2: 按主題查詢
aws dynamodb put-item \
  --table-name IntergalacticEvents \
  --item '{
    "PK": {"S": "TOPIC#Foundation"},
    "SK": {"S": "EVENT#001"},
    "name": {"S": "火星防禦研討會"},
    "planet": {"S": "MARS"},
    "speaker": {"S": "Asimov"},
    "date": {"S": "SY210-03-15"},
    "status": {"S": "AVAILABLE"}
  }' \
  --endpoint-url http://localhost:8000

# 視角3: 按講者查詢
aws dynamodb put-item \
  --table-name IntergalacticEvents \
  --item '{
    "PK": {"S": "SPEAKER#Asimov"},
    "SK": {"S": "EVENT#001"},
    "name": {"S": "火星防禦研討會"},
    "planet": {"S": "MARS"},
    "topic": {"S": "Foundation"},
    "date": {"S": "SY210-03-15"},
    "status": {"S": "AVAILABLE"}
  }' \
  --endpoint-url http://localhost:8000

# 視角4: 按時間查詢
aws dynamodb put-item \
  --table-name IntergalacticEvents \
  --item '{
    "PK": {"S": "DATE#SY210-03-15"},
    "SK": {"S": "EVENT#001"},
    "name": {"S": "火星防禦研討會"},
    "planet": {"S": "MARS"},
    "topic": {"S": "Foundation"},
    "speaker": {"S": "Asimov"},
    "status": {"S": "AVAILABLE"}
  }' \
  --endpoint-url http://localhost:8000

# 視角5: 按狀態查詢
aws dynamodb put-item \
  --table-name IntergalacticEvents \
  --item '{
    "PK": {"S": "STATUS#AVAILABLE"},
    "SK": {"S": "EVENT#001"},
    "name": {"S": "火星防禦研討會"},
    "planet": {"S": "MARS"},
    "topic": {"S": "Foundation"},
    "speaker": {"S": "Asimov"},
    "date": {"S": "SY210-03-15"}
  }' \
  --endpoint-url http://localhost:8000

洛基完成後滿意地說:「完成!現在每個查詢都可以高效執行了。」

測試新的查詢能力

# 查詢 Asimov 的所有講座
aws dynamodb query \
  --table-name IntergalacticEvents \
  --key-condition-expression "PK = :pk" \
  --expression-attribute-values '{":pk": {"S": "SPEAKER#Asimov"}}' \
  --endpoint-url http://localhost:8000

# 查詢 SY210-03-15 這天的所有活動
aws dynamodb query \
  --table-name IntergalacticEvents \
  --key-condition-expression "PK = :pk" \
  --expression-attribute-values '{":pk": {"S": "DATE#SY210-03-15"}}' \
  --endpoint-url http://localhost:8000

# 查詢所有尚未額滿的活動
aws dynamodb query \
  --table-name IntergalacticEvents \
  --key-condition-expression "PK = :pk" \
  --expression-attribute-values '{":pk": {"S": "STATUS#AVAILABLE"}}' \
  --endpoint-url http://localhost:8000

「太棒了!所有查詢都能高效執行!」洛基興奮地說。

第一個挑戰:資料更新的複雜度

這時 Hippo 跳出來說:「洛基,現在火星防禦研討會又有 10 個人報名,報名人數從 75 人變成 85 人,請更新資料。」

洛基開始更新:「簡單,我更新一下...等等...」

他突然意識到問題:「我需要更新 5 個地方!」

# 需要更新 5 個視角中的報名人數
# 視角1: PLANET#MARS
aws dynamodb update-item \
  --table-name IntergalacticEvents \
  --key '{"PK": {"S": "PLANET#MARS"}, "SK": {"S": "EVENT#001"}}' \
  --update-expression "SET registered = :reg" \
  --expression-attribute-values '{":reg": {"N": "85"}}' \
  --endpoint-url http://localhost:8000

# 視角2: TOPIC#Foundation
aws dynamodb update-item \
  --table-name IntergalacticEvents \
  --key '{"PK": {"S": "TOPIC#Foundation"}, "SK": {"S": "EVENT#001"}}' \
  --update-expression "SET registered = :reg" \
  --expression-attribute-values '{":reg": {"N": "85"}}' \
  --endpoint-url http://localhost:8000

# 視角3: SPEAKER#Asimov
aws dynamodb update-item \
  --table-name IntergalacticEvents \
  --key '{"PK": {"S": "SPEAKER#Asimov"}, "SK": {"S": "EVENT#001"}}' \
  --update-expression "SET registered = :reg" \
  --expression-attribute-values '{":reg": {"N": "85"}}' \
  --endpoint-url http://localhost:8000

# 視角4: DATE#SY210-03-15
aws dynamodb update-item \
  --table-name IntergalacticEvents \
  --key '{"PK": {"S": "DATE#SY210-03-15"}, "SK": {"S": "EVENT#001"}}' \
  --update-expression "SET registered = :reg" \
  --expression-attribute-values '{":reg": {"N": "85"}}' \
  --endpoint-url http://localhost:8000

# 視角5: STATUS#AVAILABLE
aws dynamodb update-item \
  --table-name IntergalacticEvents \
  --key '{"PK": {"S": "STATUS#AVAILABLE"}, "SK": {"S": "EVENT#001"}}' \
  --update-expression "SET registered = :reg" \
  --expression-attribute-values '{":reg": {"N": "85"}}' \
  --endpoint-url http://localhost:8000

洛基執行完後有些煩躁:「5 個操作才能更新一個欄位...這比預期複雜多了。」

第二個挑戰:狀態變更的連鎖反應

Hippo 繼續出題:「現在報名人數達到 100 人了,活動額滿了。請更新狀態為 FULL。」

洛基開始意識到更大的問題:「等等...狀態改變了,那我需要:」

# 1. 從 STATUS#AVAILABLE 中刪除這個活動
aws dynamodb delete-item \
  --table-name IntergalacticEvents \
  --key '{"PK": {"S": "STATUS#AVAILABLE"}, "SK": {"S": "EVENT#001"}}' \
  --endpoint-url http://localhost:8000

# 2. 在 STATUS#FULL 中新增這個活動
aws dynamodb put-item \
  --table-name IntergalacticEvents \
  --item '{
    "PK": {"S": "STATUS#FULL"},
    "SK": {"S": "EVENT#001"},
    "name": {"S": "火星防禦研討會"},
    "planet": {"S": "MARS"},
    "topic": {"S": "Foundation"},
    "speaker": {"S": "Asimov"},
    "date": {"S": "SY210-03-15"},
    "registered": {"N": "100"}
  }' \
  --endpoint-url http://localhost:8000

# 3. 同時更新其他 4 個視角中的狀態欄位
# (需要 4 個 update-item 操作...)

洛基停下手中的工作,陷入沉思:「這變得非常複雜...」

第三個挑戰:一致性的風險

大師看著思考中的洛基問道:「如果在更新過程中,某個操作失敗了怎麼辦?」

洛基想了想:「那就會出現資料不一致的情況!比如有些視角顯示活動還有空位,有些視角顯示已經額滿了。」

「正是如此,」大師說,「而且用戶可能同時在查詢和更新資料,這讓一致性問題更加複雜。」

洛基開始列出他發現的問題:

多視角設計帶來的挑戰:

1. 更新複雜度
   - 一個欄位的更新需要多次操作
   - 操作數量隨視角數量線性增長

2. 事務性挑戰
   - 無法保證所有視角同時更新成功
   - 部分失敗會導致資料不一致

3. 維護成本
   - 每增加一個視角,維護工作都會加重
   - 新增欄位需要更新所有視角

4. 開發複雜度
   - 需要記住所有視角的存在
   - 容易遺漏某些視角的更新

第四個挑戰:新需求的設計壓力

Hippo 再次出現:「洛基,我們又有新需求了!需要支援這些查詢:」

更多查詢需求:
6. 按容量範圍查詢 (例如:100-500 人的活動)
7. 按星球+主題組合查詢 (例如:火星的 Foundation 主題活動)
8. 按講者+時間查詢 (例如:Asimov 在本月的活動)

洛基看著這些需求,感到壓力:「如果我繼續用相同的方法...」

他快速計算:「按容量需要多個範圍視角,組合查詢需要複合 PK...我可能需要 10+ 個視角!」

洛基發現他已經無法在腦中分析,於是在白板上畫出完整的視角清單:

完整視角清單 (如果支援所有查詢):
1. PLANET#* (按星球)
2. TOPIC#* (按主題)
3. SPEAKER#* (按講者)
4. DATE#* (按時間)
5. STATUS#* (按狀態)
6. CAPACITY#100-200 (容量範圍1)
7. CAPACITY#200-500 (容量範圍2)
8. CAPACITY#500+ (容量範圍3)
9. PLANET#MARS#TOPIC#Foundation (組合1)
10. PLANET#MARS#TOPIC#Technology (組合2)
... (更多組合)

洛基看著這個清單,感到震驚:「一個活動可能需要儲存 15+ 次!更新一個活動需要 15+ 個操作!」

複雜度的現實認知

洛基坐下來,重新思考:「我開始明白問題所在了。多視角設計確實能解決查詢效能問題,但它也帶來了新的挑戰。」

大師點頭:「這就是設計中的權衡 (Trade-off)。沒有完美的解決方案,只有適合的解決方案。」

洛基在筆記本上整理思考:

多視角設計的權衡分析:

優點:
✓ 查詢效能極佳 (都是 Query 操作)
✓ 支援任意維度的查詢
✓ 讀取成本可預測

缺點:
❌ 寫入成本倍增 (N 個視角 = N 倍寫入)
❌ 維護複雜度高 (更新需要多次操作)
❌ 一致性風險 (部分更新失敗的可能)
❌ 儲存成本增加 (資料重複儲存)

「所以關鍵是要找到平衡點,」洛基說,「不是為每個可能的查詢都建立視角,而是要選擇最重要的查詢進行優化。」

「正確的理解!」大師說,「那麼問題來了:如何決定哪些查詢值得建立視角,哪些不值得?」

洛基思考:「需要一套判斷標準...比如查詢頻率、重要性、維護成本等因素?」

「很好的想法,」大師說,「明天我們就來學習如何建立這樣的分析方法。」

Hippo 的課外教學

常見的複雜度陷阱

陷阱1: 視角爆炸
- 問題:為每個可能的查詢都建立視角
- 後果:維護成本指數增長
- 解決:只為重要查詢建立視角

陷阱2: 組合查詢的過度設計
- 問題:為所有可能的欄位組合建立視角
- 後果:視角數量 = 2^n (指數爆炸)
- 解決:只為常用組合建立視角

陷阱3: 忽略更新模式
- 問題:只考慮讀取,不考慮寫入頻率
- 後果:讀寫不平衡,維護困難
- 解決:分析讀寫比例,選擇適當策略

陷阱4: 一致性要求過高
- 問題:要求所有視角即時一致
- 後果:複雜的事務處理,效能下降
- 解決:接受最終一致性,簡化設計

上一篇
Day 6:多查詢需求的初遇
下一篇
Day 8:優先級思維的建立
系列文
DynamoDB銀河傳說首部曲-打造宇宙都打不倒的高效服務9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言