通用數(shù)據(jù)保護(hù)條例(GDPR)是當(dāng)今技術(shù)世界中的重要法規(guī),也是眾多在亞馬遜云科技公有云當(dāng)中建立解決方案的用戶們所必須遵循的數(shù)據(jù)處理要求。GDPR中提出一項“刪除權(quán)”,或者叫“被遺忘權(quán)”條款,要求通過實施相關(guān)解決方案保證刪除特定用戶的個人數(shù)據(jù)。
在亞馬遜云科技大數(shù)據(jù)與分析生態(tài)系統(tǒng)的背景之下,每一套架構(gòu),無論其面向怎樣的目標(biāo),都需要使用Amazon Simple Storage Service(Amazon S3)作為核心存儲服務(wù)。盡管Amazon S3擁有豐富的功能選項與完整性,但卻缺少一種開箱即用的機(jī)制將用戶標(biāo)識符同包含用戶數(shù)據(jù)的S3對象映射起來。
在本文中,我們將介紹一套框架,幫忙清除您組織中的亞馬遜云科技托管數(shù)據(jù)湖內(nèi)的各特定用戶數(shù)據(jù)。此外,我們還將共同了解一套由多種不同亞馬遜云科技存儲層構(gòu)成的分析解決方案,以及針對Amazon S3的示例代碼。
參考架構(gòu)
為了解決數(shù)據(jù)清除框架實施中的種種挑戰(zhàn),我們在這里將問題簡化為一個簡單用例,即如何在使用亞馬遜云科技作為數(shù)據(jù)管道的平臺當(dāng)中實現(xiàn)用戶數(shù)據(jù)刪除。下圖說明了用例的基本情況。
我們引入了建立并維護(hù)索引元存儲庫的想法,該存儲庫能夠跟蹤每位用戶的記錄位置,幫助我們高效找出這些位置,從而縮小搜索空間。
您可以使用以下架構(gòu),在組織的亞馬遜云科技數(shù)據(jù)湖內(nèi)刪除特定用戶的數(shù)據(jù)。
對于此初始版本,我們創(chuàng)建了三個用戶流,這些用戶流負(fù)責(zé)將各項任務(wù)與合適的亞馬遜云科技服務(wù)映射起來:
用戶流1:實時元數(shù)據(jù)存儲更新
S3 ObjectCreated或ObjectDelete事件會觸發(fā)一項Amazon Lambda函數(shù),此函數(shù)將解析對象并執(zhí)行添加/更新/刪除操作,以使元數(shù)據(jù)索引保持最新。您也可以為任意其他存儲層建立類似的簡單工作流,具體包括Amazon Relational Database Service(RDS),Amazon Aurora或Amazon Elasticsearch Service(ES)。在本示例中,我們使用Amazon DynamoDB與Amazon RDS for PostgreSQL作為索引元數(shù)據(jù)存儲選項,這里使用的具體方法廣泛適用于其他技術(shù)場景。
用戶流2:清除數(shù)據(jù)
當(dāng)用戶要求刪除其數(shù)據(jù)時,我們會通過Amazon CloudWatch觸發(fā)一個AWS Step Functions狀態(tài)機(jī)來協(xié)調(diào)工作流。第一步是觸發(fā)Lambda函數(shù),由該函數(shù)查詢元數(shù)據(jù)以識別出包含用戶記錄的存儲層,并將生成的報告保存在S3報告存儲桶內(nèi)。接下來,由基于Lambda Node JS的工作節(jié)點創(chuàng)建并獲取Step Functions活動,并通過Amazon Simple Email Service(SES)將帶有批準(zhǔn)及拒絕鏈接的電子郵件發(fā)送給審核人員。
下圖所示為亞馬遜云科技管理控制臺上顯示的Step Functions狀態(tài)機(jī)基本架構(gòu)。
審核人員從兩條鏈接中選擇其一,而后調(diào)用Amazon API Gateway端點,由該端點調(diào)用Step Functions以恢復(fù)工作流。如果選擇批準(zhǔn)鏈接,則Step Functions將觸發(fā)一項Lambda函數(shù),此函數(shù)將存儲桶內(nèi)的報告作為輸入,據(jù)此刪除存儲層內(nèi)的對象或記錄,而后更新索引元存儲庫。在清除作業(yè)完成之后,Amazon Simple Notification Service(SNS)會向用戶發(fā)送操作成功或失敗的通知郵件。
下圖所示,為清除流程成功完成之后,控制臺上的實際Step Functions執(zhí)行流。
關(guān)于完整代碼庫,請參閱GitHub repo中的step-function-definition.json文件。
用戶流3:批量元數(shù)據(jù)存儲更新
此用戶流主要面向需要創(chuàng)建索引元存儲的現(xiàn)有數(shù)據(jù)湖用例。您可以通過Amazon Step Functions進(jìn)行流程編排,將歷史數(shù)據(jù)作為輸入并通過批處理作業(yè)更新元存儲庫。本文中的實現(xiàn)方案并不包含此用戶流的示例腳本。
我們的框架
現(xiàn)在,我們將具體介紹實現(xiàn)過程中使用的兩個用例:
您在每個Amazon S3文件中存儲有多條用戶記錄
用戶將各記錄存儲在同類Amazon Web Service存儲層內(nèi)
以這兩種用例為基礎(chǔ),我們將演示實現(xiàn)索引元數(shù)據(jù)存儲的幾種替代方法。
按S3 URI與行號建立索引
在此用例中,我們可以使用免費的RDS Postgres實例實現(xiàn)索引存儲。首先,使用以下代碼創(chuàng)建一個簡單表:
CREATE UNLOGGED TABLE IF NOT EXISTS user_objects (
userid TEXT,
s3path TEXT,
recordline INTEGER
);
您可以按user_id建立索引,借此優(yōu)化查詢性能。在上傳對象時,您需要在user_objects表中插入用于標(biāo)識用戶ID的行、標(biāo)識目標(biāo)Amazon S3對象的URI,以及對應(yīng)的行記錄。例如,當(dāng)上傳以下JSON輸入時,請輸入以下代碼:
{"user_id":"V34qejxNsCbcgD8C0HVk-Q","body":"…"}
{"user_id":"ofKDkJKXSKZXu5xJNGiiBQ","body":"…"}
{"user_id":"UgMW8bLE0QMJDCkQ1Ax5Mg","body ":"…"}
我們將Amazon S3位置s3://gdpr-demo/year=2018/month=2/day=26/input.json元組信息插入到user_objects表中,詳見以下代碼:
(“V34qejxNsCbcgD8C0HVk-Q”,“s3://gdpr-demo/year=2018/month=2/day=26/input.json”,0)
(“ofKDkJKXSKZXu5xJNGiiBQ”,“s3://gdpr-demo/year=2018/month=2/day=26/input.json”,1)
(“UgMW8bLE0QMJDCkQ1Ax5Mg”,“s3://gdpr-demo/year=2018/month=2/day=26/input.json”,2)
您可以在任意Amazon S3 ObjectCreated事件上觸發(fā)Lambda函數(shù),借此實現(xiàn)對索引的更新操作。
當(dāng)我們收到來自用戶的刪除請求時,則需要查詢索引以獲取關(guān)于數(shù)據(jù)存儲位置的相關(guān)信息。具體請參見以下代碼:
SELECT s3path,
ARRAY_AGG(recordline)
FROM user_objects
WHERE userid = ‘V34qejxNsCbcgD8C0HVk-Q’
GROUP BY;
以上示例SQL查詢將返回如下行:
(“s3://gdpr-review/year=2015/month=12/day=21/review-part-0.json“,{2102,529})
輸出表明,S3對象s3://gdpr-review/year=2015/month=12/day=21/review-part-0.json中的第529行與第2102行中包含請求的用戶數(shù)據(jù),需要清除。接下來,我們需要下載對象、刪除這些行,然后覆蓋對象。關(guān)于實現(xiàn)此功能的Lambda函數(shù)的Python實現(xiàn),請參閱GitHub repo中的deleteUserRecords.py。
可用記錄行能夠幫助我們以字節(jié)格式高效執(zhí)行刪除操作。為了簡化實施過程,我們使用空的JSON對象替換已刪除的行,借此快速實現(xiàn)行清除。此項操作只會帶來少量存儲開銷,且消除了對索引內(nèi)的后續(xù)元數(shù)據(jù)行進(jìn)行更新的高成本操作需求。要消除空J(rèn)SON對象,我們可以采用離線vaccum配合索引更新的方式。
按文件名索引,按索引鍵分組
在此用例中,我們創(chuàng)建了一個DynamoDB表以存儲索引信息。之所以選擇DynamoDB,是因為其擁有良好的易用性與可擴(kuò)展性;您可以使用按需計費模型,因此無需猜測可能需要的具體容量單位。在將文件上傳至數(shù)據(jù)湖后,Lambda函數(shù)將解析文件名(例如1001-.csv)以標(biāo)記用戶標(biāo)識符,并據(jù)此填充DyanmoDB元數(shù)據(jù)表。Userid為分區(qū)鍵,每個不同存儲層都擁有自己的屬性。例如,如果用戶1001在Amazon S3及Amazon RDS中擁有數(shù)據(jù),則其記錄將類似于以下形式:
{"userid:":1001,"s3":{"s3://path1","s3://path2"},"RDS":{"db1.table1.column1"}}
關(guān)于此功能的Python實例示例,請參閱GitHub repo中的update-dynamo-metadata.py。
根據(jù)刪除請求,我們需要查詢元數(shù)據(jù)存儲表(即DynamoDB)并生成清除報告,該報告中包含關(guān)于那些存儲層內(nèi)包含用戶記錄的詳細(xì)信息,同時提供有助于加快記錄查找速度的其他提示信息。我們將清除報告存儲在Amazon S3當(dāng)中。關(guān)于實現(xiàn)此邏輯的示例Lambda函數(shù),請參閱GitHub repo中的generate-purge-report.py。
在清除獲得批準(zhǔn)之后,我們將使用清除報告作為輸入,借此刪除所有對應(yīng)資源。關(guān)于Lambda函數(shù)的實現(xiàn)示例,請參閱GitHub repo中的gdpr-purge-data.py。
實現(xiàn)與技術(shù)替代方案
我們探索并評估了多種實現(xiàn)方案,意識到不同的方案各有所長、也都在某些方面有所妥協(xié),包括實現(xiàn)方式的簡單性、執(zhí)行效率、關(guān)鍵數(shù)據(jù)合規(guī)性以及功能完整性等等:
掃描數(shù)據(jù)文件中的每條記錄以創(chuàng)建索引—每次上傳文件時,我們都會遍歷其記錄并生成元組(包含userid,s3Uri,row_number),而后將其插入至我們的元數(shù)據(jù)存儲層內(nèi)。在刪除請求時,我們將獲取所請求的用戶ID的元數(shù)據(jù)記錄,下載相應(yīng)的S3對象,就地執(zhí)行刪除,而后重新上傳經(jīng)過更新的對象以覆蓋現(xiàn)有對象。這是最為靈活的實現(xiàn)方法,因為其支持通過單一對象存儲多個用戶的數(shù)據(jù),也成為目前最為常見的普遍實現(xiàn)方法。但靈活性也有其代價,由于過程中需要下載并重新上傳對象,因此刪除操作往往會帶來網(wǎng)絡(luò)瓶頸。用戶活動數(shù)據(jù)集(例如客戶產(chǎn)品評論)就特別適合使用此種方法,因為各個分區(qū)(例如日期分區(qū))中幾乎很少出現(xiàn)同一用戶發(fā)布多條記錄的情況,且最好是將多個用戶的活動合并到單一文件當(dāng)中。參考按S3 URI與行號建立索引部分的說明,您可以在GitHub repo當(dāng)中找到相關(guān)示例代碼。
將元數(shù)據(jù)存儲為文件名前綴—在按查詢模式定義的不同分區(qū)之下,將用戶ID設(shè)定為上傳對象的名稱前綴,能夠幫助我們減少刪除請求所需要的搜索操作。元數(shù)據(jù)處理實用程序能夠從文件名中直接查找用戶ID,并相應(yīng)執(zhí)行索引維護(hù)操作。這種方法能夠帶來極高的資源清除效率,但每個對象只能對應(yīng)一個用戶,且要求我們將用戶ID存儲在文件名當(dāng)中,這有可能與信息安全要求相違背。這套方案特別適合管理點擊流數(shù)據(jù),在此類數(shù)據(jù)流中,會話期間單一日期分區(qū)上的單一用戶將產(chǎn)生多個點擊事件。根據(jù)我們之前在按文件名索引、按索引鍵分組部分的說明,您可以從GitHub rep中下載相關(guān)代碼庫。
使用元數(shù)據(jù)文件—除了上傳新對象之外,我們還可以上傳可供索引工具使用的元數(shù)據(jù)文件,借此創(chuàng)建并維護(hù)最新索引。根據(jù)刪除請求,我們可以查詢索引、借此將我們指向需要清除的記錄位置。此方法最適合在上傳新對象時,同步上傳對應(yīng)元數(shù)據(jù)文件的情況(例如上傳多媒體數(shù)據(jù))。在其他場景下,在每一次上傳對象時都同時上傳元數(shù)據(jù)文件,可能給資源容量帶來沉重壓力。
使用亞馬遜云科技服務(wù)的標(biāo)簽功能—每當(dāng)有新文件被上傳至Amazon S3時,我們都會使用Put Object Tagging Amazon S3操作為用戶標(biāo)識添加鍵值對。而每當(dāng)出現(xiàn)用戶數(shù)據(jù)刪除請求時,即可使用該標(biāo)簽獲取對象并將其刪除。使用現(xiàn)有Amazon S3 API即可輕松實現(xiàn)這套方案,整個過程相當(dāng)輕松易行。但這套方案也面臨著諸多限制,其假定Amazon S3對象與用戶之間始終為1:1的關(guān)系(每個對象僅包含單一用戶的數(shù)據(jù));此外,基于標(biāo)簽進(jìn)行對象搜索的方法效率不高,且將用戶標(biāo)識存儲為標(biāo)簽形式的作法也可能有違組織內(nèi)的信息安全要求。
使用Apache Hudi—Apache Hudi已經(jīng)成為Amazon S3之上實現(xiàn)記錄層級數(shù)據(jù)刪除功能的一種非常流行的選擇。Hudi的最新版本僅限于Amazon EMR使用,因此只適合從零開始構(gòu)建數(shù)據(jù)湖的用戶,即要求您在創(chuàng)建過程中將數(shù)據(jù)存儲為Hudi數(shù)據(jù)集形式。Hudi項目本身相當(dāng)活躍,預(yù)計其后續(xù)還將迎來更多功能,并與更多亞馬遜云科技服務(wù)實現(xiàn)集成。
在具體方法的選擇當(dāng)中,我們始終要求將數(shù)據(jù)存儲層與元數(shù)據(jù)存儲層區(qū)分開來。因此,這里提出的各種設(shè)計方案具備良好的通用性,能夠直接插入任何現(xiàn)有數(shù)據(jù)管道當(dāng)中。與選擇數(shù)據(jù)存儲層相似,大家在選擇存儲索引方案時也需要考慮到以下重要因素:
請求并發(fā)性—如果不打算同時插入過多請求,您甚至可以考慮直接將Amazon S3這類簡單存儲方案作為初始索引選項。但如果需要面向眾多用戶處理多項并發(fā)寫入,則最好選擇那些具備更強(qiáng)事務(wù)處理能力的服務(wù)。
考慮團(tuán)隊的現(xiàn)有專業(yè)知識與基礎(chǔ)設(shè)施—在本文中,我們演示了如何使用DyanmoDB與RDS Postgres存儲及查詢元數(shù)據(jù)索引。如果您的團(tuán)隊在這方面沒有任何經(jīng)驗,而且對Amazon ES,Amazon DocumentDB(兼容MongoDB)或者其他存儲層的效果基本滿意,不妨直接使用。另外,如果您已經(jīng)擁有一套具備冗余容量的MySQL數(shù)據(jù)庫,也可以將其作為索引實現(xiàn)方案以節(jié)約運營成本。
索引大小—元數(shù)據(jù)的體量往往要比實際數(shù)據(jù)低幾個量級。但是,隨著數(shù)據(jù)集規(guī)模的顯著增長,您可能需要考慮采用具備強(qiáng)大可擴(kuò)展能力的分布式存儲解決方案,借此替換傳統(tǒng)的關(guān)系數(shù)據(jù)庫管理系統(tǒng)。
總結(jié)
GDPR的公布給最佳實踐帶來重大影響,也為數(shù)據(jù)湖的設(shè)計與實施引入了一系列額外的技術(shù)挑戰(zhàn)。希望本文中提出的參考架構(gòu)與腳本,能夠幫助大家以符合GDPR要求的方式實現(xiàn)數(shù)據(jù)刪除。
原標(biāo)題:How to delete user data in an AWS data lake