自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

1.5億用戶、萬億數(shù)據(jù),爆款社交平臺的兩次大型數(shù)據(jù)庫遷移

開發(fā) 前端
技術(shù)人員都希望,現(xiàn)行數(shù)據(jù)庫能夠滿足不斷增長的存儲需求,同時保持較低的維護需求??上КF(xiàn)實往往事與愿違—— Discord 使用的 Cassandra 集群出現(xiàn)嚴(yán)重的性能問題,技術(shù)人員耗費越來越多的精力,致力于維護數(shù)據(jù)庫,而非改進(jìn)性能。

2017年,Discord 在技術(shù)博客中提到,由于 RAM 中無法再容納數(shù)據(jù)和索引,延遲開始變得不可預(yù)測,急速增長的數(shù)據(jù)存儲亟待遷移。他們希冀找到一款可擴展、容錯且維護成本相對較低的數(shù)據(jù)庫,以實現(xiàn)存儲數(shù)十億條消息的目標(biāo),最終完成了從 MongoDB 到 Cassandra 的遷移。

技術(shù)人員都希望,現(xiàn)行數(shù)據(jù)庫能夠滿足不斷增長的存儲需求,同時保持較低的維護需求??上КF(xiàn)實往往事與愿違—— Discord 使用的 Cassandra 集群出現(xiàn)嚴(yán)重的性能問題,技術(shù)人員耗費越來越多的精力,致力于維護數(shù)據(jù)庫,而非改進(jìn)性能。

時隔六年,Discord 消息存儲再面臨性能挑戰(zhàn),于是將數(shù)據(jù)庫遷移至 ScyllaDB。

這兩次數(shù)據(jù)庫遷移原因幾何?Discord 如何選型?遷移過程中問題又將如何解決?

讓我們一同在下文中尋找答案。

2017:從MongoDB到Cassandra

Discord 的增長速度和用戶生成的內(nèi)容數(shù)量超出了我們的預(yù)期,用戶越多,聊天信息也就越多。2016年7月,平臺消息量已達(dá)4000萬條,12月增至1億條,而截至這篇文章發(fā)布(2017年1月)時,信息量已超過1.2億條。

我們很早就決定永久存儲所有聊天記錄,以便用戶隨時返回查看,并在任何設(shè)備上使用他們的數(shù)據(jù)。而這些龐大數(shù)據(jù)的增長速度及規(guī)模都在持續(xù)攀升,并且需要隨時保持可用。

這一點如何做到?我們的回答是遷移至 Cassandra 數(shù)據(jù)庫。

1.Discord 最初版本

2015年初,Discord 的最初版本在不到兩個月的時間成功創(chuàng)建。在當(dāng)時情況下,MongoDB 是快速迭代的最佳數(shù)據(jù)庫之一。Discord 的所有內(nèi)容都存儲在單個 MongoDB 集群中,這是有意為之,但同時我們也做好一切后續(xù)規(guī)劃,以便將所有數(shù)據(jù)輕松地遷移到新數(shù)據(jù)庫(我們確信不會使用 MongoDB 分片,因為它使用復(fù)雜,且穩(wěn)定性不佳)。

實際上,這是我們公司文化的一部分:快速構(gòu)建以驗證產(chǎn)品功能,但始終為更強大的解決方案留有后路。

消息存儲在 MongoDB 集合中,使用 channel_id 和 created_at 的單一復(fù)合索引。2015 年 11 月左右,存儲消息數(shù)量達(dá)到 1 億條,此時,預(yù)期問題發(fā)生了:RAM 無法再容納數(shù)據(jù)和索引,延遲開始變得不可控,是時候遷移到更合適的數(shù)據(jù)庫了。

2.選擇合適的數(shù)據(jù)庫

選擇新數(shù)據(jù)庫之前,必須了解我們的讀/寫模式,以及我們目前解決方案出現(xiàn)問題的原因。

  • 我們的讀取極其隨機,讀寫比例大約為 50/50。
  • 管理語音聊天的重型 Discord 服務(wù)器幾乎不發(fā)送任何消息,這意味著它們每隔幾天只發(fā)送一兩條消息,這種服務(wù)器在一年內(nèi)發(fā)送的信息幾乎不到 1000 條。問題是,雖然信息量很小,但卻增加了向用戶提供數(shù)據(jù)的難度。只要向用戶返回 50 條信息,就會在磁盤上產(chǎn)生許多隨機尋道,導(dǎo)致磁盤緩存被驅(qū)逐。
  • 管理文字私聊的重型 Discord 服務(wù)器發(fā)送的信息數(shù)量相當(dāng)可觀,每年可輕松達(dá)到 10 萬至 100 萬條,請求數(shù)據(jù)通常都是最新的。問題是,由于這些服務(wù)器通常只有不到 100 名成員,因此請求數(shù)據(jù)的速度很低,磁盤緩存中也不太可能具備這些數(shù)據(jù)。
  • 大型公共聊天 Discord 服務(wù)器會發(fā)送大量信息,其中,成千上萬的成員每天發(fā)送數(shù)千條信息,每年可發(fā)送數(shù)百萬條信息。它們幾乎總是在請求最近一小時內(nèi)發(fā)送的信息,并且請求頻率很高。因此,數(shù)據(jù)通常都在磁盤緩存中。
  • 在接下來的一年里,我們將為用戶提供更多隨機讀取數(shù)據(jù)的方式:查看過去 30 天內(nèi)的提及消息,并跳轉(zhuǎn)到歷史記錄的對應(yīng)內(nèi)容,查看并跳轉(zhuǎn)到標(biāo)記消息,以及全文搜索。以上這些功能都意味著更多的隨機讀??!

