本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
投訴管理系統商業使用案例
DynamoDB 是非常適合投訴管理系統 (或聯絡中心) 使用案例的資料庫,因為與其相關聯的大多數存取模式都是採用鍵值型交易查詢。這種情況的典型存取模式會是:
-
建立和更新投訴
-
呈報投訴
-
建立和讀取投訴的評論
-
取得某一位客戶的所有投訴
-
取得某一位客服人員的所有評論並取得所有呈報
某些評論可能包含描述投訴或解決方案的附件。雖然這些全都是鍵值存取模式,但可能會有其他需求,例如,有新評論新增至投訴時傳送通知,或是執行分析查詢,以便了解每週在嚴重性 (或客服人員表現) 方面的投訴分佈情形。與生命週期管理或合規有關的其他需求,可能包括從記錄投訴起算經過三年後封存投訴資料。
投訴管理系統架構圖
下圖顯示投訴管理系統的架構圖。此圖表顯示投訴管理系統使用的不同 AWS 服務 整合。

除了稍後將在「DynamoDB 資料建模」一節中討論的鍵值交易存取模式之外,還有三項非交易需求。上面的架構圖可分成以下三個工作流程:
-
有新評論新增至投訴時傳送通知
-
對每週資料執行分析查詢
-
封存超過三年的資料
讓我們進一步深入探討上述每一項。
有新評論新增至投訴時傳送通知
我們可以使用下面的工作流程來達成此需求:

DynamoDB Streams 是一種變更資料擷取機制,用來記錄 DynamoDB 資料表上的所有寫入活動。您可以設定 Lambda 函數來觸發部分或全部變更。您可以在 Lambda 觸發程序上設定事件篩選條件,以篩選掉與使用案例無關的事件。在此範例中,我們只能在有新評論新增時使用篩選條件來觸發 Lambda,並傳送通知至可從 AWS Secrets Manager 或任何其他憑證存放區提取的相關電子郵件 ID。
對每週資料執行分析查詢
DynamoDB 適用於主要專注於線上交易處理的工作負載 (OLTP)。對於具有分析需求的其他 10-20% 存取模式,可以使用受管的匯出至 Amazon S3 功能將資料匯出到 S3,這樣做不會影響 DynamoDB 資料表上的即時流量。讓我們來了解下面這個工作流程:

Amazon EventBridge 可用來 AWS Lambda 按排程觸發 - 它可讓您設定 Cron 表達式,讓 Lambda 調用定期發生。Lambda 可以叫用ExportToS3
API呼叫,並將 DynamoDB 資料存放在 S3 中。然後,Amazon Athena 等SQL引擎可以存取此 S3 資料,以在 DynamoDB 資料上執行分析查詢,而不會影響資料表上的即時交易工作負載。依嚴重性層級尋找投訴數目的 Athena 範例查詢如下所示:
SELECT Item.severity.S as "Severity", COUNT(Item) as "Count"
FROM "complaint_management"."data"
WHERE NOT Item.severity.S = ''
GROUP BY Item.severity.S ;
產生的 Athena 查詢結果如下:

封存超過三年的資料
您可以利用 DynamoDB 存留時間 (TTL) 功能,從 DynamoDB 資料表刪除過時的資料,無需額外費用 (除非是 2019.11.21 (目前) 版本的全域資料表複本,其中複寫至其他 區域的TTL刪除會使用寫入容量)。此資料隨即出現,且可從 DynamoDB Streams 取用並封存至 Amazon S3。此需求的工作流程如下:

投訴管理系統實體關係圖
這是我們將用於投訴管理系統結構描述設計的實體關係圖 (ERD)。

投訴管理系統存取模式
這些是我們針對投訴管理結構描述設計考量的存取模式。
-
createComplaint
-
updateComplaint
-
updateSeveritybyComplaintID
-
getComplaintByComplaintID
-
addCommentByComplaintID
-
getAllCommentsByComplaintID
-
getLatestCommentByComplaintID
-
getAComplaintbyCustomerIDAndComplaintID
-
getAllComplaintsByCustomerID
-
escalateComplaintByComplaintID
-
getAllEscalated投訴
-
getEscalatedComplaintsByAgentID (從最新到最舊的順序)
-
getCommentsByAgentID (兩個日期之間)
投訴管理系統結構描述設計演變
由於這是投訴管理系統,因此大多數存取模式都以投訴作為主要實體。高基數的 ComplaintID
將可確保資料在基礎分割區中均勻分佈,同時也是我們確定的存取模式最常見的搜尋條件。因此,ComplaintID
是此資料集中良好的分割區索引鍵選擇。
步驟 1:位址存取模式 1 (createComplaint
)、2 (updateComplaint
)、3 (updateSeveritybyComplaintID
) 和 4 (getComplaintByComplaintID
)
我們可以使用稱為「中繼資料」(或「AA」) 的通用排序索引鍵來儲存投訴專屬資訊,例如 CustomerID
、State
、Severity
和 CreationDate
。我們使用單一操作搭配 PK=ComplaintID
和 SK=“metadata”
來執行下列操作:
-
PutItem
用來建立新的投訴 -
UpdateItem
用來更新投訴中繼資料中的嚴重性或其他欄位 -
GetItem
用來提取投訴的中繼資料

