[譯] 讓 Apache Cassandra 尾部延遲減小 10 倍,已開源

stormluke發表於2018-04-03

在 Instagram,我們的資料庫是全球最大的 Apache Cassandra 部署之一。我們於 2012 年開始用 Cassandra 取代 Redis,來支援欺詐檢測、資訊流和 Direct 收件箱等產品需求。最初我們在 AWS 環境中執行 Cassandra 叢集,但當其他 Instagram 服務遷移到 Facebook 的基礎設施上時,我們也遷過去了。對我們來說 Cassandra 的可靠性和可用性體驗都很不錯,但是在讀取延遲上仍有改進空間。

去年,Instagram 的 Cassandra 團隊開始致力於一個專案,目標是顯著減少 Cassandra 的讀取延遲,我們稱之為 Rocksandra。在這篇文章中,我將介紹該專案的動機、我們克服的挑戰以及在內部環境和公共雲環境中的效能指標。

動機

在 Instagram 我們大量使用 Apache Cassandra 作為通用的鍵值儲存服務。大部分 Instagram 的 Cassandra 請求都是實時(Online)的,為了向巨量的 Instagram 使用者提供可靠和快速的使用者體驗,我們對這些指標的 SLA(服務等級協議,Service Level Agreement)非常嚴格。

Instagram 維護 5-9 秒的可靠性 SLA,這意味著在任何時候,請求失敗率應該小於 0.001%。為了提高效能,我們實時監控不同 Cassandra 叢集的吞吐量和延遲,尤其是 P99 讀取延遲。

下圖展示了生產環境中的一個 Cassandra 叢集的客戶端延遲。藍線是平均讀取延遲(5ms),橙線是 P99 讀取延遲(在 25ms 到 60ms 的範圍內,並隨著客戶端流量變化而變動)。

[譯] 讓 Apache Cassandra 尾部延遲減小 10 倍,已開源

[譯] 讓 Apache Cassandra 尾部延遲減小 10 倍,已開源

經過調查,我們發現 JVM 垃圾收集器(GC)對延遲峰值作出了很大貢獻。我們定義了一個叫做 GC 暫停(GC stall)百分比的度量標準,用於度量 Cassandra 伺服器在 stop-the-world GC(新生代 GC)並且無法響應客戶端請求時所佔時間百分比。這是另一張圖,顯示了我們生產環境 Cassandra 伺服器的 GC 暫停百分比。在流量最小的時間段內,這一比例為 1.25%,在高峰時段可以高達 2.5%。

該圖顯示 Cassandra 伺服器會把 2.5% 的執行時間用於垃圾收集,而不是響應客戶端請求。GC 開銷顯然對我們的 P99 延遲有很大影響,所以如果能夠降低 GC 暫停百分比,也就能夠顯著降低 P99 延遲。

解決方案

Apache Cassandra 是一個分散式資料庫,它使用自己以 Java 編寫的基於 LSM 樹的儲存引擎。我們發現儲存引擎中的某些元件,例如 memtable、壓縮、讀/寫的程式碼路徑等等,在 Java 堆中建立了很多物件,並給 JVM 增加了很多開銷。為了減少儲存引擎帶來的 GC 問題,我們考慮了不同的方法,最終決定開發一個 C++ 儲存引擎來替代現有的引擎。

我們不想從頭開始構建新的儲存引擎,因此決定在 RocksDB 之上構建新的儲存引擎。

RocksDB 是一款開源的高效能嵌入式資料庫,用於處理鍵值資料。它用 C++ 編寫,並且提供了 C++、C 和 Java 的官方 API。RocksDB 針對效能進行了優化,尤其是針對 SSD 這樣的快速儲存裝置。它在業界被廣泛用作 MySQL、mongoDB 和其他流行資料庫的儲存引擎。

挑戰

在 RocksDB 上構建新的儲存引擎時,我們克服了三個主要挑戰。

