從Discord的做法中學習 — 使用Golang進行請求合并

正如你可能之前看到的,Discord去年發布了一篇有價值的文章,討論了他們成功存儲了數萬億條消息。雖然有很多關于這篇文章的YouTube視頻和文章,但我認為這篇文章中一個名為“數據服務為數據服務”的部分沒有得到足夠的關注。在這篇文章中,我們將討論Discord對數據服務的方法,并探討如何利用Golang的并發特性來減少特定情況下的數據庫負載。

數據服務拯救熱分區

如你所知,消息和頻道是Discord中最常用的組件。讓我們想象一個場景:一個擁有50萬成員的頻道的管理員提到@everyone。會發生什么?成千上萬個同時的請求直接指向那個數據庫分區,所有請求的目標都是檢索相同的消息。這種模式重復發生,直到該分區無法回應其他請求。

img

Discord引入了一個位于Python API和數據庫集群之間的中間服務 — 他們稱之為數據服務。這個服務大致包含每個查詢一個gRPC端點,沒有任何業務邏輯。對Discord來說,這個服務的重要特性就是請求合并。

請求合并

正如我們之前討論過的,每當在一個龐大的頻道中有提及時,就會有大量類似的請求直接指向數據庫分區。通過合并這些請求,如果多個用戶請求相同的數據庫行,我們可以將這些請求合并成一個選擇查詢,并執行該查詢。

img

通過使用數據服務而不是直接連接到數據庫,我們可以實現許多令人興奮的功能,比如批量查詢,這些功能可以顯著減少數據庫開銷,并改善查詢的平均值,特別是第99百分位數。

使用Golang實現簡單的請求合并

與許多其他公司一樣,Discord使用Python作為其主要的后端語言。無論是微服務還是單體架構,后端服務通常直接連接到數據源進行查詢。雖然Python確實是一種多功能語言,但在并發性方面存在一些不足。使用Python實現并發和高吞吐量的服務可能有些挑戰,而性能與用C++、Rust和Golang等編譯語言編寫的類似服務相比,往往會較低。

在進行任何操作之前,讓我們模擬一下提到的情況。假設服務總共收到了5,000個請求,其中并發數為1,000。

  • 總請求數: 5,000
  • 并發數: 1,000
  • 需要檢索的唯一消息數: 100
type Message struct {gorm.ModelText stringUser string // some random properties that a message row may have
}func generateRandomData(db *gorm.DB) {for i := 0; i < 100; i++ {msg := &messages.Message{Text: fmt.Sprintf("Message #%d", i)}db.Save(msg)}
}

我使用Gorm構建了一個簡單的數據庫模型來表示**Message(消息)**表,然后向表中填充了100條虛擬消息。

e := echo.New()
e.GET("/randomMessage", func(c echo.Context) error {randomMessageID := rand.Intn(100)var msg messages.Messageif err := db.Where("id=?", randomMessageID).First(&msg).Error; err != nil {return err}return c.JSON(200, msg)
})
e.Logger.Fatal(e.Start(":1323"))

我創建了一個簡單的端點來模擬對0到100之間的隨機ID進行SELECT查詢。現在我們可以對這個端點進行基準測試,模擬在這種情況下會發生什么。

img

img

  • 平均每秒請求數 (RPS): 300
  • 平均響應時間: 3.2秒
  • 50% 響應時間: 546毫秒
  • 99% 響應時間: 14.7秒

如果我們有10秒的超時策略,大約有2%的請求將收不到響應。現在讓我們改變代碼。Golang有一個名為“single flight”的內置包。這個包提供了重復函數調用抑制機制。一般來說,你給它一個鍵和一個函數,而不是多次運行該函數,SingleFlight會暫時保持其他調用,直到第一次調用完成其請求并以相同的結果作出響應。

var g = singleflight.Group{}
e.GET("/randomMessage", func(c echo.Context) error {randomMessageID := rand.Intn(100)msg, err, _ := g.Do(fmt.Sprint(randomMessageID), func() (interface{}, error) {var msg messages.Messageif err := db.Where("id=?", randomMessageID).First(&msg).Error; err != nil {return nil, err}return &msg, nil})if err != nil {return err}return c.JSON(200, msg)
})

func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool)

Do 執行并返回給定函數的結果,確保同一時間針對給定鍵只有一個執行過程。如果出現重復,重復的調用者會等待原始調用完成并接收相同的結果。返回值 shared 表示是否將 v 給了多個調用者。

現在讓我們重新運行模擬并比較結果。

img

