愛奇藝的資料庫選型大法,實用不糾結!

朱小廝的部落格發表於2022-12-08

愛奇藝的資料庫選型大法,實用不糾結!

來源:rrd.me/fgGsG

我們進行資料庫選型的時候要考慮哪些問題?有哪些需求?待選用的資料庫是否和需求對得上?是不是直接可以拿來用?需不需要一些額外的開發?這些都會在本文的分享中提及。

一、資料庫技術選型的思考維度

我們做選型的時候首先要問:

誰選型?是負責採購的同學、 DBA 還是業務研發?

如果選型的是採購的同學,他們更注重成本,包括儲存方式、網路需求等。

如果選型的是 DBA 同學,他們關心的:

 ① 運維成本

首先是運維成本,包括監控告警是否完善、是否有備份恢復機制、升級和遷移的成本是否高、社群是否穩定、是否方便調優、排障是否簡易等;

 ② 穩定性

其次,DBA會關注穩定性,包括是否支援資料多副本、服務高可用、多寫多活等;

 ③ 效能

第三是效能,包括延遲、QPS 以及是否支援更高階的分級儲存功能等;

 ④ 擴充性

第四是擴充套件性,如果業務的需求不確定,是否容易橫向擴充套件和縱向擴容;

 ⑤ 安全

最後是安全,需要符合審計要求,不容易出現 SQL 注入或拖庫情況。

 ⑥ 其他

除了採購和 DBA之外,後臺應用研發的同學同樣會關注穩定性、效能、擴充套件性等問題,同時也非常關注資料庫介面是否便於開發,是否便於修改資料庫 schema 等問題。

愛奇藝的資料庫選型大法,實用不糾結!

接下來我們來看一下愛奇藝使用的資料庫型別:

  • MySQL,網際網路業務必備系統;

  • TiDB,愛奇藝的 TiDB 實踐會有另外的具體介紹;

  • Redis,KV 資料庫,網際網路公司標配;

  • Couchbase,這個在愛奇藝用得比較多,但國內網際網路公司用得比較少,接下來的部分會詳細說明;

  • 其他,比如 MongoDB、圖資料庫、自研 KV 資料庫 HiKV 等;

  • 大資料分析相關係統,比如 Hive、Impala 等等。

可以看到愛奇藝的資料庫種類還是很多的,這會造成業務開發的同學可能不太清楚在他的業務場景下應該選用哪種資料庫系統。

那麼,我們先對這些資料庫按照介面(SQL、NoSQL)和麵向的業務場景(OLTP、OLAP)這兩位維度進行一個簡單非嚴謹的分類。

下圖中,左上角是面向 OLTP、支援 SQL 的這樣一類系統,例如 MySQL,一般支援事務不同的隔離級別, QPS 要求比較高,延時比較低,主要用於交易資訊和關鍵資料的儲存,比如訂單、VIP 資訊等。

愛奇藝的資料庫選型大法,實用不糾結!

左下角是 NoSQL 資料庫,是一類針對特殊場景做最佳化的系統,schema 一般比較簡單,吞吐量較高、延遲較低,一般用作快取或者 KV 資料庫。

整個右側都是 OLAP 的大資料分析系統,包括 Clickhouse、Impala等,一般支援SQL、不支援事務,擴充套件性比較好,可以透過加機器增加資料的儲存量,響應延遲較長。

還有一類資料庫是比較中立的,在資料量比較小的時候效能比較好,在資料量較大或複雜查詢的時候效能也不差,一般透過不同的儲存引擎和查詢引擎來滿足不同的業務需求,我們把它叫做 HTAP,TiDB 就是這樣一種資料庫。

二、iQIYI對資料庫的最佳化與完善

前面我們提到了很多種的資料庫,那麼接下來就和大家介紹一下在愛奇藝我們是怎麼使用這些資料庫的。

1、MySQL在愛奇藝的使用

 ① MySQL

首先是 MySQL。MySQL 基本使用方式是 master-slave + 半同步,支援每週全備+每日增量備份。我們做了一些基本功能的增強,首先是增強了資料恢復工具 Xtrabackup 的效能。

之前遇到一個情況,我們有一個全量庫是 300G 資料,增量庫每天 70G 資料,總資料量 700G 左右。我們當時只需要恢復一個表的資料,但該工具不支援單表恢復,且整庫恢復需要 5 個小時。

針對這個情況我們具體排查了原因,發現在資料恢復的過程中需要進行多次寫盤的 IO 操作並且有很多序列操作,所以我們做了一些最佳化。例如刪減過程中的一些寫盤操作,減少落盤並將資料處理並行化,最佳化後整庫恢復耗時減少到 100 分鐘,而且可以直接恢復單表資料。

然後是適配 DDL 和 DML 工具到內部系統,gh-ostt 和 oak-online-alter-table 在資料量大的時候會造成 master-slave 延時,所以我們在使用工具的時候也增加了延時上的考慮,實時探測Master-Slave 庫之間延時的情況,如果延時較大會暫停工具的使用,恢復到正常水平再繼續。

 ② MySQL高可用

第二是 MySQL 高可用。Master-slave 加上半同步這種高可用方式不太完善,所以我們參照了 MHA 並進行了改動,採用 master + agent 的方式。Agent 在每一個物理機上部署,可以監控這個物理機上的所有例項的狀態,週期性地向 master 傳送心跳,Master 會實時監測各個Agent的狀態。

如果 MySQL故障,會啟動 Binlog 補償機制,並切換訪問域名完成 failover。考慮到資料庫跨機房跨地區部署的情況,MHA 的 master 我們也做了高可用設計,眾多 master 會透過 raft 組成一個 raft group,類似 TiDB 的 PD 模組。目前 MySQL failover 策略支援三種方式:同機房、同地域跨機房以及跨地域。

 ③ MySQL擴充能力

第三是提高MySQL擴充套件能力,以提供更大容量的資料儲存。擴充套件方式有 SDK,例如開源的 ShardingSphere,在愛奇藝的使用也比較廣泛。另外就是 Proxy,開源的就更多了。但是 SDK 和 Proxy 使用的問題是支援的 SQL 語句簡單,擴容難度大,依賴較多且運維複雜,所以部分業務已經遷移至 TiDB。

 ④ 審計

第四是審計。我們在 MySQL 上做了一個外掛獲取全量 SQL 操作,後端打到 Kafka,下游再接入包括 Clickhouse 等目標端進行 SQL 統計分析。除此之外還有安全策略,包括主動探索是否有 SQL 注入及是否存在拖庫情況等,並觸發對應的告警。 

MySQL 審計外掛最大的問題是如何降低對 MySQL 效能的影響,對此我們進行了一些測試,發現使用 General Log 對效能損耗較大,有 10%~20% 的降低。

於是我們透過介面來獲取 MySQL 外掛裡的監控項,再把監控項放到 buffer 裡邊,用兩級的 RingBuffer 來保證資料的寫入不會有鎖資源競爭。在這個外掛裡再啟動一個執行緒,從 RingBuffer 裡讀取資料並把資料打包寫到 FIFO 管道里。

我們在每臺 MySQL 的物理機裡再啟動一個 Agent,從管道里阻塞地讀取資料發至 Kafka。最佳化後我們再次進行壓測,在每臺機器上有 15 萬的更新、刪除或插入操作下不會丟失資料,效能損耗一般情況下小於 2%。

目前已經在公司內部的叢集上線了一年時間,執行比較穩定,上線和下線對業務沒有影響。

 ⑤ 分級儲存

第五是分級儲存。MySQL 裡會存一些過程性的資料,即只需要讀寫最近一段時間存入的資料,過段時間這些資料就不需要了,需要進行定時清理。

分級儲存就是在 MySQL 之上又用了其他儲存方式,例如 TiDB 或其他 TokuDB,兩者之間可以進行資料自動搬遷和自動歸檔,同時前端透過 SDK + Proxy 來做統一的訪問入口。這樣一來,業務的開發同學只需要將資料存入 MySQL 裡,讀取時可能從後端接入的任意資料庫讀出。這種方式目前只是過渡使用,之後會根據 TiDB 的特性進行逐步遷移。

愛奇藝的資料庫選型大法,實用不糾結!

Redis在愛奇藝的使用

接下來是 Redis。Redis 也是使用 master - slave 這種方式,由於網路的複雜性我們對 Sentinel 的部署進行了一些特殊配置,在多機房的情況下每個機房配置一定數量 Sentinel 來避免腦裂。

備份恢復方面介紹一個我們的特殊場景,雖然 Redis 是一個快取,但我們發現不少的業務同學會把它當做一個 KVDB 來使用,在某些情況下會造成資料的丟失。

所以我們做了一個 Redis 實時備份功能,啟動一個程式偽裝成 Redis 的 Slave 實時獲取資料,再放到後端的 KV 儲存裡,例如 ScyllaDB,如果要恢復就可以從 ScyllaDB 裡把資料拉出來。

我們在用 Redis 時最大的痛點就是它對網路的延遲或抖動非常敏感。如有抖動造成 Redis Master 超時,會由 Sentinel 重新選出一個新的節點成為 Master,再把該節點上的資料同步到所有 Slave 上,此過程中資料會放在 Master 節點的 Buffer 裡,如果寫入的 QPS 很高會造成 Buffer 滿溢。如果 Buffer 滿後 RDB 檔案還沒有複製過去,重建過程就會失敗。

基於這種情況,我們對 Redis 告警做了自動化最佳化,如有大量 master - slave 重建失敗,我們會動態調整一些引數,例如把 Buffer 臨時調大等, 此外我們還做了 Redis 叢集的自動擴縮容功能。

我們在做 Redis 開發時如果是 Java 語言都會用到 Jedis。用 Jedis 訪問客戶端分片的 Redis 叢集,如果某個分片發生了故障或者 failover,Jedis 就會對所有後端的分片重建連線。如果某一分片發生問題,整個 Redis 的訪問效能和 QPS 會大幅降低。針對這個情況我們最佳化了 Jedis,如果某個分片發生故障,就只針對這個分片進行重建。

在業務訪問 Redis 時我們會對 Master 繫結一個讀寫域名,多個從庫繫結讀域名。但如果我們進行 Master failover,會將讀寫域名從某舊 Master 解綁,再繫結到新 Master 節點上。

DNS 本身有一個超時時間,所以資料庫做完 failover 後業務程式裡沒有立刻獲取到新的 Master 節點的 IP的話,有可能還會連到原來的機器上,造成訪問失敗。

我們的解決方法是把 DNS 的 TTL 縮短,但對 DNS 服務又會造成很大的壓力,所以我們在 SDK 上提供 Redis 的名字服務 RNS,RNS 從 Sentinel 裡獲取叢集的拓撲和拓撲的變化情況,如果叢集 failover,Sentinel 會接到通知,客戶端就可以透過 RNS 來獲取新的 Master 節點的 IP 地址。我們去掉域名,透過 IP 地址來訪問整個叢集,遮蔽了 DNS 的超時,縮短了故障的恢復時間。

SDK 上還做了一些功能,例如 Load Balance 以及故障檢測,比如某個節點延時較高的話會被臨時熔斷等。

客戶端分片的方式會造成 Redis 的擴容非常痛苦,如果客戶端已經進行了一定量的分片,之後再增加就會非常艱難。

Redis 在 3.0 版本後會提供 Redis Cluster,因為功能受限在愛奇藝應用的不是很多,例如不支援顯示跨 DC 部署和訪問,讀寫只在主庫上等。

我們某些業務場景下會使用 Redis 叢集,例如資料庫訪問只發生在本 DC,我們會在 DC 內部進行 Cluster 部署。

但有些業務在使用的過程中還是想做 failover,如果叢集故障可以切換到其他叢集。根據這種情況我們做了一個 Proxy,讀寫都透過它來進行。寫入資料時 Proxy 會做一個旁路,把新增的資料寫在 Kafka 裡,後臺啟用同步程式再把 Kafka 裡的資料同步到其他叢集,但存在一些限制,比如我們沒有做衝突檢測,所以叢集間資料需要業務的同學做單元化。線上環境的Redis Cluster 叢集間場景跨 DC 同步 需要 50 毫秒左右的時間。

愛奇藝的資料庫選型大法,實用不糾結!

2、Couchbase在愛奇藝的使用

Redis 雖然提供 Cluster 這種部署方式,但存在一些問題。所以資料量較大的時候(經驗是 160G),就不推薦 Redis 了,而是採用另一種儲存方式 Couchbase。

