DynamoDB 是 Amazon 基於《 Dynamo: Amazon’s Highly Available Key-value Store 》實現的 NoSQL 數據庫服務。它能夠知足數據庫無縫的擴展,能夠保證數據的持久性以及高可用性。開發人員沒必要費心關注 DynamoDB 的維護、擴展、性能等一系列問題,它由 Amazon 徹底託管,開發人員能夠將更多的精力放到架構和業務層面上。數據庫
本文主要介紹做者所在團隊在具體業務中所遇到的挑戰,基於這些挑戰爲什麼最終選型使用 Amazon DynamoDB,在實踐中遇到了哪些問題以及又是如何解決的。文中不會詳細討論 Amazon DynamoDB 的技術細節,也不會涵蓋 Amazon DynamoDB 的所有特性。微信
TalkingData 移動廣告效果監測產品(TalkingData Ad Tracking)做爲廣告主與媒體之間的一個廣告投放監測平臺,天天須要接收大量的推廣樣本信息和實際效果信息,並最終將實際的效果歸因到推廣樣本上。架構
舉個例子,咱們經過手機某新聞類 APP 瀏覽信息,會在信息流中看到穿插的廣告,廣告多是文字形式、圖片形式、視頻形式的,而無論是哪一種形式的廣告它們都是能夠和用戶交互的。負載均衡
若是廣告推送比較精準,恰好是用戶感興趣的內容,用戶可能會去點擊這個廣告來了解更多的信息。一旦點擊了廣告,監測平臺會收到這一次用戶觸發的點擊事件,咱們將這個點擊事件攜帶的全部信息稱爲樣本信息,其中可能包含點擊的廣告來源、點擊廣告的時間等等。一般,點擊了廣告後會引導用戶進行相關操做,好比下載廣告推薦的 APP,當用戶下載並打開 APP 後,移動廣告監測平臺會收到這個 APP 發來的效果信息。到此爲止,對於廣告投放來講就算作一次成功轉化。
框架
DynamoDB實踐:當數據量巨大而不可預知,如何保證高可用與實時性?函數
移動廣告監測平臺須要接收源源不斷的樣本信息和效果信息,並反覆、不停的實時處理一次又一次轉化。對於監測平臺來說,它的責任重大,不能多記錄,也不能少記錄,若是轉化數據記多了廣告主須要多付廣告費給媒體,記少了媒體會有虧損。這樣就爲平臺帶來了幾大挑戰:性能
在 2017 年 6 月前咱們的業務處理服務部署在機房,使用 Redis Version 2.8 存儲全部樣本數據。Redis 使用多節點分區,每一個分區以主從方式部署。在最開始咱們 Redis 部署了多個節點,分紅多個分區,每一個分區一主一從。剛開始這種方式尚未出現什麼問題,但隨着用戶設置的樣本有效期加長、監測樣本增多,當時的節點數量逐漸已經不夠支撐業務存儲量級了。若是用戶監測推廣量一旦暴增,咱們系統存儲將面臨崩潰,業務也會癱瘓。因而咱們進行了第一次擴容。優化
因爲以前的部署方式咱們只能將 Redis 的多個節點翻倍擴容,這一切都須要人爲手動操做,而且在此期間咱們想盡各類辦法來保護用戶的樣本數據。
DynamoDB實踐:當數據量巨大而不可預知,如何保證高可用與實時性?編碼
這種部署方式隨着監測量的增加以及用戶設定有效期變長會愈來愈不堪重負,當出現不可預知的突發量時就會產生嚴重的後果。並且,手動擴容的方式容易出錯,及時性低,成本也是翻倍的增加。在當時因爲機器資源有限,不只 Redis 須要擴容,廣告監測平臺的一系列服務和集羣也須要進行擴容。架構設計
通過討論和評估,咱們決定將樣本處理等服務遷移到雲端處理,同時對存儲方式從新選型爲 Amazon DynamoDB,它可以知足咱們的絕大部分業務需求。通過結構調整後系統大概是下圖的樣子:
DynamoDB實踐:當數據量巨大而不可預知,如何保證高可用與實時性?
此外還有:
咱們在使用一些開源框架或服務時總會遇到一些「坑」,這些「坑」其實也能夠理解爲沒有很好的理解和應對它們的一些使用規則。DynamoDB 和全部服務同樣,也有着它本身的使用規則。在這裏主要分享咱們在實際使用過程當中遇到的問題以及解決辦法。
在 DynamoDB 中建立表時須要指定表的主鍵,這主要爲了數據的惟一性、可以快速索引、增長並行度。主鍵有兩種類型,「單獨使用分區鍵」做爲主鍵和「使用分區鍵 + 排序鍵」做爲主鍵,後者能夠理解爲組合主鍵(索引),它由兩個字段惟一肯定 / 檢索一條數據。DynamoDB 底層根據主鍵的值對數據進行分區存儲,這樣能夠負載均衡,減輕單獨分區壓力,同時 DynamoDB 也會對主鍵值嘗試作「合理的」分區。
在開始咱們沒有對主鍵值作任何處理,由於 DynamoDB 會將分區鍵值做爲內部散列函數的輸入,其輸出會決定數據存儲到具體的分區。但隨着運行,咱們發現數據開始出現寫入偏移了,並且很是嚴重,帶來的後果就是致使 DynamoDB 表的讀寫性能降低,具體緣由在後面會作詳細討論。發現這類問題以後,咱們考慮了兩種解決辦法:
因此咱們選擇了第二種方法,調整業務代碼,在寫入時將主鍵值作哈希,查詢時將主鍵條件作哈希進行查詢。
在解決了數據偏移以後讀 / 寫性能恢復了,可是運行了一段時間以後讀寫性能卻再次降低。查詢了數據寫入並不偏移,當時咱們將寫入性能提高到了 6 萬 +/ 秒,但沒起到任何做用,實際寫入速度也就在 2 萬 +/ 秒。最後發現是咱們的分區數量太多了,DynamoDB 在後臺自動維護的分區數量已經達到了 200+ 個,嚴重影響了 DynamoDB 表的讀寫性能。
DynamoDB 自動擴容、支持用戶任意設定的吞吐量,這些都是基於它的兩個自動擴容規則:單分區大小限制和讀寫性能限制。
DynamoDB 會自動維護數據存儲分區,但每一個分區大小上限爲 10GB,一旦超過該限制會致使 DynamoDB 拆分區。這也正是數據偏移帶來的影響,當數據嚴重偏移時,DynamoDB 會默默爲你的偏移分區拆分區。咱們能夠根據下面的公式計算分區數量:
數據總大小 / 10GB 再向上取整 = 分區總數
好比表裏數據總量爲 15GB,15 / 10 = 1.5,向上取整 = 2,分區數爲 2,若是數據不偏移均勻分配的話兩個分區每一個存儲 7.5GB 數據。
DynamoDB 爲何要拆分區呢?由於它要保證用戶預設的讀 / 寫性能。怎麼保證呢?依靠將每一個分區數據控制在 10G 之內。另外一個條件就是當分區不能知足預設吞吐量時,DynamoDB 也會將分區進行擴充。DynamoDB 對於每一個分區讀寫容量定義以下:
也就是說,一個分區的最大寫入容量單位和讀取容量單位是固定的,超過了分區最大容量單位就會拆分區。所以咱們能夠根據下面的公式計算分區數量:
(預設讀容量 /3000)+(預設寫容量 /1000)再向上取整 = 分區總數
好比預設的讀取容量爲 500,寫入容量爲 5000,(500 / 3000) + (5000 / 1000) = 5.1,再向上取整 = 6,分區數爲 6。
須要注意的是,對於單分區超過 10G 拆分後的新分區是共享原分區讀寫容量的,並非每一個表單獨的讀寫容量。
由於預設的讀寫容量決定了分區數量,但因爲單分區數據量達到上限而拆出兩個新的分區。
因此當數據偏移嚴重時,讀寫性能會急劇降低。
產生上面的問題是因爲咱們一開始是單表操做。這樣就算數據不偏移,但隨着時間推移數據量愈來愈多,天然拆出的分區也愈來愈多。
所以,咱們根據業務作了合理的拆表、設定了冷熱數據表。這樣作有兩大好處:
表對於數據的大小以及數量並無限制,能夠無限制的往一張表裏寫入數據。但對於 AWS 的一個帳戶,每一個 DynamoDB 使用區域的限制爲 256 張表。對於一個公司來講,若是共用同一個帳號的話可能會存在建立表受限的風險。因此若是啓用了冷熱表策略,除了刪冷表下降成本外,也是對 256 張表限制的一種解決辦法。
上面提到了寫入單位每條數據最大 1KB、讀取單位每條最大 4KB 的限制。單條數據的大小除了字段值佔用字節外,屬性名也會佔用字節,所以在保證可讀性的前提下應儘可能縮減表中的屬性名。
DynamoDB 的使用也是存在成本的,主要體如今寫入和讀取的費用。咱們本身研發了一套按照實際流量實時調整讀、寫上限的策略。隨着發展 DynamoDB 也推出了 Auto Scaling 功能,它實現了自定義策略動態調整寫入與讀取上限的能力,對於開發者來講又能夠省去了很多研發精力。目前咱們也有部分業務使用了 Auto Scaling 功能,但因爲該功能的限制,實際使用上動態調整的實時性略顯欠缺。
做者介紹: