技術選型是由技術方向和業務場景 trade-off 決定的,脫離業務場景來說技術選型是沒有任何意義的,所以本文只是闡述了伴魚技術團隊數據庫選型的過程,這並不是 MySQL、MongoDB 和 TiDB 之間直接的比較,只能說明 TiDB 更適合伴魚的業務場景和技術規劃,另外由於 TiDB 是非常新的數據庫技術,所以這也能體現出伴魚技術團隊對新技術的態度、技術後發優勢的理解、成本與效率的衡權和技術生態與紅利的思考。

爲什麼放棄 MongoDB?

伴魚是 2015 年成立的,那個時候 NoSQL 還如日中天,關係型數據庫爲了應付海量的數據只能業務侵入式的分庫分表,雖然 Google 在 2012 年發佈了 NewSQL 數據庫 Spanner 的論文,但是工業界還沒有一款可以使用的 NewSQL 數據庫,綜合當時的各種情況,伴魚選擇的是 MongoDB。

不過,在 2015 年到 2017 年之間,對於伴魚來說 MongoDB 確實是一個上佳之選,主要有以下幾個方面的原因:

  • 開發更高效 公司初期處於探索期,產品迭代非常快,MongoDB 是 NoSQL 數據庫,不需要做建庫建表等 DDL 操作,特別在產品快速迭代,需要頻繁增減字段的時候就更高效,當然這個也是有代價的,從本質上來說,MongoDB 是讀模式,它幾乎不檢查寫入的內容是否合法,對數據 Schema 的解釋是在應用程序的代碼中,導致寫入數據的約束性是沒有保證的。

  • 運維更高效 當時公司研發非常少,這段時間整個後端只有兩個工程師,沒有專職的運維和 DBA ,但是 MongoDB 的單機性能比 MySQL 要高不少,不但對數據庫的運維成本要低不少,並且當時除了幾個熱點庫外,其他的庫 MongoDB 可以直接扛住流量壓力,省去了中間的 Cache 層,讓開發和運維都更高效。

  • 有事務需求的場景不多 當時使用的是 MongoDB 2.x 和 3.x,只提供了數據一致性的選擇(強一致性、單調一致性和最終一致性)和原子操作,在少數的幾個場景,比如交易相關的場景,通過選擇強一致性和原子操作,再在應用層實現 MVCC 的機制,可以滿足簡單的事務需求。

總體來說,在伴魚產品的探索期,爲了效率犧牲一點數據約束性和事務能力是值得的,但是 2017 年底伴魚產品方向比較明確後,業務場景從探索期轉變到快速發展期,對數據庫的需求從效率優先轉變爲效率、事務能力與生態並重:

  • 有事務需求的場景急增: 事務場景從最初與錢相關的交易擴展到一些虛擬貨幣,並且由於併發量的增加,之前沒有事務保障的場景出現競爭的情況越來越多,還在應用層通過 MVCC 機制實現簡單的事務是非常低效的,並且在應用層實現事務的正確性也是很難保證的。(一個有趣的故事:Jeff Dean 曾經說過自己對 Bigtable 最後悔的事情是沒有提供跨行事務支持,導致業務就會在上層企圖自己搞事務,並且業務實現的分佈式事務大部分都是錯的,所以在後來的 Spanner 數據庫中 Jeff Dean 直接提供了官方分佈式事務支持。)

  • 對大數據生態的需求急增: 在產品探索期的時候,也有很強的數據分析需求,不過當時數據總量小,在 MongoDB 的隱藏從庫中直接分析就足夠了,但是產品快速發展期,數據量急劇增加,在 OLTP 數據庫中進行 OLAP 操作已經力不從心了。但是通過大數據生態來進行數據分析,對於 MongoDB 來說有一個非常殘酷的現實,基本所有的大數據生態都是圍繞 MySQL 生態打造的,如果想接入 MongoDB 的數據,意味着需要重新大量造輪子。

  • 對數據約束性的要求更高: 由於業務快速的發展,服務可能會出現多人維護和移交的情況,如果存儲的數據沒有約束,意味着存儲的數據 Schema 是不可控的,這很容易讓後面參與的工程師崩潰和掉進坑裏,這個時候數據的約束性變成是一個更高優秀級的需求,關係數據庫的寫模式變成更好的選擇。

到產品快速發展期,由於業務場景對數據庫的需求已經發生了很大的改變,所以在這個時候,伴魚技術團隊開始謹慎思考數據庫重新選型的問題,我們理想型的數據庫是這樣的:

  • 高可用;

  • 高吞吐;

  • 支持 ACID 事務;

  • 大數據生態友好;

  • 有水平擴張能力,並且儘量做到不侵入業務;

基於上面這些需求,我們開始了數據庫的重新選型之路。

初識 TiDB

早在 2015 年的時候我就非常關注分佈式數據庫,當時已經經歷過高併發高 QPS 的場景,利用分佈式架構解決無狀態高併發高 QPS 場景是不復雜的,但是分佈式存儲由於涉及到一致性問題是非常有挑戰的,如果還想支持 ACID 事務那就更難了,所以當時就對分佈式數據庫技術特別感興趣,開始關注 OceanBase 並且收集和研究相關的理論和架構文檔。

後來同事推薦說有個叫 TiDB 的數據庫,目前有些公司也在用了,反饋也不錯,所以我們決定調研一下。TiDB 官網的文檔做的非常友好,不論是理論還是架構的文章都非常齊全,幾乎一口氣就把所有的文章都看了一遍(當時文章比現在要少),完備的理論支持、優雅的架構設計、與 Google Spanner 一脈相承的設計思路讓我們對 TiDB 的前景非常看好,並且功能上完全滿足我們的要求,所以當時就決定長期關注 TiDB,並且準備進行初步驗證。

初步驗證

通過調研我們發現,TiDB 是通過 raft 協議來保證多副本數據的一致性( ACID 中的 C ),通過 2PC 協議來保證事務的原子性( ACID 的 A ),通過樂觀鎖加 MVCC 來實現可重複讀的事務隔離級別( ACID 中 I ),這意味着 TiDB 每一次事務的成本是比 MySQL 要高很多的,特別是有事務衝突的時候(樂觀鎖的原因),所以性能是需要驗證的關鍵點。

當時伴魚所有的業務都部署在阿里雲上(現在有自建機房),就直接在阿里雲上面按 TiDB 的配置要求購買了機器,當時安裝的是 TiDB 1.x 的版本。因爲 TiDB 官網已經有 Sysbench 的壓力測試數據,這個性能數據是符合我們需求的,所以決定對我們的業務場景進行一次完全模擬的長期測試:伴魚 IM 的併發比較高,並且採用寫擴散的設計,對數據庫的要求會比較高,所以適合進行驗證。通過對 IM 業務的 inbox 表進行雙寫,業務同步寫 MongoDB 和異步寫 TiDB,業務讀只讀 MongoDB,這樣如果 TiDB 有問題也不會影響線上業務。

在低峯期對 IM 業務開啓雙寫後,TiDB 監控的 999 線和 99 線還滿足要求,但是所有 TiKV 節點的 io 使用率一直在 90% 附近波動,這個如果到高峯期是絕對會有問題的,通過重讀 TiKV 的配置,在修改配置項 sync-log = false 後,TiKV 的 io 使用率維持在 5% 以下,當天的高峯期也一切正常,沒有出現問題。

在這之後,我們對 IM 的雙寫觀察 2-3 個月的時間,在確認一切正常後,再將同步讀寫都修改爲 TiDB 並且異步寫 MongoDB,一切正常並且持續觀察。

sync-log 配置是控制 TiKV 數據多副本進行 raft 協議同步的時候,如果 sync-log=false,則內存中處理完成就返回 ack,對於 3 副本來說,單節點故障是不會丟失數據的,同一個 raft 集的 2 個副本同時故障可能會出現丟數據的情況,這個問題除了金融等對數據安全性要求非常高的場景外,其他的業務場景是可以接受的,並且 MySQL 等其他數據庫的集羣方案在 master 節點故障的時候問題更大。

深度交流

在前面初步驗證 TiDB 的過程中,一個看似很嚴重的問題但是調整一個配置就可以解決,這讓我們發現了我們對 TiDB 的理解和控制力還不夠,在對每一個配置都進行理解研究外,還有一些我們非常關心的問題但沒有官方答案。如果對這些問題沒有官方答案,那麼我們直接使用 TiDB 就是有很大風險的,所以我們決定和 TiDB 團隊進行一次深度的交流。

我們當時非常關心的問題列表爲:

TiKV 的線性擴展能力怎麼樣?

  • 兩地三中心架構,TiDB 可以容忍數據中心之間的延遲是多少?

  • 目前業界 TiDB 最大的一個集羣的 TiKV 和 TiDB 的節點數、數據量、QPS 最高是多少?

  • TiDB 哪一些配置是需要特別關注和調整的?

收集了大概 20 多個問題,得益於伴魚和 TiDB 都在北京,離得還非常近,在線上聯繫上並且約好時間後,和 TiDB 進行了第一次深度的交流。

大概是 2018 年上半年的一天,我和 TiDB 的 3-4 個同事聊了一整個上午,基本都是我將收集到的問題一個個拋出來,大家一起討論。整個交流過程解答了很多我們關心的問題,也瞭解到當前業界對 TiDB 的使用情況,大大增強了我們對 TiDB 的信心,對於數據庫的選型來說,這是非常關鍵的事情。

爲什麼不選擇 MySQL?

經過對 TiDB 的調研、試用和深入交流後,在傳統的關係型數據庫 MySQL 和 NewSQL 數據庫 TiDB 之間,我們需要做出自己的選擇了,這不僅僅是兩個數據庫之間的選擇,這其實也體現了伴魚對新技術的態度、技術後發優勢的理解、成本與效率的衡權和技術生態與紅利的思考。

對新技術的態度

伴魚對新技術的態度是非常積極的,如果業務場景需要的新技術我們都會去了解它、研究它和掌握它,我們相信我們對新技術趨勢的判斷能力和掌控能力,所以在 TiDB 和 MySQL 的選型的過程中,MySQL 確實是非常穩的選擇,並且對我們的需求目前都有現成的解決方案,比如高可用,比如水平擴展能力,只不過不是非常優雅的解決方案,但是 TiDB 無論是理論層面和架構層面都比 MySQL 高出一個時代(MySQL 是面向單機數據庫設計的,是這個領域非常優秀的數據庫,只是現在伴魚想要解決的是單機無法存儲的海量數據場景,在這個維度上比較確實 TiDB 更好一些,但是這並不是 MySQL 的問題,是因爲它們的設計目標不同而已),但是穩定性和成熟度會比 MySQL 要差一些,這個時候,我們選擇相信我們對 NewSQL 技術方向的判斷力和掌控力,相信 TiDB 的進化能力,相信時間站在我們這邊,讓子彈再飛一會。

技術後發優勢的理解

伴魚在之前用的數據庫是 MongoDB,MySQL 和 TiDB 都沒有用過,如果我們判斷 TiDB 更面向未來的數據庫,那麼我們是先從 MySQL 開始,走一遍 MySQL 的道路,在後面可預見的未來再遷移到 TiDB 上來;還是直接深入研究和掌握 TiDB,直接 All in TiDB?

初創公司在技術沉澱和積累上是遠遠不及一些成熟公司的,這些沉澱和積累就是成熟公司在技術上的先發優勢,當技術沒有出現變革的時候我們沒有選擇,但是當技術正出現重大變革的時候,如果我們還做同樣的技術選型,那麼也需要花同樣的時間和成本才能達到成熟公司的水平,然後等大家都開始遷移到新的技術上的時候,這些技術沉澱和積累就可能會變成技術債務。

所以初創公司應該去預判技術趨勢,選擇面向未來的技術,在技術上彎道超車,避免自己的技術債務,這個是伴魚技術團隊對技術後發優勢的理解。

成本與效率的衡權