Couchbase 在國內網際網路公司用的比較少,一開始我們是把他當做一個 Memcached 來使用的,即純粹的快取系統。

但其實它效能還是比較強大的,是一個分散式高效能的 KV 系統,支援多種儲存引擎 (bucket)。第一種是 Memcached bucket,使用方式和 Memcached 一樣為 KV 儲存,不支援資料持久化也沒有資料副本,如果節點故障會丟失資料;

第二種是 Couchbase bucket,支援資料持久化,使用 Json 寫入,有副本,我們一般會線上上配置兩個副本,如果新加節點會對資料進行 rebalance,愛奇藝使用的一般是 Couchbase bucket 這種配置。

Couchbase 資料的分佈如下圖,資料寫入時在客戶端上會先進行一次雜湊運算,運算完後會定位 Key 在哪一個 vBucket (相當於資料庫裡的某個分片)。之後客戶端會根據 Cluster Map 傳送資訊至對應的服務端,客戶端的 Cluster Map 儲存的是 vBucket 和伺服器的對映關係,在服務端資料遷移的過程中客戶端的 Cluster Map 對映關係會動態更新,因此客戶端對於服務端的 failover 操作不需要做特殊處理,但可能在 rebalance 過程中會有短暫的超時,導致的告警對業務影響不大。

Couchbase 在愛奇藝應用比較早,2012 年還沒有 Redis Cluster 的時候就開始使用了。叢集管理使用 erlang 語言開發,最大功能是進行叢集間的複製,提供多種複製方式:單向、雙向、星型、環式、鏈式等。

愛奇藝從最初的 1.8 版本使用到如今的 5.0 版本,正在調研的 6.0,中間也遇到了很多坑,例如 NTP 時間配置出錯會導致崩潰,如果每個叢集對外 XDCR 併發過高導致不穩定,同步方向變更會導致資料丟失等等,我們透過運維和一些外部工具來進行規避。

Couchbase 的叢集是獨立叢集,叢集間的資料同步透過 XDCR,我們一般配置為雙向同步。對於業務來說,如果 Cluster 1 寫入, Cluster 2 不寫入,正常情況下客戶端會寫 Cluster 1。如果 Cluster 1 有故障,我們提供了一個 Java SDK,可以在配置中心把寫入更改到 Cluster 2,把原來到 Cluster 1 的連線逐步斷掉再與Cluster 2 新建連線。這種叢集 failover 的過程對於客戶端來說是相對透明和無感的。

愛奇藝的資料庫選型大法,實用不糾結!

3、愛奇藝自研資料庫HiKV的使用

Couchbase 雖然效能非常高,並且資料的儲存可以超過記憶體。但是,如果資料量超過記憶體 75% 這個閾值,效能就會下降地特別快。在愛奇藝,我們會把資料量控制在可用記憶體的範圍之內,當做記憶體資料庫使用。但是它的成本非常高,所以我們後面又開發了一個新的資料庫—— HiKV。

開發 HiKV 的目的是為了把一些對效能要求沒那麼高的 Couchbase 應用遷移到 HiKV 上。HiKV 基於開源系統 ScyllaDB,主要使用了其分散式資料庫的管理功能,增加了單機儲存引擎 HiKV。

ScyllaDB 比較吸引人的是它宣稱效能高於 Cassandra 十倍,又完全相容 Cassandra 介面,設計基本一致,可以視為 C++ 版 Cassandra 系統。 

ScyllaDB 效能的提升主要是使用了一些新的技術框架,例如 C++ 非同步框架 seastar,主要原理是在j每臺物理機的核上會 attach 一個應用執行緒,每個核上有自己獨立的記憶體、網路、IO 資源,核與核之間沒有資料共享但可以通訊,其最大的好處是記憶體訪問無鎖,沒有衝突過程。

當一個資料讀或寫到達 ScyllaDB 的 server 時,會按照雜湊演算法來判斷請求的 Key 是否是該執行緒需要處理的,如果是則本執行緒處理,否則會轉發到對應執行緒上去。

除此之外,它還支援多副本、多資料中心、多寫多活,功能比較強大。