第一個挑戰是 Cassandra 的架構不支援可插拔的儲存引擎,就是說現有的儲存引擎與資料庫中的其他元件耦合在一起。為了在大量重構和快速迭代之間找到平衡,我們定義了一個新的儲存引擎 API,包括最常見的讀/寫和流介面。通過這種方式,我們可以在 API 後面構建新的儲存引擎,並將其插入到 Cassandra 內部的相關程式碼路徑中。

其次,Cassandra 支援豐富的資料型別和表模式,而 RocksDB 只提供純粹的鍵值介面。我們仔細地定義了編碼/解碼演算法,以便在 RocksDB 的資料結構之上支援 Cassandra 的資料模型,並支援與原始 Cassandra 相同的查詢語義。

第三個挑戰是流介面。流傳輸是像 Cassandra 這樣的分散式資料庫的重要組成部分。我們新增或移除 Cassandra 叢集中的節點時,Cassandra 需要在不同節點之間傳輸資料以平衡叢集中的負載。現有的流傳輸實現是基於當前儲存引擎中的內部細節的。因此,我們必須將它們分離開,建立一個抽象層,並使用 RocksDB API 重新實現流傳輸。為了提高流吞吐量,目前我們先將資料寫入到 temp sst 檔案,然後使用 RocksDB ingest file API 將它們一次性批量載入到 RocksDB 中。

效能指標

經過大約一年的開發和測試,我們已經完成了第一個版本的實現,併成功在 Instagram 內部將其推广部署到多個 Cassandra 叢集。在我們的其中一個生產叢集中,P99 讀取延遲從 60ms 降至 20ms。我們還觀察到,該群集上的 GC 暫停從 2.5% 下降到 0.3%,足足減小了 10 倍!

我們還想驗證 Rocksandra 在公共雲環境中是否會表現良好。我們使用三個 i3.8 xlarge EC2 例項在 AWS 環境中配置 Cassandra 叢集,每個例項都有 32 個 CPU 核心,244GB 記憶體以及 4 個 nvme 快閃記憶體磁碟組成的 raid0。

我們使用 NDBench 作為基準測試框架,並使用這個框架中預設的表模式:

TABLE emp (
  emp_uname text PRIMARY KEY,
  emp_dept text,
  emp_first text,
  emp_last text`
)
複製程式碼

我們預載入了 2.5 億行每行 6KB 的資料到資料庫中(每個伺服器在磁碟上儲存大約 500GB 資料),並在 NDBench 中配置了 128 個讀取端和 128 個寫入端。

我們測試了不同的負載並測量了平均/P99/P999的讀/寫延遲。如你所見,Rocksandra 提供了更低且更穩定的尾部讀/寫延遲。

[譯] 讓 Apache Cassandra 尾部延遲減小 10 倍,已開源

[譯] 讓 Apache Cassandra 尾部延遲減小 10 倍,已開源

我們還測試了只讀負載,並觀察到在相似的 P99 讀取延遲(2ms)下,Rocksandra 可以提供 10 倍的讀取吞吐量(Rocksandra 為 300K/s,C* 3.0 為 30K/s)。

[譯] 讓 Apache Cassandra 尾部延遲減小 10 倍,已開源

[譯] 讓 Apache Cassandra 尾部延遲減小 10 倍,已開源

展望

我們已經開源了 Rocksandra 程式碼庫基準測試框架,你可以從 Github 上下載並在自己的環境中嘗試!請讓我們知道它的表現。

作為下一步,我們正在積極開發更多的 C* 功能支援,如二級索引,資料修復等等。我們還在開發一個 C* 可插拔儲存引擎架構,將我們的工作回饋給 Apache Cassandra 社群。

如果您身處灣區,並有興趣瞭解更多關於 Cassandra 開發的資訊,請參加我們的下一次 聚會活動

Dikang Gu 是 Instagram 的一名基礎架構工程師


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章