成本和效率是技術選型繞不過的關鍵點,對於數據庫來說更是如此,因爲數據庫需要的機器等資源成本會佔總資源成本的很大一部分,所以伴魚技術團隊在 TiDB 和 MySQL 做選擇的時候,對成本與效率進行了深度的評估。

Unix 哲學是經過時間和實踐的錘鍊設計原則,很多時候也是伴魚技術團隊的實踐原則,比如 Rule of Economy :「寧花機器一分,不花程序員一秒」。在技術選型上,我們是總是期望基礎軟件做更多的事情,業務研發做更少的事情,如果業務研發需要在業務層去做原本基礎軟件應該做好的事情,那其實就是基礎軟件的抽象泄漏了。如果基礎軟件抽象泄漏,必然會導致業務層重複去解決這個問題,這個其實是非常大的隱性成本。

MySQL 相比較 TiDB 而言,集羣的高可用和大表需要分庫分表其實就是 MySQL 在對面當前需求的抽象泄漏,MySQL 的集羣高可用需要 DBA 和基礎架構團隊花成本去解決,MySQL 的大表分庫分表方案需要 DBA、基礎架構團隊和業務研發團隊花成本去解決,只不過這些都是隱性成本,不像在搭建數據庫集羣的時候,TiDB 比 MySQL 可能需要更多的機器來的簡單直接,所以很容易被忽略了。

所以,對於成本與效率的衡權,伴魚技術團隊更關注工程師的效率,更關注工程師的心情(在業務上層重複解決一些底層軟件抽象泄漏的問題是很影響心情的),更關注隱性成本,而不僅僅是賬面明顯可以比較的資源數字,特別是在機器越來越便宜,人才越來越值錢的趨勢下。

技術生態與紅利的思考

選擇一個技術,其實也是選擇了這個技術的生態,如果技術生態完善,做事情往往會事半功倍,極大地提高研發效率。TiDB 在這個方面做的非常好,全面兼容 MySQL 協議,讓 TiDB 的用戶享受到 NewSQL 的能力的同時也享受到 MySQL 的生態,這個是非常正確的決定,MySQL 生態是幾十年的積累,不是一朝一夕可以做到的。

另一方面,在選擇面向未來、優雅高效的解決方案,還是選擇成熟的但不夠優雅和高效的解決方案,如果選擇成熟的解決方案,對技術的掌控會比較高,但是會在效率方面持續的進行付出;如果選擇面向未來的解決方案,需要花時間和精力來掌握新技術,但是新技術會優雅和高效的解決問題,我們認爲這個就是技術的紅利。比如對於大表的解決方案,MySQL 提供的解決方案是分庫分表,業務研發和 DBA 一起配合非常低效地解決這個問題,但是對於 NewSQL 的 TiDB,單表幾乎可以理解爲無限大的(業界已經存在 100 億以上的表),從根本上解決了這個問題。現在伴魚的大表都從 MongoDB 遷移到 TiDB 上面,業務研發和 DBA 不再爲數據的增加而不停地進行分庫分表,這個就是巨大的技術紅利。

所以,基於上面的一些討論與思考,伴魚決定 All in TiDB,MongoDB 不再增加新的庫和表,正在使用 MongoDB 的業務繼續使用,並且對 MongoDB 上的大表進行有計劃的遷移,避免進行分庫分表操作。

踩過的坑

在完全掌握一項新技術前,享受新技術的紅利是有代價的,特別是伴魚在 TiDB 比較早期的時候就決定 All in,這很考驗技術團隊的學習和進化能力、新技術的社區和官方提供的技術支持的能力。在 TiDB 這件事情上,伴魚技術團隊和 TiDB 的技術支持團隊都做的非常優秀了,但是我們從 TiDB 1.x 到目前的 3.x 的過程中依然還是踩了一些坑。

優化器選擇索引問題

單表數據 30W+,查詢請求併發約 10+,某次業務上線,新增一個索引後,導致原有的查詢索引選擇錯誤,TiKV 實例所在機器 cpu 迅速被打滿,引發故障。