在愛奇藝,我們基於 SSD 做了一個 KV 儲存引擎。Key 放在記憶體裡,Value 放在盤上的檔案裡,我們在讀和寫檔案時,只需要在記憶體索引裡定位,再進行一次盤的 IO 開銷就可以把資料讀出來,相比 ScyllaDB 原本基於 LSM Tree 的儲存引擎方式對 IO 的開銷較少。

索引資料全部放在記憶體中,如果索引長度較長會限制單機可儲存的資料量,於是我們透過開發定長的記憶體分佈器,對於比較長的 Key 做摘要縮短長度至 20 位元組,採用紅黑樹索引,限制每條記錄在記憶體裡的索引長度至為 64 位元組。記憶體資料要定期做 checkpoint,客戶端要做限流、熔斷等。

HiKV 目前在愛奇藝應用範圍比較大,截至目前已經替換了 30% 的 Couchbase,有效地降低了儲存成本。

愛奇藝的資料庫選型大法,實用不糾結!

4、愛奇藝的資料庫運維管理

愛奇藝資料庫種類較多,如何高效地運維和管理這些資料庫也是經歷了不同的階段。

最初我們透過 DBA 寫指令碼的方式管理,如果指令碼出問題就找 DBA,導致了 DBA 特別忙碌。

第二個階段我們考慮讓大家自己去查問題的答案,於是在內部構建了一個私有云,透過 Web 的方式展示資料庫執行狀態,讓業務的同學可以自己去申請叢集,一些簡單的操作也可以透過自服務平臺實現,解放了 DBA。一些需要人工處理的大型運維操作經常會造成一些人為故障,敲錯引數造成資料丟失等。

於是在第三個階段我們把運維操作 Web 化,透過網頁點選可以進行 90% 的操作。

第四個階段讓經驗豐富的 DBA 把自身經驗變成一些工具,比如有業務同學說 MySQL master-slave 延時了,DBA 會透過一系列操作排查問題。現在我們把這些操作串起來形成一套工具,出問題時業務的同學可以自己透過網頁上的一鍵診斷工具去排查,自助進行處理。

除此之外我們還會定期做預警檢查,對業務叢集裡潛在的問題進行預警報告;開發智慧客服,回答問題;透過監控的資料對例項打標籤,進行削峰填谷地智慧排程,提高資源利用率。

愛奇藝的資料庫選型大法,實用不糾結!

三、不同場景下資料庫選型建議

1、實用資料庫選型樹

最後來說一些具體資料庫選型建議。這是 DBA 和業務一起,透過經驗得出來的一些結論。

對於關係型資料庫的選型來說,可以從資料量和擴充套件性兩個維度考慮,再根據資料庫有沒有冷備、要不要使用 Toku 儲存引擎,要不要使用 Proxy 等等進行抉擇。

愛奇藝的資料庫選型大法,實用不糾結!

NoSQL 也是什麼情況下使用 master-slave,什麼情況下使用客戶端分片、叢集、Couchbase、HiKV 等,我們內部自服務平臺上都有這個選型樹資訊。

愛奇藝的資料庫選型大法,實用不糾結!

2、一些思考

① 需求

我們在選型時先思考需求,判斷需求是否真實。

你可以從資料量、QPS、延時等方面考慮需求,但這些都是真實需求嗎?是否可以透過其他方式把這個需求消耗掉,例如在資料量大的情況下可以先做資料編碼或者壓縮,資料量可能就降下來了。

不要把所有需求都推到資料庫層面,它其實是一個兜底的系統。

 ② 選擇 

第二個思考的點是對於某個資料庫系統或是某個技術選型我們應該考慮什麼?是因為熱門嗎?還是因為技術上比較先進?但是不是能真正地解決你的問題?如果你資料量不是很大的話就不需要選擇可以儲存大資料量的系統。

 ③ 放棄 

第三是放棄,當你放棄一個系統時真的是因為不好用嗎?還是沒有用好?放棄一個東西很難,但在放棄時最好有一個充分的理由,包括實測的結果。

 ④ 自研 

第四是自研,在需要自己開發資料庫時可以參考和使用一些成熟的產品,但不要盲目自研。

 ⑤ 開源 

最後是開源,要有擁抱開源的態度。

愛奇藝的資料庫選型大法,實用不糾結! 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69940568/viewspace-2667343/,如需轉載,請註明出處,否則將追究法律責任。

相關文章