一、項目介紹
汽車之家社區於 2005 年上線,作為之家最老的業務之一,十四年來沉淀了億級帖子、十億級回復數據,目前每天有千萬級 DAU、億級的訪問量,接口日均調用量 10億+次 。期間經歷過架構升級重構、技術棧升級等,但其數據始終存放在SQL Server中,隨着數據的不斷遞增,我們在使用SQL Server 數據庫方面遇到了很多瓶頸,以至於我們不得不尋找一個新的數據庫替換方案。
二、使用SQL Server遇到的瓶頸
隨着業務的不斷擴大,汽車之家社區的訪問量和發表量不斷上漲,遇到的數據庫問題也越來越多,下面列舉兩個必須很快要解決掉的問題:
-
歷史上,之家社區回復庫采用了分庫分表的設計,用以解決SQL Server單表過大的時候性能下降等問題。時至今日,回復庫有100+個庫、1000+張表(根據帖子ID分庫分表)。這本身並沒有問題,代碼寫好了,數據該寫哪里寫哪里,該讀哪里讀哪里。但是隨着應用的發展、需求的變化,我們發現在實現某些需求時,分庫分表的結構難以滿足。我們需要數據邏輯上在一張表里。
-
近些年來,隨着業務加速成長,數據量突飛猛進,而硬盤容量是有限的,每台服務器上能擴展的硬盤數量也是有限的。致使每隔一段時間都要增加更大容量的存儲服務器來應對,而且這個事情一開始是很復雜的,涉及到很多關聯項目,即便到現在我們輕車熟路了,每次換服務器的時候依然需要關注它,並且大容量數據庫服務器價格昂貴。我們需要讓擴容對應用來說,無感知。
三、分布式數據庫調研3.1 確定方向
在2018年底的時候,公司專門成立了虛擬架構組來調研新的數據來解決之家社區遇到問題,經過各種分析和測試,今年年初確定方向為分布式數據庫,一共調研了三款當前比較火的分布式數據庫:TiDB(PingCap)、Ignite(ASF-TLP)、CockroachDB。經過無數次測試我們最終選擇了TiDB,主要有以下幾個原因:
-
兼容MySQL協議與生態,上手門檻低
-
跟TiDB官方一直保持比較好的技術溝通
-
TiDB公司在北京,有問題可以當面解決
-
TiDB的設計架構更加優秀
-
官方社區比較活躍,文檔豐富
-
官方的技術人員經常到公司進行交流
下面引用TiDB官方的一段描述:“TiDB 是一款定位於在線事務處理、在線分析處理( HTAP: Hybrid Transactional/AnalyticalProcessing)的融合型數據庫產品,實現了一鍵水平伸縮,強一致性的多副本數據安全,分布式事務,實時 OLAP 等重要特性。同時兼容 MySQL協議和生態,遷移便捷,運維成本極低。“從中我們不難發現,TiDB切實的解決了我們在應用SQL Server時候的痛點:
-
水平伸縮:在當前集群內可以隨時加節點,更換節點也輕而易舉。
-
海量數據支持:基於其特性以及業內使用的經驗,十億乃至百億級別的數據量輕松搞定。
-
高可用:相較SQL Server的主從模式,TiDB使用基於Raft協議,可以實現100%的數據強一致性,並且多數副本可用的情況下,可實現自動故障恢復。
-
HTAP:TiDB自身就支持一定程度的OLAP場景,更復雜的OLAP分析可以通過 TiSpark 項目來完成。對於更深度的OLAP應用,我們也已經在實踐的路上。
3.2 實踐出真知
基於以上理論的支持,我們進行了大量的功能測試、性能測試、異常測試、業務接入測試等。
-
OLTP測試:2000萬數據,500並發線程測試,在OLTP場景測試下TiDB的響應時間99%在16ms以內,滿足業務需求。且在數據量級越來越大的情況下,TiDB會體現出更大的優勢,后續還可以通過添加TiDB/PD/TiKV節點來提高讀寫性能。
-
OLAP測試:50G TPC-H測試,TiDB相較MySQL有很大的速度優勢。
| Query |
TiDB(second) |
MySql(second) |
| 1 |
201.89 |
1285.14 |
| 2 |
30.62 |
35.78 |
| 3 |
133.73 |
1789.76 |
| 4 |
31.47 |
311.68 |
| 5 |
600.01 |
553.70 |
| 6 |
77.13 |
298.15 |
| 7 |
78.81 |
818.32 |
| 8 |
512.43 |
1542.10 |
| 9 |
309.06 |
3478.29 |
| 10 |
48.31 |
595.37 |
| 11 |
37.86 |
201.42 |
| 12 |
600.01 |
385.05 |
| 13 |
121.17 |
648.64 |
| 14 |
68.92 |
336.48 |
| 15 |
0.01 |
1080.20 |
| 16 |
90.70 |
192.12 |
| 17 |
315.73 |
94.43 |
| 18 |
600.00 |
308.18 |
| 19 |
94.02 |
139.46 |
| 20 |
87.58 |
569.54 |
| 21 |
600.012 |
1410.61 |
| 22 |
62.07 |
47.89 |
*TPC Benchmark™H(TPC-H)是決策支持基准。它由一套面向業務的臨時查詢和並發數據修改組成。選擇查詢和填充數據庫的數據具有廣泛的行業范圍相關性。該基准測試說明了決策支持系統,該系統可檢查大量數據,高度復雜地執行查詢並為關鍵業務問題提供答案。
-
異常測試: 我們測試了PD、TiKV異常宕機情況下的表現,對業務影響很小,可實現自動故障恢復。
四、遷移方案4.1 遷移前需要解決的問題在真正的數據遷移之前,我們還有一些實際問題需要解決:
-
SQL Server 和 TiDB 的部分字段類型是不一樣的,通過查閱相關文檔,將不同的字段一一對應后再在TiDB中建表,例如DATETIME的精度問題。
-
同步時將分庫分表的數據合並到一個表里,值得慶幸的是原有設計中,我們除了自增主鍵 ID,還有一份業務 ID,其在各個表中均不重復,這樣省了不少事情。
-
一次性導入十億級數據以及后續增量同步的過程中,如何保證數據的一致性。
-
如果TiDB在生產時出現了不可預估的問題,一時無法解決,那我們必須立刻切換到SQL Server,保證業務不受影響。換句話說,在TiDB中產生的數據需要實時同步回SQL Server。
-
因為訪問量比較大,切換時間必須控制在秒級。
-
因為SQL Server是商業數據庫,跟開源數據庫進行數據同步的方案較少,所以同步方案、架構設計、研發、測試必須我們自己解決。
下面會詳細介紹全量和增量同步的實施方案。
五、全量同步
首先我們要感謝以下兩個開源項目,站在巨人的肩膀上使我們節約了很多時間。
https://github.com/alibaba/yugonghttps://github.com/alswl/yugong
愚公是阿里巴巴推出的一款Oracle數據遷移同步工具,而作者alswl在此基礎上實現了SQL Server數據源的支持。
在此愚公的使用方法我們不再贅述,感興趣的同學請自行查看。
在認真拜讀了大神的項目,並進行了相關測試后,發現它並不能100%滿足我們的需求。
Yugong 數據流是標准 ETL 流程,分別有 Extractor、 Translator、Applier 這三個大類來實現 ETL 過程。
首先講Extractor,愚公原有的配置方式是將需要導出的庫表寫在配置文件當中,這對於1000+張表來說,太不現實了。這里我們增了一個新特性,在不配置需要導出的表名的情況下,將數據庫中所有的用戶表讀出來,並通過一個新增的配置項進行正則匹配,以此決定哪些表需要進行數據同步。
#查詢表SELECT name FROM sys.databases WITH (nolock) WHEREstate_desc = 'ONLINE'#查詢開啟CDC的表SELECT name FROM %s.sys.tables t WITH (nolock) JOIN%s.[cdc].[change_tables] ct WITH (nolock) ON t.object_id = ct.source_object_id
其次,合庫合表后,原有SQL Server中各個表的自增主鍵ID沖突,所以新增實現RowDataMergeTranslator,其功能是,讀取內存中的 RowData然后進行轉換,將從SQL Server中讀取的行數據,丟棄其原有的主鍵列,轉而使用TiDB生成。並根據配置文件決定哪些表需要實現這一特性。
record.removeColumnByName(config.getDiscardKey());
最后的Applier並未做改動,處理好的數據直接寫入TiDB。
自此合庫合表的事情我們解決了。
六、增量同步與實時校驗
在實現這部分需求的時候,我們應用了SQL Server的CDC,並在增量同步的基礎上增加了延遲驗證數據正確性的功能。
更多關於CDC的內容,這里不再贅訴,你只需要知道它能獲取到增量數據。
#CDC官方文檔https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-data-capture-sql-server?view=sql-server-ver15
需要注意的是,CDC開啟的時機需要在全量同步之前,保證CDC記錄可以覆蓋全量同步過程中產生的增量數據。
根據以上的流程圖可以看到,Producer從SQL Server中讀取CDC日志,並將其轉化成一條包含表信息、列信息和行數據的消息,投遞到Kafka中。下游的消費者在拉取到消息之后,把數據轉化成兼容MySQL的SQL語句在TiDB中執行(這里也實現了合庫合表),從而實現整個數據增量同步的過程。
這里還有另一個消費者實現數據校驗功能,它會延遲五秒消費同一隊列,並通過提取主鍵(或索引)的方式從TiDB中查出該條已經寫入的數據,將兩側的整行數據做比較(本實踐中去除主鍵后比較),如果有問題會進行嘗試重新寫入,如出現異常則向相關人員發送報警。
在實現了這些並進入到測試階段后,我們發現了一個問題,1000+回復表,對應1000+CDC日志表,一個Producer就需要開啟1000+線程,以設計的5s間隔去輪詢這些表時,服務器CPU直接就跑滿了,產生了大量線程等待,輪詢CDC日志的及時性無法保證。通過分析業務和DBA查詢得知,其實之家社區每天產生的回復有95%都集中在最新的5%的帖子當中,換言之,我們只有幾十張表需要如此高頻的去檢索CDC日志,其他的表我們通過增加輪詢間隔、分批部署等方式,將這個問題解決了。
細心的同學讀到這里會發現,校驗功能其實邏輯上並不嚴謹,如果說在五秒鍾內上游數據產生了變更,就有可能會產生拿着新數據去校驗老數據的問題。這里有兩個解決方案,一、采用單partition的topic和單個消費程序,保證增量同步和校驗的順序嚴格一致,但此種方案性能相對較低,可用性無法保證。二、我們將SQL Server中的表行加入上版本戳(rowversion),將版本戳一並同步到TiDB中,校驗時比較該值,如不一致則放棄本次校驗,本方案會損失一定的校驗樣本,但可通過增加Partition和消費者提高性能和可用性。
七、回滾方案
之前我們提到了,當項目切換到TiDB以后,需要預防其出現不可預估的問題,能夠隨時切回SQL Server才能保障萬無一失。
TiDB的binlog使得這件事情輕而易舉,我們使用官方提供的Pump和Drainer將binlog抽取到Kafka之中,解析數據變更的內容,根據業務ID計算出數據在SQL Server中原本屬於哪個庫哪個表,然后進行數據同步。
通過業務ID決定數據寫到哪個庫表八、之家社區業務TiDB遷移改造
就業務的改造這一環節,因歷史積淀,需修改的地方很多,分布於各個項目之中,我們采取通過接口查找實現、搜索代碼、DBA幫助抓取SQL的方式,保證涵蓋了100%的相關業務,只有這樣才能保障上線后無故障。
-
數據訪問層增加對MySQL語法的支持。
-
去掉SQL Server中的存儲過程。
-
SQL Server和TiDB(MySQL)的語句和函數支持不盡相同,逐個改造、測試並優化。
-
根據TiDB索引的原理以及梳理出來的SQL語句,重新建索引。
與此同時,我們針對每一條改造后的SQL都進行了優化,使可以精確的命中最優的索引,從而實現了在十億級數據量下,TP業務99%的響應時間在12ms,99.9%的響應時間在62ms。
九、TiDB周邊體系建設
除以上遷移流程所涉及到的功能點以外,我們還制定了一些開發規范和一些實用工具的研發,用以保障TiDB在汽車之家更好的應用。
-
我們建立了完善的TiDB開發規范、運維規范、上線規范,並在公司內部對開發同學進行相關的培訓。
-
開發了實時慢SQL分析工具——TiSlowSQL,該工具可以提供實時、多維度、全視角的SQL報告,幫助我們快速定位慢SQL導致的集群級故障。
-
為解決監控單點問題,我們自己開發了一套監控工具,對TiDB核心組件進行監控,后續會將監控系統統一遷移到之家雲平台。
-
定期在汽車之家大學舉行技術培訓,定期在組內進行技術分享,經驗總結。
十、總結與展望
汽車之家社區已於9月底正式上線分布式數據庫TiDB,目前運行穩定。在其他業務遷移完成之后,之家社區的SQL Server服務會逐步下線。對於本次遷移的過程我們做了以下幾點總結:
-
通過不斷的優化SQL,目前線上TP99 穩定,與遷移之前並無太大差別,跟測試效果相符。對用戶和業務都無感知。
-
隨着業務的不斷擴大,可以更好的應對數據的暴增,再擴容集群就不需要找昂貴的大存儲機器,而且可以在線不停業務隨時擴容。
-
本次遷移我們積累了SQL Server轉TiDB的很多經驗,可以為其他團隊使用分布式數據庫TiDB提供技術支持,讓其他團隊在遷移過程中節省時間。
-
目前正在與TiDB官方溝通,准備把遷移方案和與業務無關的遷移邏輯放到開源社區。
由SQL Server遷移至TiDB,從傳統關系型到分布式HTAP ,從商業授權到開源社區,是汽車之家社區歷史上一次重大的技術方向轉型。之家有很多海量數據的應用場景,這一次從SQL Server到分布式數據庫TiDB的遷移,為我們以后其他業務遷移至 TiDB 打下了良好的基礎,也與TiDB官方建立了良好的定期溝通機制,希望TiDB官方一如既往的快速迭代,我們也會和TiDB官方合作開發一些比較實用的功能。