線上某張大表,請求量比較大,偶爾出現個別條件走不到索引,導致全表掃描,從而引發接口響應時間的抖動,影響業務。

線上某張 14 億的大表,查詢條件區分度很高,某天出現特定條件突然走不到索引,導致全表掃描,引發故障。後面經過 TiDB 同學排查,系 bug 導致。

優化器選擇索引問題,TiDB 從 1.x 到 3.x 的過程中,優化器表現越來越好,同時伴魚 DBA 團隊通過性能監控和慢日誌監控提前快速地發現問題,並且對大表採用強制索引的方式避免隱患,目前這個問題已經比較徹底的解決了。

大數據同步問題

爲了進行數據分析,我們把上游各 TiDB 集羣的數據通過 Pump / Drainer 匯聚到一個 TiDB 集羣供大數據分析使用,在使用過程中,遇到數據不一致、數據同步慢和編碼不一致導致同步失敗等問題。

隨着伴魚的 DBA 團隊深度研究 TiDB 並且和 TiDB 的同學進行持續的深入溝通,目前對 TiDB 的掌控力越來越強,大數據同步問題目前已經得到解決。

現在的情況

現在伴魚有 10 套 TiDB 數據庫,110+ 數據庫實例,6 個 TPS 過萬核心集羣,999 線基本維持在 16ms 左右,響應時間和穩定性都達到預期。從目前的情況來看,伴魚選擇 TiDB 是一次非常正確的選擇,我們在數據庫技術方面彎道超車,避免了對 MySQL 技術的重複建設與積累,享受了 NewSQL 數據庫 TiDB 在高可用和水平擴展等方面的技術紅利,大大提高了業務研發和 DBA 的工作效率。 當然,這是伴魚技術團隊(特別是 DBA)和 TiDB 技術團隊共同努力的結果。

這裏還要特別提一點,TiDB 每一次版本升級都會帶來驚喜,這是一個可以持續享受的技術紅利。

寫在後面的話

目前,在摩爾定律失效、業務的高可用要求和成本優化等綜合的大環境下,分佈式架構是技術潮流的大勢所趨,流量路由策略加多副本部署(微服務是其中的一種架構形式)解決了無狀態服務的分佈式架構問題,Redis Cluster 和 Codis 等方案解決了緩存的分佈式架構問題,Kubernetes 完成了操作系統的分佈式進化,數據庫領域自然也不會例外,它的分佈式架構趨勢一定是不可阻擋的。要特別說明一下,這裏所說的解決問題是指系統性的解決問題,MySQL 業務侵入式的分庫分表確實是一個可以解決問題的分佈式架構方案,但是需要業務研發配合一個業務場景一個業務場景的去解決,這就不能稱之爲系統性的解決方案,因爲在解決這個問題方式上,業務侵入式的分庫分表方案將本應由數據庫處理好的大表抽象泄漏給業務層了,在這個問題上,我們認爲 NewSQL 是一個系統性的解決方案,而 TiDB 就是當下非常不錯的一個選擇。

另外還需要說明一點的是,這是一篇數據庫選型的文章,所以只記錄了與之相關的內容,比如詳細描述了伴魚技術團隊在將數據庫遷移到 TiDB 後踩的坑,因爲這是我們數據庫選型 TiDB 付出的代價,所以一定要詳細記錄;沒有記錄在使用其他數據庫踩的坑,這並不代表我們沒有踩到,比如在使用 MongoDB 的過程中也踩過一些坑,但是因爲這並不是我們決定重新做數據庫選型的原因(決定重新選型的原因見文章「爲什麼放棄 MongoDB」部分),所以就沒有在文章中記錄。

參考閱讀:

https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/spanner-osdi2012.pdf

https://book.douban.com/subject/26197294/

https://pingcap.com/blog-cn/

https://asktug.com/

https://book.douban.com/subject/1467587/

https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/

https://tech.ipalfish.com/blog/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/

:bulb: 本文轉載自   InfoQ 微信公衆號,點擊【閱讀原文】查看原版文章

相關文章