步驟 2:位址存取模式 5 (addCommentByComplaintID
)
此存取模式需要 one-to-many投訴與投訴評論之間的關係模型。我們將在這裡採用垂直分割技術來使用排序索引鍵,並建立具有不同類型資料的項目集合。只要查看存取模式 6 (getAllCommentsByComplaintID
) 和 7 (getLatestCommentByComplaintID
),就會了解評論需依時間排序。我們也可以同時接收多個評論,如此就能使用複合排序索引鍵技術將時間和 CommentID
附加到排序索引鍵屬性中。
其他處理這類可能發生的評論衝突的選項,包括增加時間戳記的精細度,或是加上一個累加數字作為尾碼,而不要使用 Comment_ID
。在這種情況下,我們將在對應評論之項目的排序索引鍵值加上首碼「comm#」,以實現範圍型操作。
另外還需要確保投訴中繼資料中的 currentState
能反映新增新評論的狀態。新增評論可能表示,投訴已指派給客服人員或已解決等情況。為了綁定在投訴中繼資料中新增評論和更新目前狀態, all-or-nothing我們將使用 TransactWriteItems API。產生的資料表狀態現在看起來像這樣:

讓我們在資料表中新增更多資料,另外也新增 ComplaintID
作為有別於 PK
的另一個欄位,以便在 ComplaintID
需要其他索引時,讓模型能夠因應未來需要。另請注意,某些評論可能具有附件,我們會將這些附件存放在 Amazon Simple Storage Service 中,而且只會維護其參考或 DynamoDB URLs中。最佳實務是盡可能保持交易資料庫精簡,以最佳化成本和效能。資料現在看起來像這樣:

步驟 3:位址存取模式 6 (getAllCommentsByComplaintID
) 和 7 (getLatestCommentByComplaintID
)
若要取得投訴的所有評論,我們可以在排序索引鍵上使用 query 操作搭配 begins_with
條件。與其耗用額外的讀取容量來讀取中繼資料項目,隨後因篩選相關結果而造成額外負荷,倒不如使用項這樣的排序索引鍵條件幫助我們只讀取所要的內容。例如,使用 PK=Complaint123
和 SK
start_with 的查詢操作comm#
會傳回下列項目,同時略過中繼資料項目:

既然我們在模式 7 (getLatestCommentByComplaintID
) 中需要投訴的最新評論,那麼就使用兩個額外的查詢參數:
-
ScanIndexForward
應設定為 False,才能以遞減順序排序結果 -
Limit
應設定為 1,才能取得最新 (單獨一個) 評論
類似存取模式 6 (getAllCommentsByComplaintID
),我們使用 begins_with
comm#
作為排序索引鍵條件以略過中繼資料項目。現在,您可以使用查詢操作搭配 PK=Complaint123
和 SK=begins_with comm#
、ScanIndexForward=False
、Limit
1,對此設計執行存取模式 7。結果會傳回以下目標項目:

讓我們新增更多虛設資料到資料表中。

步驟 4:位址存取模式 8 (getAComplaintbyCustomerIDAndComplaintID
) 和 9 (getAllComplaintsByCustomerID
)
存取模式 8 (getAComplaintbyCustomerIDAndComplaintID
) 和 9 (getAllComplaintsByCustomerID
) 引入新的搜尋條件:CustomerID
。若要從現有資料表提取它,則須使用昂貴的 Scan
來讀取所有資料,然後篩選相關項目以找出所指的 CustomerID
。我們可以建立全域次要索引 (GSI),並以 CustomerID
做為分割區索引鍵,讓此搜尋更有效率。請記住 one-to-many客戶與投訴之間的關係以及存取模式 9 (getAllComplaintsByCustomerID
),ComplaintID
將是排序索引鍵的合適候選者。
中的資料GSI如下所示:

GSI 存取模式 8 (getAComplaintbyCustomerIDAndComplaintID
) 的範例查詢為:customer_id=custXYZ
、sort key=Complaint1321
。結果會是:

若要取得客戶針對存取模式 9 (getAllComplaintsByCustomerID
) 的所有投訴, 上的查詢GSI會是: customer_id=custXYZ
作為分割區索引鍵條件。結果會是:

步驟 5:位址存取模式 10 (escalateComplaintByComplaintID
)
此存取將介紹呈報相關層面。若要呈報投訴,我們可以使用 UpdateItem
將 escalated_to
和 escalation_time
等屬性新增至現有投訴中繼資料項目中。DynamoDB 提供了靈活的結構描述設計,這表示非索引鍵屬性集可以合併在一起,也可以分散到不同的項目中。如需範例,請參閱下列內容:
UpdateItem with PK=Complaint1444, SK=metadata