img

  • 平均每秒請求數 (RPS): 2309
  • 平均響應時間: 433毫秒
  • 50% 響應時間: 389毫秒
  • 99% 響應時間: 777毫秒

正如你所看到的,僅使用了一個簡單的技術就將第99百分位數減少了14秒,新方法支持的每秒請求次數提高了7.6倍。

結論

從那時起我們就注意到,通過優化數據庫查詢,可以大大提高應用程序的整體性能。雖然我們討論的方法是情景性的,但Discord已經使用了一年多,對他們有很大幫助。

你應該知道,如果你使用數據服務,你將面臨其他的復雜情況。例如,你可能會有多個數據服務實例,而你的Python API必須有一種機制將類似的請求發送到同一個實例。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/164581.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/164581.shtml
英文地址,請注明出處:http://en.pswp.cn/news/164581.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

QT項目移植到VS+QT(RTI-DDS)

QT中.pro文件中include(./xxx.pri) pri文件如下定義 unset(FILENAMES)for(FILENAME, FILENAMES) {HEADERFILE $$PWD/$${FILENAME}.hif(exists($$HEADERFILE)) {HEADERS * $$HEADERFILE}SOURCEFILE $$PWD/$${FILENAME}.cppif(exists($$SOURCEFILE)) {SOURCES * $$SOURCEFILE}…

CSS-鼠標屬性篇

屬性名&#xff1a;cursor 功能&#xff1a;設置鼠標光標的樣式 屬性值&#xff1a; pointer&#xff1a;小手move&#xff1a;移動圖標text&#xff1a;文字選擇器crosshair&#xff1a;十字架wait&#xff1a;等待help&#xff1a;幫助 eg.html{ cursor: wait;}(此處使用css改…

SpringBoot——MVC原理

優質博文&#xff1a;IT-BLOG-CN 一、SpringMVC自動配置 SpringMVC auto-configuration&#xff1a;SpringBoot自動配置好了SpringMVC。以下是SpringBoot對SpringMVC的默認配置&#xff1a;[WebMvcAutoConfiguration] 【1】包括ContentNegotiatingViewResolver和BeanNameView…

Keil工程打開發現目標芯片無法選擇解決方案

買了一個開發板&#xff0c;配套有一些底層驅動的例程&#xff0c;打開后發現目標芯片無法選擇&#xff0c;對應的下載Flash FLM文件也無法選擇。從提示框中可以知道所提供的例程是Keil4的例程&#xff0c;我電腦上安裝的Keil版本是Keil版本&#xff0c;估計是這個原因導致工程…

C# 執行Excel VBA宏工具類

寫在前面 在Excel文檔的自動化處理流程中&#xff0c;有部分值需要通過已定義的宏來求解&#xff0c;所以延伸出了用C# 調用Excel中的宏代碼的需求。 首先要從NuGet中引入Microsoft.Office.Interop.Excel 類庫 using Excel Microsoft.Office.Interop.Excel; 代碼實現 /// &l…

HashMap,1.7與1.8的區別,HashMap的擴容方式有哪些

HashMap,1.7與1.8的區別 底層數據結構的區別 JDK 1.8之前&#xff1a; 1&#xff09;JDK1.8 之前HashMap 底層是數組和鏈表結合在一起使用也就是鏈表散列。 2&#xff09;HashMap 通過key 的hashCode 經過擾動函數處理過后得到hash 值&#xff0c;然后通過(n - 1&#xff09…

修改el-radio-group樣式,自定義單選組件

修改el-radio-group樣式,自定義單選組件 自定義組件 MyRadioGroup.vue <template><div class"btnsBox"><el-radio-group v-model"activeIndex" change"handleClick"><el-radio-buttonv-for"(item, index) in list&qu…

CSS3動畫

在CSS3中新增了一個很有意思的東西&#xff0c;那就是動畫&#xff0c;有了動畫我們可以做很多的事情&#xff0c;讓我為大家介紹一下動畫吧&#xff01; 本篇文章關于介紹動畫&#xff0c;利用小球移動為你們介紹一下動畫 默認樣式&#xff1a; <!DOCTYPE html> <ht…

普通話考試相關(一文讀懂)

文章目錄&#xff1a; 一&#xff1a;相關常識 1.考試報名時間 2.報名地方 費用 證件 3.考試流程 4.普通話等級說明 二&#xff1a;題型 三&#xff1a;技巧 1.前三題 2.命題說話 四&#xff1a;普通話考試題庫 1.在線題庫 2.下載題庫 一&#xff1a;相關常識 …

JavaEE(SpringMVC)期末復習

文章目錄 JavaEE期末復習一、單選題&#xff1a; JavaEE期末復習 一、單選題&#xff1a; 1.Spring的核?技術是&#xff08; A &#xff09;&#xff1f; A依賴注入 B.JdbcTmplate C.聲明式事務 D.資源訪問 Spring的核心技術包括依賴注入&#xff08;Dependency Injection&am…

【前端】js通過canvas獲取瀏覽器的唯一指紋可以當做唯一標識

【前端】js通過canvas獲取瀏覽器的唯一指紋可以當做唯一標識 <!DOCTYPE html> <html><head> <meta charset"utf-8" /> <meta name"viewport" content"widthdevice-width" /> <title>JS Bin</title> &…

解決Emmy Lua插件在IDEA或 Reder 沒有代碼提示的問題(設置文件關聯 增加對.lua.txt文件的支持)

目錄 Reder版本2019.x Reder版本2021.1.5x Reder版本2019.x 解決Emmy Lua插件在IDEA或 Reder 沒有代碼提示的問題(設置文件關聯 增加對.lua.txt文件的支持) Reder版本2021.1.5x 解決Emmy Lua插件在IDEA或 Reder 沒有代碼提示的問題(設置文件關聯 增加對.lua.txt文件的支持)…

java游戲制作-王者榮耀游戲

一.準備工作 首先創建一個新的Java項目命名為“王者榮耀”&#xff0c;并在src下創建兩個包分別命名為“com.sxt"、”com.stx.beast",在相應的包中創建所需的類。 創建一個名為“img”的文件夾來儲存所需的圖片素材。 二.代碼呈現 package com.sxt;import javax.sw…

Netty Review - 探索ByteBuf的內部機制

文章目錄 概念ByteBuf VS Java NIO BufferByteBuf實現類HeapByteBuf vs DirectByteBufPooledByteBuf vs UnpooledByteBuf其他 ByteBuf的實現機制 概念 ByteBuf是Netty中用于處理二進制數據的緩沖區 Netty的ByteBuf是一個可用于高效存儲和操作字節數據的數據結構。與傳統的Byt…

跳躍游戲[中等]

優質博文&#xff1a;IT-BLOG-CN 一、題目 給你一個非負整數數組nums&#xff0c;你最初位于數組的第一個下標 。數組中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最后一個下標&#xff0c;如果可以&#xff0c;返回true&#xff1b;否則&#xff0c;返…

阿里入局鴻蒙!鴻蒙原生應用再添兩員新丁

今日HarmonyOS微博稱&#xff0c;阿里釘釘、螞蟻集團旗下的移動開發平臺mPaaS與華為達成合作&#xff0c;宣布啟動鴻蒙原生應用的開發&#xff01;相關應用將以原生方式適配#HarmonyOS NEXT#系統。 #HarmonyOS#市場或迎來爆發式增長&#xff01; 阿里釘釘 阿里釘釘與華為達成合…

Android 匿名內存深入分析

Android 匿名內存解析 有了binder機制為什么還需要匿名內存來實現IPC呢&#xff1f;我覺得很大的原因就是binder傳輸是有大小限制的&#xff0c;不說應用層的限制。在驅動中binder的傳輸大小被限制在了4M&#xff0c;分享一張圖片可能就超過了這個限制。匿名內存的主要解決思路…

黑馬點評-10實現用戶點贊和點贊排行榜功能

用戶點贊功能 如果用戶只要點贊一次就對數據庫中blog表中的liked字段的值加1就會導致一個用戶無限點贊 PutMapping("/like/{id}") public Result likeBlog(PathVariable("id") Long id) {// 修改點贊數量,update tb_blog set liked liked 1 where id …

編譯器核心技術概覽

編譯技術是一門龐大的學科&#xff0c;我們無法對其做完善的講解。但不同用途的編譯器或編譯技術的難度可能相差很大&#xff0c;對知識的掌握要求也會相差很多。如果你要實現諸如 C、JavaScript 這類通用用途語言&#xff08;general purpose language&#xff09;&#xff0c…

buck降壓電路

一、Buck電路的拓撲結構 Buck是直流轉直流的降壓電路,下面是拓撲結構,作為硬件工程師,這個最好是能夠記下來,了然于胸。 為啥要記下來,自然是因為這個電路太基礎了,并且誰都會用到,更重要的一點,面試可能會考。。。 上圖是個異步buck,同步buck就是將里面的二極管換成M…