隨后,我們來定義下需求:

  • 線性可擴展性:不希望以后重新考慮解決方案或手動重新分揀數(shù)據(jù)。
  • 自動故障轉(zhuǎn)移:我們不希望被夜間打擾,因此系統(tǒng)出現(xiàn)問題時,要盡可能讓 Discord 自動修復(fù)。
  • 低維護:一旦配置完畢,它就能正常工作。我們只需在數(shù)據(jù)增長時添加更多節(jié)點即可。
  • 經(jīng)證明可行:我們喜歡嘗試新技術(shù),但不能太新。
  • 可預(yù)測性:當(dāng) API 的響應(yīng)時間95%超過 80ms 時無需警報,我們也不想在 Redis 或 Memcached 中緩存消息。
  • 不使用 blob 存儲:如果不斷對 blob 存儲數(shù)據(jù)進(jìn)行反序列化并新增數(shù)據(jù),那么每秒寫入數(shù)千條消息的速度將大打折扣。
  • 開源:將命運掌握在自己手里,不依賴第三方公司。

Cassandra 是唯一能滿足所有要求的數(shù)據(jù)庫。添加節(jié)點即可擴展,同時添加過程中可以容忍節(jié)點丟失,不會對應(yīng)用程序產(chǎn)生任何影響。Netflix 和蘋果等大公司已部署使用了數(shù)千個 Cassandra 節(jié)點。相關(guān)數(shù)據(jù)連續(xù)存儲在磁盤上,這樣減少了數(shù)據(jù)訪問尋址次數(shù),并且數(shù)據(jù)便于在集群中分布。Cassandra 由 DataStax 支持,但仍然是開源的,由社區(qū)驅(qū)動。

既然做出了選擇,我們就需要證明它確實可行。

3.數(shù)據(jù)建模

如何向新手介紹 Cassandra?最好的方式就是將其描述為一個 KKV 存儲器,它的主鍵由兩個 K 組成。

第一個 K 是分區(qū)鍵,用于確定數(shù)據(jù)所在的節(jié)點以及在磁盤上的位置。分區(qū)中包含多行記錄,每行記錄由第二個 K(即聚類鍵)確定。聚類鍵既是分區(qū)內(nèi)的主鍵,也是行的排序方式,可以將分區(qū)看作有序字典。這些屬性結(jié)合在一起,即可實現(xiàn)非常強大的數(shù)據(jù)建模。

前文提到, MongoDB 使用 channel_id 和 created_at 索引信息, 因為所有查詢都在頻道(channel)中進(jìn)行,所以channel_id 被設(shè)為分區(qū)鍵,但 created_at 并不是一個很好的聚類鍵,因為不同消息的創(chuàng)建時間可能相同。

好在Discord 的所有 ID都是雪花算法(可按時間排序),因此我們可以用它們來代替created_at。由此主鍵變成了(channel_id,message_id),其中 message_id 是雪花算法。這意味著加載頻道時,可以告知 Cassandra 掃描消息的準(zhǔn)確范圍。

下面是消息表的簡化模式(省略了約 10 列)。