步驟 6:位址存取模式 11 (getAllEscalatedComplaints
) 和 12 (getEscalatedComplaintsByAgentID
)
預計在整個資料集中,只有少數投訴會呈報。因此,在升級相關屬性上建立索引將導致有效率的查詢以及符合成本效益的GSI儲存。我們可以利用稀疏索引技術來實現這個目標。GSI 分割區索引鍵為 escalated_to
,排序索引鍵escalation_time
如下:

若要取得存取模式 11 (getAllEscalatedComplaints
) 的所有升級投訴,我們只需要掃描此 GSI。請注意,由於 的大小,此掃描將具有效能和成本效益GSI。若要取得特定客服人員的 (存取模式 12 (getEscalatedComplaintsByAgentID
)) 的已呈報投訴,分割區索引鍵會是 escalated_to=agentID
,且我們會將 ScanIndexForward
設定為 False
,以便從最新到最舊排序。
步驟 7:位址存取模式 13 (getCommentsByAgentID
)
對於最後一個存取模式,我們需要以新的維度執行查詢:AgentID
。我們也需要以時間為基礎的排序來讀取兩個日期之間的評論,因此,我們使用 建立 GSI agent_id
作為分割區索引鍵,並以 comm_date
作為排序索引鍵。其中的資料GSI如下所示:

其中的範例查詢GSI為 partition key agentID=AgentA
和 sort key=comm_date between (2023-04-30T12:30:00, 2023-05-01T09:00:00)
,其結果為:

下表摘要整理了所有存取模式,以及結構描述設計處理這些模式的方式:
存取模式 | 基礎 table/GSI/LSI | 作業 | 分割區索引鍵值 | 排序索引鍵值 | 其他條件/篩選條件 |
---|---|---|---|---|---|
createComplaint | 基本資料表 | PutItem | PK=complaint_id | SK=metadata | |
updateComplaint | 基本資料表 | UpdateItem | PK=complaint_id | SK=metadata | |
updateSeveritybyComplaintID | 基本資料表 | UpdateItem | PK=complaint_id | SK=metadata | |
getComplaintByComplaintID | 基本資料表 | GetItem | PK=complaint_id | SK=metadata | |
addCommentByComplaintID | 基本資料表 | TransactWriteItems | PK=complaint_id | SK=metadata, SK=comm#comm_date#comm_id | |
getAllCommentsByComplaintID | BASE 資料表 | Query | PK=complaint_id | SK begins_with "comm#" | |
getLatestCommentByComplaintID | BASE 資料表 | Query | PK=complaint_id | SK begins_with "comm#" | scan_index_forward=False, Limit 1 |
getAComplaintbyCustomerIDAndComplaintID | Customer_complaint_GSI | Query | customer_id=customer_id | complaint_id = complaint_id | |
getAllComplaintsByCustomerID | Customer_complaint_GSI | Query | customer_id=customer_id | N/A | |
escalateComplaintByComplaintID | 基本資料表 | UpdateItem | PK=complaint_id | SK=metadata | |
getAllEscalated投訴 | 呈報_GSI | Scan | N/A | N/A | |
getEscalatedComplaintsByAgentID (從最新到最舊的順序) | 呈報_GSI | Query | escalated_to=agent_id | N/A | scan_index_forward=False |
getCommentsByAgentID (兩個日期之間) | Agents_Comments_GSI | Query | agent_id=agent_id | SK between (date1, date2) |
投訴管理系統最終結構描述
以下是最終結構描述設計。若要將此結構描述設計下載為 JSON 檔案,請參閱 上的 DynamoDB 範例
基底資料表

Customer_Complaint_GSI

呈報_GSI

Agents_Comments_GSI

使用此結構描述設計搭配 NoSQL Workbench
您可以將此最終結構描述匯入 NoSQL Workbench,這是一種視覺化工具,可為 DynamoDB 提供資料建模、資料視覺化和查詢開發功能,以進一步探索和編輯您的新專案。請依照下列步驟以開始使用:
-
下載無SQL Workbench。如需詳細資訊,請參閱下載無SQL適用於 DynamoDB 的 Workbench。
-
下載上列結構描述檔案,該檔案已採用 NoSQL Workbench JSON 模型格式。
-
將JSON結構描述檔案匯入 NoSQL Workbench。如需詳細資訊,請參閱匯入現有的資料模型。
-
匯入 NOSQL Workbench 後,您可以編輯資料模型。如需詳細資訊,請參閱編輯現有的資料模型。
-
若要視覺化您的資料模型、新增範例資料,或從CSV檔案匯入範例資料,請使用無SQL工作台的資料視覺化工具功能。