cassandra百亿级数据库迁移实践
為什么80%的碼農都做不了架構師?>>> ??
遷移背景
cassandra集群隔段時間出現rt飆高的問題,帶來的影響就是請求cassandra短時間內出現大量超時,這個問題發生已經達到了平均兩周一次的頻率,已經影響到正常業務了。而出現這些問題的原因主要有以下3點:
遷移方案
整個遷移方案主要分為以下5個步驟:
mysql的分庫分表方案
- 集合結構的轉成json之后長度都在1000個字符以內的,可以直接轉成json用varchar來保存,優點:處理起來簡單。缺點:需要考慮集合的數據增長問題。
- 轉成json之后長度比較長,部分已經達到上萬個字符了,用單獨的一張表來保存。優點:不用考慮集合的數據增長問題。缺點:處理起來麻煩,需要額外維護新的表。
全量遷移方案調研
copy導出:通過cqlsh提供的copy命令,把keyspace導出到文件。 缺陷:
- 在測試過程中,導出速度大概4500行每秒,在導出過程中偶爾會有超時,導出如果中斷,不能從中斷處繼續。
- 如果keyspace比較大,則生成的文件比較大。所以這種方式不考慮
sstableloader方式:這種方式僅支持從一個cassandra集群遷移到另一個cassandra集群。所以該方式也不考慮
token環遍歷方式:cassandra記錄的存儲原理是采用的一致性hash的策略<br>整個環的范圍是[Long.MIN_VALUE, Long.MAX_VALUE],表的每條記錄都是通過partition key進行hash計算,然后確定落到哪個位置。
- 例如有這樣一張表:
- 通過以下兩個cql就可以遍歷該張表:
- 循環以上兩個過程,直到token(a, b) = LONG.MAX_VALUE,表示整個表遍歷完成。最終采用了該方式。以上幾個方案都有一個共同的問題,在遷移過程中,數據有變更,這種情況需要額外考慮。
全量遷移詳細過程
最終采用了以上方案3,通過遍歷cassandra表的token環的方式,遍歷表的所有數據,把數據搬到mysql中。具體如下:
- single模式:逐一insert至mysql。數據量不大的情況選擇,單表億級別以下選擇,在64個線程情況下,16個線程讀cassandra的情況下,速度可以達到1.5w行每秒。
- batch模式:batch insert至mysql。數據量比較大的情況下選擇,單表過億的情況下選擇。最大的一張100億數據量的表,遷移過程實際上峰值速度只有1.6w行每秒的速度。這是因為cassandra讀這部分達到瓶頸了。本身線上應用耗掉了部分資源。如果cassandra讀沒有達到瓶頸,速度翻倍是沒問題的。
- 異常處理問題:由于本身cassandra和mysql的字段限制有一定區別。在這個過程肯定會遇到部分記錄因為某列不符合mysql列的限制,導致寫入失敗,寫入失敗的記錄會記錄到文件。這一過程最好是在測試過程中覆蓋的越全越好。具體的一些case如下:
- cassandra text長度超過mysql的限制長度
- cassandra為null的情況,mysql字段設置為is not null(這種情況需要創建表的時候多考慮)
- cassandra的timestamp類型超過了mysql的datetime的范圍(eg:1693106-07-01 00:00:00)
- cassandra的decimail類型超過了mysql的decimail范圍(eg:6232182630000136384.0)
- 數據遺漏問題:由于部分表的字段比較多,代碼中字段轉換的時候最好仔細一點。我們這邊遇到過字段錯亂、字段漏掉等問題。再加上該過程沒有測試接入,自己開發上線了,數據遷移完成后才發現字段漏掉,然后又重頭再來,其中最大的一張表,從頭遷一次差不多需要花掉2周的時間。現在回過頭去看,這張表當初遷移的時候,還不止返工一次。這個過程實際上是非常浪費時間的。
- 慢查詢問題:在最大的一張表的遷移過程中,超時比其他小表要嚴重一些。并且在跑的過程中發現,速度越跑越慢,排查發現是部分線程遇到了某個token查詢始終超時的情況。然后線程一直死循環查詢查token。當把cassandra超時時間設置為30s時,這種情況有所改善,但還存在極個別token存在該問題。此處有一點奇怪的是,通過登錄到線上cassandra機器,通過cqlsh直接查詢,數據是能夠查詢出來的。最終處理方案是針對該token加了5次重試,如果還是不成功,則記錄日志單獨處理。
增量遷移詳細過程
記錄全量遷移開始的時間,以及記錄這段時間所有變更的account(一個user包含多個account),把這部分數據發往kafka。再通過額外的增量遷移程序消費kakfa的方式把這部分數據搬到mysql,循環往復該過程,直到mysql中的數據追上cassandra中的數據。
數據比對
為什么有該步驟?為了確保cassandra和mysql數據源盡可能的一致。
- 時間精度的問題:cassandra的timestamp時間戳精確到毫秒(cassandra的一個客戶端工具DevCenter查詢出來的時間只精確到秒,毫秒部分被截斷了,如果通過該工具肉眼比對,不容易發現該問題),而mysql的datetime默認條件只精確到了秒。
- decimal小數位問題:cassandra中采用的decimal,對應mysql的字段類型是decimal(18,2),cassandra中如果是0或者0.000,遷移到mysql中會變成0.00,需要注意該精度問題。
- 兩張表來保存同一份數據導致臟數據問題:由于cassandra查詢有很多限制,為了支持多種查詢類型。創建了兩張字段一模一樣的表,除了primary key不一樣。然后每次增刪改的時候,兩張表分別都增刪改,雖然這種方式帶來了查詢上的遍歷,但是產生臟數據的幾率非常大。在比對的過程中,發現同一份數據兩張表的數據量相差不小,排查發現由于早期代碼bug導致表一寫成功,表二寫失敗這種情況(好在的是這些數據都是很早之前的數據,所以直接忽略該問題)。而遷移至mysql,只遷移一張表過去。如果兩張表的數據不能完全一致,必然有接口表現不一致。我個人對這種一份數據保存兩份用法也是不推薦的,如果不在物理層做限制,只通過代碼邏輯層來保證數據的一致性,是幾乎不可能的事。
- 空字符和NULL的問題:cassandra中""空字符串的情況下轉換至mysql變為了NULL,這種情況會帶來接口返回的數據不一致的問題,在不確定下游如何使用該數據的時候,最好保證完全一致。
- 字段漏掉的問題:比對發現有張表的一個字段漏掉了,根本沒有遷移過去,除了需要重新全量遷移該表。并且增量遷移也需要重頭再來(盡量避免該問題,該過程是非常耗時的)。
- cassandra數據不一致的問題:同一條select查詢語句,連續查詢兩次返回的結果數不一致。這個比例在萬分之一-千分之一,帶來的問題就是有的數據始終是比較不過的。
- 應用本地時鐘不一致導致的問題:現象就是隨著應用的發版,某張表的lastModifyTime的時間,出現了cassandra比mysql小的情況,而從業務角度來說,mysql的時間是正確的。大概有5%的這種情況,并且不會降下去。可能隨著下一次發版,該問題就消失了。近10次發版有3次出現了該問題,最終排查發現,由于部署線上應用機器的本地時鐘相差3秒,而cassandra會依賴客戶端的時間,帶來的問題就是cassandra后提交的寫入,可能被先提交的寫入覆蓋。為什么該問題會隨著發版而偶然出現呢?因為應用是部署在容器中,每次發版都會分配新的容器。
開雙寫
經過以上步驟,基本可以認為cassandra和mysql的數據是一致的。然后打開雙寫,再關閉增量遷移。這時候如果雙寫有問題,通過比對程序也能夠發現。
切mysql讀
雙寫大概一周后,沒什么問題的話,就可以逐步按服務切mysql讀,然后就可以下線cassandra數據庫了。
總結
- 表的設計:特別需要注意partition key的設計,盡量要保證單個partition的數據量不要太大。
- 墓碑機制:需要注意cassandra的本身的墓碑機制,主要產生的墓碑的情況,主要是delete操作和insert null字段這兩種情況。我們這里曾經因為某個用戶頻繁操作自己app的某個動作,導致數據庫這邊頻繁的對同一個partition key執行delete操作再insert操作。用戶執行操作接近上百次后,導致該partition產生大量墓碑,最終查詢請求打到該partition key。造成慢查詢,應用超時重試,導致cassandra cpu飆升,最終導致其他partition key也受到影響,大量查詢超時。
- cassandra客戶端時鐘不一致的問題,可能導致寫入無效。
- 全量遷移和增量遷移,最好在上線之前測試充分,千萬注意字段漏掉錯位的問題,盡可能的讓測試參與。在正式遷移之前,最好在線上創建一個預備庫,先可以預跑一次。盡可能的發現線上正式遷移時遇到的問題。否則正式遷移的時候遇問題的時候,修復是比較麻煩的。
- 在切或關閉讀寫過程中,一定要有回滾計劃。
版權聲明 作者:wycm
出處:https://my.oschina.net/wycm/blog/3046173
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
轉載于:https://my.oschina.net/wycm/blog/3046173
總結
以上是生活随笔為你收集整理的cassandra百亿级数据库迁移实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python+selenium中webd
- 下一篇: loadrunner编写脚本常用策略,用