CREATE TABLE messages (
  channel_id bigint,
  message_id bigint,
  author_id bigint,
  content text,
  PRIMARY KEY (channel_id, message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

Cassandra 的 schema 與關(guān)系數(shù)據(jù)庫的模式有很大差別,Cassandra 的 schema 更改成本相較更低,而且不會對性能造成任何臨時性影響,因此,我們獲得了 blob 存儲和關(guān)系型存儲的最佳效果。

當(dāng)我們開始將現(xiàn)有信息導(dǎo)入 Cassandra 時,日志立刻出現(xiàn)告警,提醒分區(qū)的大小超過了 100MB。這是怎么回事?Cassandra 宣稱可以單個分區(qū)可支持 2GB!顯然,理論性能并不意味著實際應(yīng)用效果。

在壓縮、集群擴展等操作過程中,大分區(qū)會給 Cassandra 帶來很大的 GC 壓力。同時,大分區(qū)還意味著其中的數(shù)據(jù)無法分布在集群中。由于 Discord 頻道(channel)將存在數(shù)年,且持續(xù)增長擴大,所以必須限制分區(qū)的大小。

我們決定按時間分類信息,參考Discord 上最大頻道,確定如果在一個桶中存儲約 10 天的消息,就可以輕松地將容量控制在 100MB 以下。桶必須從消息 ID 或時間戳中歸并。

DISCORD_EPOCH = 1420070400000
BUCKET_SIZE = 1000 * 60 * 60 * 24 * 10




def make_bucket(snowflake):
   if snowflake is None:
       timestamp = int(time.time() * 1000) - DISCORD_EPOCH
   else:
       # When a Snowflake is created it contains the number of
       # seconds since the DISCORD_EPOCH.
       timestamp = snowflake_id >> 22
   return int(timestamp / BUCKET_SIZE)
  
  
def make_buckets(start_id, end_id=None):
   return range(make_bucket(start_id), make_bucket(end_id) + 1)

Cassandra 分區(qū)鍵可復(fù)合,因此新主鍵變成了((channel_id, bucket), message_id)。

CREATE TABLE messages (
   channel_id bigint,
   bucket int,
   message_id bigint,
   author_id bigint,
   content text,
   PRIMARY KEY ((channel_id, bucket), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

查詢通道中最近的消息,需要生成一個從當(dāng)前時間到 channel_id 的桶范圍(它也是雪花算法,必須比第一條消息更早)。然后,按順序查詢分區(qū),直到收集到足夠多的信息。

這種方法的缺點是,不活躍的 Discord 頻道需要查詢多個分區(qū)才能收集到足夠多的信息。不過該方法在實踐中證明可行,因為對于活躍的 Discord 頻道來說,通常在第一個分區(qū)中就能找到足夠的消息,且這種情況占多數(shù)。

將消息導(dǎo)入 Cassandra 的過程非常順利,我們已做好了遷移到生產(chǎn)環(huán)境的準(zhǔn)備。

4.驚險的啟動

將一個新系統(tǒng)引入生產(chǎn)環(huán)境總是令人恐懼的,因此最好在不影響用戶的情況下進(jìn)行測試。我們將代碼設(shè)置為 MongoDB 和 Cassandra 的雙重讀/寫。

啟動后,我們的錯誤(bug)跟蹤器立即收到錯誤信息,提示稱 author_id 為空(null)。怎么回事?這是一個必填字段!

讓我們先一起回顧下問題產(chǎn)生的背景。

5.最終一致性

Cassandra 是 AP 數(shù)據(jù)庫,這意味著它犧牲了強一致性以換取可用性,而這正是我們想要的。在 Cassandra 中,“先讀后寫”是一種反模式(讀取比寫入成本更高),因此,即使只訪問某些列,在 Cassandra 上本質(zhì)也會成為一個更新插入操作。你也可以向任何節(jié)點寫入數(shù)據(jù),它將根據(jù)每一列的情況,使用“l(fā)ast write wins”的策略自動解決沖突。這對我們有什么影響?

編輯/刪除 race condition 的例子編輯/刪除 race condition 的例子

以上動圖例子中,在一個用戶編輯消息的同時,另一個用戶刪除了同一條消息。由于 Cassandra 寫入時執(zhí)行更新插入操作,因此我們最終發(fā)現(xiàn)記錄中只有主鍵和文本外,缺少其余數(shù)據(jù)。

有兩種可能解決方案處理這個問題:

  • 編輯信息時,寫回整條信息。這有可能找回已刪除的信息,但也增加并發(fā)寫入其他列時發(fā)生沖突的機會。
  • 找出已損壞的信息并從數(shù)據(jù)庫中刪除。

我們采用了第二種方法,即選擇一個必填列(本例中為 author_id),如果該列為空,則刪除該消息。

解決這個問題時,我們注意到我們的寫入效率非常低。由于 Cassandra 被設(shè)計為最終一致性,因此它執(zhí)行刪除操作時不會立即刪除數(shù)據(jù),必須將刪除復(fù)制到其他節(jié)點。即使其他節(jié)點暫時不可用,也要執(zhí)行刪除操作。

Cassandra 將刪除作為一種名為“墓碑”的寫入形式來處理。在讀取時,它會跳過遇到的墓碑。墓碑的存活時間可配置(默認(rèn)為 10 天),過期后會在壓縮過程中被永久刪除。

刪除列和將空值(null)寫入列完全是一回事。它們都會生成墓碑。由于 Cassandra 中的所有寫入都是更新插入,這意味著即使第一次寫入空值也會生成墓碑。實際上,我們的整個消息模式包含 16 個列,但平均每條消息長度僅有 4 個值。這導(dǎo)致插入新一行數(shù)據(jù)時,大部分時間都在無緣無故地向 Cassandra 寫入 12 個墓碑。

解決這個問題的辦法很簡單:只向 Cassandra 寫入非空值。

6.性能

眾所周知,Cassandra 的寫入速度比讀取速度快,我們的觀察結(jié)果也是如此:寫入速度低于1毫秒,讀取速度低于 5 毫秒。無論訪問什么數(shù)據(jù),觀察結(jié)果都一致,并且性能在一周的測試中始終如一。意料之中,我們得到了所期望的數(shù)據(jù)庫。

通過 Datadog 查看讀寫延遲通過 Datadog 查看讀寫延遲

快速、一致的讀取性能可以通過以下例子表現(xiàn):在數(shù)百萬條信息的頻道中跳轉(zhuǎn)到一年前的某條消息。

跳轉(zhuǎn)到一年前的聊天記錄跳轉(zhuǎn)到一年前的聊天記錄

7.巨大的意外

一切都很順利,我們將 Cassandra 切換為主數(shù)據(jù)庫,并在一周內(nèi)淘汰掉 MongoDB 。Cassandra 完美地運行了約 6 個月,直到有一天變得反應(yīng)遲鈍。

我們注意到 Cassandra 持續(xù)出現(xiàn) 10 秒鐘的 GC 全停頓("stop-the-world "GC),但原因未知。我們開始定位問題,發(fā)現(xiàn)加載 Discord 頻道需要 20 秒。一個名為“Puzzles & Dragons Subreddit”的公共 Discord 服務(wù)器是罪魁禍?zhǔn)?。由于它是一個開放的服務(wù)器,我們加入進(jìn)去一探原因。

令人驚訝的是,頻道里只有一條信息。同時我們發(fā)現(xiàn),他們使用我們的 API 刪除了數(shù)百萬條信息,只在頻道中留下了 1 條信息。

上文(談及最終一致性時)提到過, Cassandra 是如何使用墓碑處理刪除操作。當(dāng)用戶加載該頻道時,即使只有一條消息,Cassandra 也必須掃描數(shù)以百萬計的消息墓碑(產(chǎn)生垃圾的速度比 JVM 收集垃圾的速度更快)。

我們通過以下方法解決了這個問題:

  • 因為我們每晚都會在消息集群上運行 Cassandra 修復(fù)(反熵進(jìn)程),所以將墓碑的生命周期從 10 天縮短到 2 天。
  • 修改查詢代碼,以跟蹤空桶,并在未來避免在頻道中出現(xiàn)空桶。這意味著,如果用戶再次進(jìn)行此查詢,即使是最壞的情況下,Cassandra 也只能掃描最近的數(shù)據(jù)桶。

8.未來發(fā)展規(guī)劃

目前正在運行一個復(fù)制系數(shù)為 3 的 12 節(jié)點集群,并將根據(jù)意外所需繼續(xù)添加新的 Cassandra 節(jié)點,我們相信在很長一段時間內(nèi)這個集群可以持續(xù)高效運行。

但隨著 Discord 的不斷發(fā)展,在遙遠(yuǎn)的未來,有可能每天需要存儲數(shù)十億條消息。Netflix 和蘋果公司運行著數(shù)百個節(jié)點的集群,因此我們知道這個階段不用過多顧慮。不過,我們還是準(zhǔn)備了一些未雨綢繆的計劃。

1)近期計劃

  • 將我們的消息集群從 Cassandra 2 升級到 Cassandra 3。Cassandra 3 有一種新的存儲格式,可將存儲大小減少 50% 以上。
  • 新版本的 Cassandra 更善于在單個節(jié)點上處理更多數(shù)據(jù)。我們目前在每個節(jié)點上存儲近 1TB 的壓縮數(shù)據(jù)。我們相信,只要將其提升到 2TB,就能安全地減少集群中的節(jié)點數(shù)量。

2)長期計劃

  • 探索使用 C++ 編寫的 Cassandra 兼容數(shù)據(jù)庫 Scylla 。在正常運行期間,我們的 Cassandra 節(jié)點占用的 CPU 并不多,但在非高峰時段,當(dāng)我們運行修復(fù)(一個反熵進(jìn)程)時,CPU 占用就非常高。同時,在上一次修復(fù)后,修復(fù)持續(xù)時間和寫入的數(shù)據(jù)量也會增加。Scylla 宣稱能大大縮短修復(fù)時間。
  • 建立一個系統(tǒng),將未使用的頻道歸檔到谷歌云存儲的文件中,并按需加載回來。其實我們不希望這樣做,所以這一計劃未必會落實。

9.結(jié)論

盡管經(jīng)歷“巨大的意外”,但我們的切換過程一直很順利。每日信息總量從 1 億多條增加到 1.2 億多條,也一直保持著良好的性能與穩(wěn)定性。

由于這個項目的成功,我們已經(jīng)將其余的實時生產(chǎn)數(shù)據(jù)轉(zhuǎn)移到 Cassandra,并且也取得了成功。

2023:從Cassandra到 ScyllaDB

2023年,Discord 使用的 Cassandra 集群出現(xiàn)嚴(yán)重的性能問題,技術(shù)人員耗費越來越多的精力,致力于維護數(shù)據(jù)庫,而非改進(jìn)性能。

時隔六年,Discord 消息存儲再面臨性能挑戰(zhàn),存儲遷移刻不容緩。

1.Cassandra的存儲痛點

Discord 將信息存儲在名為 cassandra-messages 的數(shù)據(jù)庫中。顧名思義,它運行 Cassandra 以存儲信息。2017年,Discord 運行12個 Cassandra 節(jié)點,存儲數(shù)十億條信息。

截至2022年初,以上系統(tǒng)擁有177個節(jié)點和數(shù)萬億條消息,Cassandra 出現(xiàn)了嚴(yán)重的性能問題。由于不可預(yù)測的數(shù)據(jù)庫延遲等問題,技術(shù)團隊必須隨時保持聯(lián)系,減少運維操作,避免增加系統(tǒng)運行成本。

痛點從何產(chǎn)生?讓我們來看以下這條消息。

CREATE TABLE messages (
   channel_id bigint,
   bucket int,
   message_id bigint,
   author_id bigint,
   content text,
   PRIMARY KEY ((channel_id, bucket), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

以上 CQL 語句是消息模式的最小版本。Discord 使用的每個 ID 都通過Snowflake 生成,因此可按時間排序。根據(jù)消息發(fā)送的通道以及一個存儲桶(一個靜態(tài)時間窗口)來劃分消息。這種分區(qū)意味著,在 Cassandra 中,給定通道和存儲桶的所有消息都將存儲在一起,并跨三個節(jié)點(或設(shè)置的任何復(fù)制系數(shù))進(jìn)行復(fù)制。

在 Cassandra 中,讀取比寫入的成本高。寫入操作被附加到提交日志中,并寫入名為內(nèi)存表(memtable)的內(nèi)存結(jié)構(gòu)中,最終被刷新到磁盤。然而,讀取需要查詢內(nèi)存表(memtable)和可能的多個磁盤文件(SSTable),操作成本相當(dāng)高。

用戶與服務(wù)器交互時,大量并發(fā)讀取使分區(qū)出現(xiàn)熱點,一般將其稱為“熱分區(qū)”。一旦數(shù)據(jù)集的規(guī)模與這些訪問模式結(jié)合,導(dǎo)致 Cassandra 集群出現(xiàn)問題。

熱分區(qū)經(jīng)常導(dǎo)致整個數(shù)據(jù)庫集群延遲。通道與存儲桶組合接收大量流量,隨著節(jié)點服務(wù)流量越發(fā)吃力,節(jié)點延遲就越發(fā)嚴(yán)重。

由于節(jié)點速度無法跟上,對該節(jié)點的其他查詢受到影響。由于我們使用仲裁一致性級別執(zhí)行讀寫操作,因此服務(wù)于熱分區(qū)的節(jié)點的所有查詢都會影響,延遲增加,從而對最終用戶產(chǎn)生更廣泛的影響。

集群維護任務(wù)也經(jīng)常引起麻煩。我們很容易在壓縮上落后,Cassandra 會壓縮磁盤上的SSTable以獲得更高的性能讀取。這樣一來,我們的讀取成本不僅會更高,并且由于節(jié)點試圖壓縮,還會產(chǎn)生級聯(lián)延遲。

我們經(jīng)常執(zhí)行一種被稱為“八卦舞蹈”的操作,即停止一個節(jié)點的輪換,讓它在不占用流量的情況下進(jìn)行壓縮,將其重新加入輪換,從 Cassandra 獲取切換提示,然后重復(fù)這個操作,直至壓縮積壓的信息被清空。由于 GC 暫停會導(dǎo)致顯著的延遲峰值,我們還花了大量時間調(diào)優(yōu) JVM 的垃圾收集器和堆設(shè)置。

2.架構(gòu)遷移

消息集群并非唯一的 Cassandra 數(shù)據(jù)庫,我們還具備其他幾個集群,每個集群都表現(xiàn)出類似的缺點(雖然可能沒有那么嚴(yán)重)。

ScyllaDB 引起了我們的興趣,這是一個用 C++ 編寫的與 Cassandra 兼容的數(shù)據(jù)庫。它承諾提供更好的性能、更快的修復(fù)、通過核分片架構(gòu)實現(xiàn)更強的工作負(fù)載隔離,以及無垃圾回收,聽起來相當(dāng)吸引人。

當(dāng)然ScyllaDB也存在不足之處,由于采用 C++ 編譯而不是 Java,所以它沒有垃圾收集器。過去,我們的團隊在 Cassandra 上的垃圾收集器上遇到許多問題,包括影響延遲的 GC 暫停、一直到超長的連續(xù) GC 暫停,以至于操作員必須手動重新啟動,問題節(jié)點才能恢復(fù)健康狀態(tài)。這些問題導(dǎo)致技術(shù)團隊必須隨叫隨到,同時,這些問題也是影響消息集群穩(wěn)定性問題的根源。

在對 ScyllaDB 進(jìn)行試驗,并觀察到測試中的改進(jìn)成效后,我們決定遷移所有數(shù)據(jù)庫。雖然這項決定本身可以用一篇博客來介紹,但簡而言之,截至2020年,我們已經(jīng)將所有數(shù)據(jù)庫遷移到 ScyllaDB,除了一個數(shù)據(jù)庫—— cassandra-messages 。

為什么我們還未開始遷移?首先,集群規(guī)模巨大,包括數(shù)以萬億計的消息和近200個節(jié)點,任何遷移操作都非常復(fù)雜。此外,我們希望調(diào)整新數(shù)據(jù)庫時,其性能達(dá)到最佳狀態(tài)。我們還想在生產(chǎn)環(huán)境中積累使用 ScyllaDB 的更多經(jīng)驗,了解其缺陷。

針對我們的用例,還改進(jìn)了 ScyllaDB 的性能。我們在測試中發(fā)現(xiàn),反向查詢的性能不足以滿足需求。以與表排序相反的順序進(jìn)行數(shù)據(jù)庫掃描時,例如按升序掃描消息時,則執(zhí)行反向查詢。ScyllaDB 團隊優(yōu)先對其進(jìn)行改進(jìn),實現(xiàn)了高性能的反向查詢,清除了我們遷移計劃中的“最后一個數(shù)據(jù)庫”的障礙。

我們擔(dān)心,在系統(tǒng)上添加新數(shù)據(jù)庫不太可能有翻天覆地的性能改進(jìn),“熱分區(qū)”的問題依然存在于 ScyllaDB ,因此,我們寄希望于投資改進(jìn)數(shù)據(jù)庫上游系統(tǒng),以助于數(shù)據(jù)庫屏蔽和提升數(shù)據(jù)庫性能。

3.使用數(shù)據(jù)服務(wù)提供數(shù)據(jù)

針對 Cassandra ,我們遇到了熱分區(qū)的難題,分給特定分區(qū)的高流量會引起無限并發(fā),進(jìn)而導(dǎo)致級聯(lián)延遲,延長后續(xù)查詢時間。如果能夠控制熱分區(qū)的并發(fā)流量,就可保護數(shù)據(jù)庫免于重負(fù)。

為了完成這項任務(wù),我們編寫了所謂的數(shù)據(jù)服務(wù)——位于 API 單體和數(shù)據(jù)庫集群之間的中介服務(wù)。編寫數(shù)據(jù)服務(wù)時,我們選擇了一種在 Discord 應(yīng)用越發(fā)廣泛的語言:Rust!它能在保障安全性的前提下,提供可與 C/ C++ 媲美的高速度。

Rust 的主要優(yōu)勢之一是無懼并發(fā)——該語言使編寫安全并發(fā)代碼變得容易。它的庫也非常適合完成我們的其他工作,Tokio生態(tài)系統(tǒng)是構(gòu)建異步I/O系統(tǒng)的監(jiān)視基礎(chǔ),并且該語言也對 Cassandra 和 ScyllaDB 提供驅(qū)動程序支持。

此外,Rust 編譯器提供的幫助、清晰的錯誤消息、語言結(jié)構(gòu)以及對安全性的重視,編寫代碼變得很有趣。Rust 程序一旦通過編譯,就可以運行,這讓我們很滿意。最重要的是,我們用Rust 進(jìn)行重寫(meme/模因信譽非常重要)。

我們的數(shù)據(jù)服務(wù)位于 API 和 ScyllaDB 集群之間。每個數(shù)據(jù)庫查詢大約包含一個gRPC 端點,并且故意不包含業(yè)務(wù)邏輯。數(shù)據(jù)服務(wù)一大特點是請求合并,如果多個用戶同時請求同一行,我們將只查詢一次數(shù)據(jù)庫。首個發(fā)出請求的用戶會觸發(fā)數(shù)據(jù)服務(wù)中的工作任務(wù),后續(xù)請求將檢查該任務(wù)是否存在并訂閱它,該工作任務(wù)將查詢數(shù)據(jù)庫并將該行返回給所有訂閱者。

這就是 Rust 的強大之處:它使編寫安全的并發(fā)代碼變得輕松。

讓我們想象一下,大型服務(wù)器有一條@所有人的重要公告:用戶將打開應(yīng)用程序并閱讀消息,向數(shù)據(jù)庫發(fā)送大量流量。以往,這可能會導(dǎo)致熱分區(qū),并且可能需要工程師隨時保持待機,以幫助系統(tǒng)恢復(fù)。數(shù)據(jù)服務(wù)能夠顯著減少數(shù)據(jù)庫的流量峰值。

第二個神奇之處在于數(shù)據(jù)服務(wù)的上游。為實現(xiàn)更有效的合并,我們實現(xiàn)了一致的基于哈希的數(shù)據(jù)服務(wù)路由,為每個數(shù)據(jù)服務(wù)請求提供路由鍵。對消息來說,這是一個通道ID,因此對同一通道的所有請求都將轉(zhuǎn)到服務(wù)的同一實例,這種路由方式有助于進(jìn)一步減少數(shù)據(jù)庫的負(fù)載。

這些改進(jìn)頗有助益,但并未解決所有問題。,但它們并不能解決我們所有的問題。Cassandra集群上仍然存在熱分區(qū)和延遲增加,只是不那么頻繁了,這為我們贏得了一些時間,以便準(zhǔn)備最優(yōu)ScyllaDB集群并執(zhí)行遷移。

4.大規(guī)模數(shù)據(jù)遷移

我們的遷移需求非常簡單:在不停機的情況下遷移數(shù)萬億條消息,并快速完成。正如上文所述,雖然 Cassandra 的情況有所改善,但還常常出現(xiàn)問題。

第一步很簡單:使用超級磁盤存儲拓?fù)渑渲靡粋€新的 ScyllaDB 集群。

使用本地 SSD 提高速度,并利用 RAID 將數(shù)據(jù)鏡像到持久磁盤,由此我們同時獲得了連接本地磁盤的速度和持久磁盤的持久性。集群建立后,就可以開始向其遷移數(shù)據(jù)了。

我們的初版遷移技術(shù)旨在快速獲取價值。我們將開始使用全新的 ScyllaDB 集群來處理新數(shù)據(jù),在切換時間內(nèi)遷移歷史數(shù)據(jù)。這項操作增加了復(fù)雜性,但每個大型項目都無法避免額外的復(fù)雜性,對吧?

然后,向 Cassandra 和 ScyllaDB 雙重寫入新數(shù)據(jù),并同時開始準(zhǔn)備 ScyllaDB 的 Spark 遷移器。這需要大量調(diào)整,一旦設(shè)置完成,我們預(yù)計三個月能夠完成遷移。

這個時間期限讓我們并不滿意,因為我們希望更快獲取價值。所以,我們組織了一場團隊會議,集思廣益,思考如何增速——我們已經(jīng)編寫了一個快速和高性能的數(shù)據(jù)庫,可以對其進(jìn)行拓展。因而我們選擇參與了一些模因驅(qū)動工程,用Rust重寫數(shù)據(jù)遷移器。

某一天的下午,我們擴展了數(shù)據(jù)服務(wù)庫以便執(zhí)行大規(guī)模數(shù)據(jù)遷移。它從數(shù)據(jù)庫讀取令牌范圍,通過 SQLite 在本地檢查,然后將它們送入 ScyllaDB 。連接改進(jìn)后的新遷移器后,我們重新預(yù)估工期:9天!如果可以這么迅速地遷移數(shù)據(jù),就可以放棄基于時間的復(fù)雜方式,一次性切換所有數(shù)據(jù)。

啟動遷移器并讓其保持運行,以每秒320萬的速度遷移信息。幾天后,遷移進(jìn)度達(dá)到100%,但我們意識到它停留在99.9999%的完成度。遷移器在讀取數(shù)據(jù)的最后幾個令牌范圍時超時了,因為它們包含了 Cassandra 中未壓縮過的巨大墓碑范圍。我們將這個令牌范圍壓實,幾秒鐘后,遷移完成!

執(zhí)行自動數(shù)據(jù)驗證,即通過向兩個數(shù)據(jù)庫發(fā)送一小部分讀取數(shù)據(jù)并比較結(jié)果,一切看起來都很好。在全生產(chǎn)流量的情況下,集群表現(xiàn)良好,而 Cassandra 卻遭受越來越頻繁的延遲問題。我們團隊聚集在現(xiàn)場,打開開關(guān)使 ScyllaDB 成為主數(shù)據(jù)庫。

5.數(shù)據(jù)庫遷移效果

Discord 在2022年5月切換了消息數(shù)據(jù)庫,遷移效果如何?

運行的177個 Cassandra 節(jié)點減少到72個 ScyllaDB 節(jié)點,每個 ScyllaDB 節(jié)點有9tb的磁盤空間,高于每個 Cassandra 節(jié)點平均 4tb 的磁盤空間。

我們的尾部延遲也大大改善了。例如,在 Cassandra 上獲取歷史消息的p99在40-125ms之間,ScyllaDB 的p99延遲在15ms之間,消息插入性能從 Cassandra 上的5-70ms p99上升到 ScyllaDB 上穩(wěn)定的5ms p99。

2022年底,全球觀眾都在收看世界杯。技術(shù)人員發(fā)現(xiàn),Discord 的監(jiān)控圖表可以展示決賽的進(jìn)球情況。這為技術(shù)團隊提供了一個在會議期間觀看足球比賽的借口——不是“在會議期間看足球比賽”,而是“主動監(jiān)控系統(tǒng)性能”。

以上的信息發(fā)送數(shù)量圖,描繪了世界杯決賽的精彩發(fā)展,圖中的每個峰值分別代表比賽中的重要節(jié)點。

1. 梅西罰進(jìn)點球,阿根廷1比0領(lǐng)先。

2. 阿根廷再次得分,以2比0領(lǐng)先。

3. 中場休息。當(dāng)用戶談?wù)摫荣悤r,會持續(xù)15分鐘。

4. 姆巴佩為法國隊進(jìn)球,90秒后再次進(jìn)球?qū)⒈确职馄?

5. 規(guī)則結(jié)束了,這場重要的比賽將進(jìn)入加時賽。

6. 在加時賽的前半段沒有太多的事情發(fā)生,但我們到了中場休息,用戶開始聊天。

7. 梅西再次得分,阿根廷隊領(lǐng)先!

8. 姆巴佩反擊將比分扳平!

9. 加時賽結(jié)束了,我們要進(jìn)點球了!

  1. 在整個點球大戰(zhàn)中,興奮和壓力不斷增加,直到法國隊失誤,而阿根廷隊沒有!阿根廷贏了!

每秒合并消息數(shù)每秒合并消息數(shù)

世界各地的人們都在觀看這場激動人心的比賽,與此同時,Discord 使用基于 rust 的數(shù)據(jù)服務(wù)和 ScyllaDB ,不費吹灰之力便承擔(dān)了比賽產(chǎn)生的巨大流量,同時為用戶提供交流平臺。

參考資料

  • HOW DISCORD STORES BILLIONS OF MESSAGEShttps://discord.com/blog/how-discord-stores-billions-of-messages
  • HOW DISCORD STORES TRILLIONS OF MESSAGEShttps://discord.com/blog/how-discord-stores-trillions-of-messages

作者丨Stanislav Vishnevskiy & Bo Ingram

編譯丨onehunnit

責(zé)任編輯:武曉燕 來源: dbaplus社群
相關(guān)推薦

2009-03-23 09:05:01

2020-12-01 15:12:49

字節(jié)跳動數(shù)據(jù)庫數(shù)據(jù)

2011-03-11 08:53:06

DB2Oracle

2019-03-05 10:16:54

數(shù)據(jù)分區(qū)表SQLserver

2020-06-08 10:41:13

云計算數(shù)據(jù)工具

2020-03-05 10:36:12

云計算數(shù)據(jù)庫云平臺

2010-09-25 15:42:26

數(shù)據(jù)庫集群Facebook

2020-10-23 09:38:41

數(shù)據(jù)分庫分表數(shù)據(jù)遷移

2025-01-14 16:12:54

2011-05-11 10:26:36

MySQL數(shù)據(jù)庫無縫遷移

2020-02-28 18:27:37

運維管理微盟

2011-04-12 10:59:46

Oracle數(shù)據(jù)庫

2020-08-20 07:53:14

安全數(shù)據(jù)泄露數(shù)據(jù)

2011-09-23 09:09:38

數(shù)據(jù)庫遷移

2020-08-13 07:42:15

數(shù)據(jù)庫Flyway代碼

2010-08-25 14:32:49

DB2數(shù)據(jù)庫遷移

2017-11-22 09:20:41

數(shù)據(jù)庫在線數(shù)據(jù)遷移Subscriptio

2009-03-19 09:44:07

SQL Server數(shù)據(jù)庫遷移數(shù)據(jù)庫

2019-08-13 15:52:34

數(shù)據(jù)庫同步遷移

2011-04-29 14:30:23

點贊
收藏

51CTO技術(shù)棧公眾號