<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 我們如何擴展 VividCortex 的后端系統 > 原文: [http://highscalability.com/blog/2015/3/30/how-we-scale-vividcortexs-backend-systems.html](http://highscalability.com/blog/2015/3/30/how-we-scale-vividcortexs-backend-systems.html) ![](https://img.kancloud.cn/cb/db/cbdb8f015bf1ef4fd869f01f168691a9_175x146.png) *這是 [Baron Schwartz](https://twitter.com/xaprb) , [VividCortex](https://vividcortex.com/about-us/) 的創始人& CEO 的來賓帖子,這是專門為當今的大型,多語種持久層設計的首個統一的性能管理工具套件 。* VividCortex 是用于數據庫性能管理的云托管 SaaS 平臺。 我們的客戶安裝了代理,以衡量其服務器執行的工作(查詢,流程等),并從中頻繁地生成度量和事件。 代理將結果數據發送到我們的 API,在此托管我們的分析后端。 后端系統是數據庫,內部服務(準微服務)和面向 Web 的 API 的集合。 這些 API 也為我們的 AngularJS 前端應用程序提供了動力。 我們處理大量數據。 我們高速攝取指標和事件。 我們還執行以交互方式接觸大量數據的分析。 我們不是唯一的,我不想暗示我們在事物計劃方面給人留下深刻的印象。 我們尚未以“網絡規模”運作。 盡管如此,我們的工作負載具有一些相對不尋常的特征,我們已經能夠擴展到最大程度,同時在成本和基礎架構方面仍然保持相當高的效率。 我的咨詢生涯告訴我,建立這樣的系統通常對公司來說是一個挑戰(對我們來說一直如此)。 我們的故事可能對其他人有用。 因此,我將不必要地詳細介紹工作負載的特定部分及其帶來的挑戰。 ## 我們所做的 VividCortex 是一個復雜的系統,具有很多功能,但是熱門查詢(以及我們為實現此目的而捕獲的數據)很容易成為對我們的后端產生重大影響的最重要的功能。 熱門查詢是一個查詢類別的表,按所選維度從大到小排列,并顯示一個綜合行,該行顯示的類別不符合前 N 個。 [![top-queries](https://img.kancloud.cn/05/7f/057f9e061e4a8291c63f53d8da85ccc7_1275x992.png)](https://camo.githubusercontent.com/2f0d8f8246190511ec9d7a5b99ed48b3d939fdc1/68747470733a2f2f636c6f75642e67697468756275736572636f6e74656e742e636f6d2f6173736574732f3237393837352f363838393234382f38353663663566612d643636302d313165342d383565312d6131393330653962636139662e706e67) 屏幕截圖(順便說一下,來自我們自己的工作量)顯示了此功能,并精簡了其基本功能。 熱門查詢實際上是我們的應用程序可以實現的一般任務類別的特定示例:您可以對查詢,用戶,流程,數據庫等的度量進行排名,排序,過濾,切片和切塊。 您可以同時在一臺或多臺主機上執行此操作,因此可以獲取全局視圖,然后向下鉆取到特定主機或主機組。 您可以從許多不同的維度中進行選擇。 所有這些本質上都采用度量類別,并按維度對其進行排名,保留前 N 個,最后將其余的折疊到所有行中。 我們還有很多其他的事情要做,但大多數事情在后端實現方面與許多指標和監視系統所做的并沒有很大不同。 與“熱門查詢”相比,這不是一項重大的工程挑戰。 ## 時間序列數據 我們的時間序列指標是屬于序列的帶有時間戳的 float64 值。 該系列由度量標準名稱和測量它們的主機標識。 時間戳當前是 UTC 中的 uint32 UNIX 時間戳。 指標名稱是點分隔的字符串。 我們從許多來源收集了大量指標,所有指標均以 1 秒為單位。 * 每個數據庫狀態指標。 例如,對于 MySQL,這包括來自 SHOW STATUS 的數百個計數器,以及來自其他來源的數百個計數器。 * 每個 CPU,每個網絡設備以及每個塊/磁盤設備,內存等的操作系統指標。 * 操作系統中的進程級別指標,包括每個進程的 CPU,內存,IO,打開的文件句柄,網絡套接字等。 但最重要的是,我們嗅探網絡流量并解碼流行數據庫產品的有線協議。 目前,我們提供針對 MySQL 和 PostgreSQL 的 GA 支持,Redis 和 MongoDB 處于 beta 測試階段,并且還有更多。 我們測量入站查詢(或視情況而定的請求/命令/等;因產品而異)和響應延遲時間,并提取各種有用的細節(例如錯誤等)。 在無法監聽網絡的情況下,可以使用內置的工具,例如 MySQL 的`PERFORMANCE_SCHEMA`和 PostgreSQL 的`pg_stat_statements`視圖。 這使我們能夠支持諸如 Amazon RDS 之類的部署方案。 > 順便說一句,捕獲這些數據本身會帶來有趣的問題,并且我們之前已經在博客上進行了介紹(兩個示例: [netlink](https://vividcortex.com/blog/2014/09/22/using-netlink-to-optimize-socket-statistics/) 和 [perf 模式](https://vividcortex.com/blog/2014/11/03/mysql-query-performance-statistics-in-the-performance-schema/))。 在這里,Go 是我們的一大財富。 最明顯的挑戰可能是我們收集的系列數量之多。 我們通過消化可變部分,然后對抽象查詢進行 md5sum,將相似查詢分類。 我們對此進行十六進制編碼(對此我感到遺憾并為自己負責,這是一個愚蠢的決定),并將結果嵌入到指標名稱中,例如 `host.queries.c.1374c6821ead6f47.tput`。 最后一個要素是維度,在這種情況下,是吞吐量(每秒的速率),但是它可能是諸如延遲,錯誤率,受影響的行數之類的許多東西之一。 此特定度量標準名稱中的可變部分是`c`,它表示這是什么類型的操作(查詢,準備好的語句執行等)和`1374c6821ead6f47`,它是摘要查詢的校驗和。 您可能會猜到,度量標準名稱的可變部分可能是高基數的。 我們的一些客戶生成了數千萬種不同類型的查詢,這導致時間序列的組合爆炸式增長。 在最壞的情況下,它是度量標準名稱的每個可變部分的叉積,通常包括 2、3、4 或有時更多的可變部分。 我們還生成并存儲許多其他數據。 關于查詢之類的描述性指標還不夠。 我們通過查詢事件本身和各種其他事件(例如,為響應系統故障或配置更改而引發的事件)的單個樣本來補充這一點。 事實證明,對這些數據進行縮放并不像時間序列度量標準那么困難,這主要是因為這些數據的讀取工作負載并不難于擴展。 ## 寫工作量 按原始事實存儲量,后端的寫入工作量中最大的部分是時間序列指標。 查詢樣本和事件在磁盤上構成了很多數據,但這是因為它比時間序列數據緊湊得多。 許多小數據比中等數量的大數據更難處理。 數據庫內部和存儲引擎的學生將了解,讀取優化存儲和寫入優化存儲在某些方面存在沖突。 如果要讀取優化的數據,則可能需要在寫入時進行額外的工作。 我們絕對需要一些讀取優化,您將在后面看到。 寫入特性非常簡單:數據按時間戳順序到達,而主要的挑戰只是將其持久化以及讀取優化所需的放大。 如您所見,我們做了很多事情來回避這一點。 我們將 1 秒分辨率的數據存儲 3 天。 我們還將下采樣分辨率降低到 1 分鐘,并保持更長的時間。 兩種保留限額均可在客戶合同中協商。 在我們自己的系統中,我們保留 10 天的 1 秒分辨率。 ## 讀取工作量 我們的時間序列指標有兩個主要的讀取工作負載。 它們揭示了關于數據庫的普遍真理之一:許多不同類型的數據通常可以放入數據庫中,甚至是專用數據庫。 對于完全不同的目的而言,彼此相鄰的數據位可能很重要,并且涉及到完全不同的問題的答案。 這是要提防的。 工作負載 1(我稱為范圍讀取)是在一個時間范圍內以所需的分辨率讀取一個或多個單獨指定的指標。 一個明顯的例子是繪制一個具有單個度量標準的時間序列圖:寬 600 像素,上周的數據; 在時間戳 1426964208 和 1427569008 之間以主機 100 的 600 個間隔向我提供來自主機 Y 的度量 X。 原則上,讀取路徑并不十分復雜:找出數據所在的位置以及適合的分辨率(在這種情況下,可以使用 1 分鐘的數據),找到起點,并讀取終點直到終點 。 根據需要重新采樣并將其流回客戶端。 實際上,有很多細微差別,但它們并不能從根本上改變我剛才描述的內容。 細微差別包括多個指標(有時數百個左右),知道什么時間范圍已被完全下采樣,調整采樣參數,同時從許多來源(包括可能會延遲的副本)讀取數據范圍等等。 這些都不是什么大不了的。 只要對數據進行讀取優化,范圍讀取工作負載就沒有什么特別困難的了。 即使在很長的時間范圍內和多個指標上,它通常也不是大量數據。 我們發現很難快速響應此類讀物的大量請求。 稍后我將討論我們如何確保數據經過讀取優化。 2 號工作量比較難。 這是熱門查詢用例,我將其稱為批量讀取。 看起來是這樣的:從時間戳 A 到 B 讀取大量未指定的指標,并對它們進行匯總,排序,返回前 N 位,并包含計算通用行所需的數據。 可以通過解釋范圍讀取的方法來獲取跟蹤數據,例如在前 N 個數據中顯示每個結果的迷你圖所需的跟蹤數據。 “熱門查詢”工作負載還包括相同的注意事項,例如在可用時使用較粗的粒度,同時讀取等等。 批量讀取工作負載的難點在于,它涉及許多指標-可能達數千萬甚至更多。 讀取單個指標的速度很快-足夠快,我們可以以比客戶習慣更快的速度呈現充滿圖表的頁面。 (我們經常收到有關圖表運行速度的評論。)但是有數千萬個圖表? 即使每公制是微秒,不是,我們已經在談論數十秒。 我們通過這種批量讀取獲得的數據也非常大。 這就是當“糞便變得虛構”的時候。 還要注意,盡管這些指標受批量讀取的影響,但它們也受各個范圍讀取的影響。 它不是“或”,所以我們無法進行簡單的優化,例如將屬于類別的度量標準與總是由完全合格的度量標準名稱查找的度量標準分開存儲。 我們可以復制它們,但這會帶來不小的成本。 最后,批量讀取的一些挑戰性特征似乎幾乎是偶然的。 例如,包羅萬象的行似乎沒什么大不了的,但對于用戶界面來說,這兩者都是至關重要的,在用戶界面中,提供有關用戶正在分析的應用程序工作負載以及我們的后端的上下文信息至關重要, 它可以代表大量工作。 請注意,在屏幕截圖中,我們正在顯示 159 個其他查詢,這些查詢被分組到了 catchall 行中。 這個數字可能高達數百萬。 ## 數據分布特征 除了大容量讀取工作負載呈現的有趣讀取特性外,數據本身還具有一些不尋常的屬性,尤其是在值的分布和密度方面。 大多數監視系統和時間序列數據庫都是為密集指標(例如 CPU 或內存利用率)構建的。 密集度量標準屬于可以依靠其存在的事物,并且在度量它時,總會得到一個值。 大多數用于監視數據的用例都隱式地假設所有指標都是密集的。 這說明了 RRDTool,Graphite 的 Whisper 后端等的設計。 現代應用程序體系結構已經對這些時間序列數據庫提出了重大挑戰,因為一切都變得高度動態且短暫。 在目前的極端情況下,我們擁有諸如 Docker 和微服務之類的東西,最終我們將擁有許多像亞馬遜的 Lambda 那樣運行的系統,其中計算節點本身的存在是非常短暫的。 目前,在這種環境中進行監視的挑戰已基本解決。 我認為沒有一個流行的時間序列數據庫可以處理它。 但是在 VividCortex,高度稀疏和動態的數據不是理論上或新興的用例,而是我們目前的現實。 查詢,流程以及我們監控的所有其他各種可變活動,都會生成具有這些特征的監控數據。 如果我們要處理的一件事很不尋常,那很有可能。 (再次,我不主張唯一性,只是不尋常)。 這也是一個程度的問題。 在具有這種數據的*中,我們并不是唯一的,但我們受到的打擊可能會比看上去相似的其他公司/產品受到更大的打擊。 部分原因是大多數從查詢角度衡量產品的產品都是從應用程序的角度來看的。 應用程序代碼是有限的,并且不是臨時性的,這會生成數量有限的不同類型的查詢和其他要度量的內容。* 但是,我們有坐在服務器上的代理,并且正在看到服務器的全部工作負載,而不僅僅是來自已檢測應用程序的工作負載。 除非您看到典型服務器從不受監視的來源獲得多少工作量,否則這種區別似乎并不重要。 這包括人工活動和其他臨時活動,監視系統本身,后端應用程序,cron 作業等。 實際上,僅用于衡量應用程序生成的查詢的典型 APM 工具確實存在很多盲點。 無論如何,我們系列的大部分內容(即基數)都是稀疏的,有時僅以相隔幾天或幾周的間隔記錄點。 因為這構成了系列的大部分,但僅占數據的一小部分,所以任何假設將某個塊/頁面/范圍用于一個系列的系統(假設以后將被填充)都是行不通的。 那里幾乎是空白。 ## 讀或寫優化? 在存儲和檢索我們一直在討論的數據類型時,存在著一個巨大的,存在的,生存或死亡的問題來回答:讀取優化還是寫入優化? 通過研究折衷,您可以輕松地建立事業。 如果對其進行了讀取優化,則將面臨巨大的寫擴展挑戰。 如果它是寫優化的,將存在讀取挑戰。 讓我們看看為什么。 如果我們認為主機和指標是真正一起構成系列 ID 的,那么問題就等同于詢問我們要存儲按時間戳排序還是按系列排序的數據。 對于(概念上)僅追加的不可變存儲(例如我們的時間序列數據),我們將按照時間戳順序對其進行讀取(對嗎?),乍一看,寫優化似乎是理想的選擇,因為數據將自動按照時間戳順序進行存儲。 只需在數據末尾附加點: ``` v host metric ts value | 1 a.b.c.d 1426964208 123 | 1 d.e.f.g 1426964208 87654 t 2 a.b.c.d 1426964208 19283 i 2 d.e.f.g 1426964208 183 m 3 a.b.c.d 1426964208 9876 e 4 l.m.n.o 1426964208 72736 | 5 a.b.c.d 1426964208 17364 | 1 a.b.c.d 1426964209 129 | 1 d.e.f.g 1426964209 87656 v 5 q.q.q.q 1426964209 9988 ``` 問題在于我們想要一起讀取的數據沒有聚集在一起。 如果要讀取主機 1,從給定的時間戳到另一個時間戳的度量`a.b.c.d`,我將遍歷*大量*不需要的數據來執行此操作。 利用上面描述的稀疏特性,您可以輕松地計算出,我可能比我關心的多讀取了幾個數量級的數據。 這是*讀擴增。* 自然,索引是解決此問題的方法。 這就是發明索引的目的。 但是索引將復制數據并寫入,并為寫入引入隨機 I / O。 當工作集超出內存時,B 樹索引的性能和開銷也會大大降低,并且我們的數據比內存大。 諸如 LSM 樹之類的新一代索引并不能解決所有這些問題。 無論我們怎么看,我們都不會免費獲得讀取優化和寫入優化。 一定會有一些讀取,寫入和/或空間放大。 鑒于純寫優化存儲將無法正常工作,正確權衡的問題至關重要。 如何在不對寫入造成嚴重影響的情況下對數據進行讀取優化? 換句話說,用于讀取優化的數據的正確組織是什么? 通過在上面的示例中顯示時間序列表的架構,我已經部分給出了我們答案的早期版本。 我們以`(host, metric, timestamp)`順序在數據上定義了集群主鍵。 因此,這些點按主機和指標分組在一起,并且在其中按時間戳排序。 這樣可以在一段時間內針對一組單獨識別的系列的范圍讀取工作量優化數據。 但是,此群集對批量讀取工作沒有任何作用。 對于該工作負載,我們正在一系列時間戳上讀取大量數據,并且正如我所提到的,這些系列構成了大多數數據。 因此,最有效的方法可能就是掃描整個范圍,并丟棄不需要的數據。 二級索引(時間戳優先)可能是訣竅。 我們走了這條路,事實證明,由于要捕獲的指標多種多樣,因此此處的數學也不可行。 因為我們捕獲了許多不同的批量指標組,但一次只查看一組,所以我們仍在檢查少數數據,并且仍將極大地放大讀數。 在早期,隨著我們繼續向捕獲的數據集中添加越來越多的數據,這個問題變得更加嚴重。 回想一下,這是我們客戶最有價值的用例。 這使其成為非常重要的問題。 我將回到這個問題,稍后再解釋我們的解決方案。 現在,我想探索更多我們的架構,并為其他各種有趣的事物設置背景。 ## 分片和分區 正如您所期望的,我們擁有多租戶分片存儲架構。 分片有助于滿足我們的幾種需求:寫入擴展,讀取擴展,數據隔離和租期以及安全性。 通常,只能通過分片來實現寫縮放。 實際上,這確實是在全局中進行分片的唯一原因。 可以通過“橫向擴展”復制來很好地解決讀取擴展問題,這可以提供更多的讀取容量。 數據隔離對我們很重要。 有時,SaaS 供應商會在所有客戶中展示諸如實時分析之類的信息。 這種功能意味著一定程度的混合和缺乏隔離,我覺得有些不安。 我們正在努力防止某人獲得廣泛,不受限制的訪問他們不應看到的數據的可能性。 在安全性與便利性之間取得平衡,我們傾向于使針頭遠離便利性。 (我們還[對敏感數據](https://vividcortex.com/blog/2014/11/11/encrypting-data-in-mysql-with-go/)進行加密,例如在飛行中和靜止時的 SQL 端到端示例。[在即將舉行的 Percona Live MySQL 會議](https://www.percona.com/live/mysql-conference-2015/sessions/encrypting-data-mysql-go)中,我將對此進行討論。) 為此,我們的分片單位是客戶的“環境”-考慮分期,生產等。 主機屬于一個且只有一個環境,并且所有 API 訪問都被隔離到一個環境中。 API 令牌被定義為只能訪問一個環境,因此在邏輯級別和物理級別上,每個環境都與所有其他環境隔離。 有時我們談論按環境和主機進行重新分片,但到目前為止,即使是最大的客戶環境也沒有問題。 分片實際上是分區,我剛剛描述了按環境分區。 由于多種原因,我們還按時間范圍劃分數據。 一種是使清除過期數據變得容易。 刪除數據的成本太高了,在許多系統中,至少要使系統的寫入工作量增加一倍,甚至更多。 取消鏈接文件要好得多。 從本質上講,它是免費的。 按時間分區是我們滿足批量讀取工作負載需求的方法之一。 按時間進行的分區本質上是一個粗粒度,開銷少,時間戳優先的索引。 起初,我們一次分配一天; 現在,我們一次分配一個小時用于 1 秒數據,一天一次分配一個 1 分鐘數據。 我們最初進行分區的方式是我要承擔個人責任的另一個錯誤。 開始時,我們使用本機 MySQL 分區。 這有一些好處,我認為這些好處將勝過任何缺點: * MySQL 本機修剪不包含請求時間戳的分區 * 分區表隱藏了應用程序代碼的復雜性 * MySQL 5.6 為分區添加了許多不錯的功能,例如拉出一個分區并分別對其進行操作,然后將其交換回(EXCHANGE PARTITION)的功能。 我還認為,隨著 MySQL 5.6 的改進,我們不會遇到早期版本的 MySQL 分區遇到的問題。 我是對的,我們不是。 我們遇到了其他人! 哎呀。 分區維護作業(刪除舊分區并創建新分區以保存將來的數據)將阻止長時間運行的查詢,從而導致一切停止,并使服務器無法連接。 模式更改曾遇到類似的問題,在確定大容量讀取工作負載無法正常工作并試圖修復它時,我們不得不做幾次。 模式更改還消耗了大量時間,并且由于元數據鎖定等原因,模式更改永遠不可能完全熄滅。 這些都是無聊的可預測的事情,只是因為我對它的思考不夠深而發生。 因此,我們基本上遇到了幾次“ ALTER TABLE 死機”和“分區維護死機”,然后從一開始就切換到應該做的事情。 我們為每個時間范圍創建了單獨的表,并使用表名對架構版本,數據分辨率和時間范圍進行了編碼。 1 秒數據的架構版本 1 的示例表名稱為`observation_1_s_1424444400_1424448000`。 (我們將時間序列指標稱為“觀測值”,因為命名很困難,而且我當時還沒有想到“點”。) 新的模式版本只是被寫入新表中,并且當從所有環境中清除舊數據時,我們最終可以刪除旨在處理該模式版本的代碼。 我們不再更改。 舊數據將長期存在。 這類似于我在 New Relic 上看到的情況,并且我認為此博客上有較早的文章對此進行了討論(我也曾在 New Relic 進行過咨詢),但是我認為分區的改進使其變得不必要。 為我們。 不幸的是,這是成為顧問的缺點之一。 您認為您了解系統方面的知識,但您不了解 jack,因為您只是在系統附近,您自己還沒有操作過它。 活到老,學到老。 ## 緊湊的存儲 我們的架構看起來并不像我之前顯示的那樣。 如果考慮以下偽代碼之類的模式及其包含的數據,則會發現效率低下: ``` observation ( host number metric string ts number value number primary key(host, metric, ts) ) ``` 首先,將有很多主機重復。 接下來,您將獲得指標,這些指標也將瘋狂地復制。 時間戳也將基本相似。 而且大多數時間序列值都為零。 最后,最重要的是,每個點將有一行。 這本質上是行標頭的瘋狂數目,最后附加了一些小數值。 為了解決這個問題并更緊湊地存儲我們的數據,我們做了幾件事: * 我們使用主機 ID 和度量 ID,而不使用諸如主機名和度量名稱之類的文本字符串。 * 我們以向量的形式將值分批處理,因此它不是每秒一行,而是每更長的時間范圍(當前是每分鐘,但將來可能會改變)一行。 這將分攤行中鍵的成本,這是我使用多年的技術。 它確實使某些事情不太方便,但是值得這樣做,因為它使行數減少了 60 倍。 * 我們并不總是存儲確切的值。 我們使用一種定制的壓縮算法,該算法利用了我們看到的時間序列數據的特征。 這進一步減少了我們價值觀的存儲空間。 某些高動態范圍指標有時可能只占誤差的一小部分,但沒人在乎。 * 我們存儲大多數指標的增量,這些指標通常很小且易于壓縮。 沒有人會像查詢數量一樣在乎計數器的實際值。 無論如何,它將重置并重新啟動。 人們關心的是這類數據的費率,而不是價值。 我們通過計算時間范圍內的增量(例如每秒)來生成速率。 我們將某些類型的數據計算為值和費率,但這只是少數。 * 我們不存儲零。 一個“心跳”度量標準(例如系統時間戳或正常運行時間計數器)足以確定我們是否及時測量了一批值,因此我們可以分辨出零值與缺失值之間的差異。 這有助于逐步改變度量標準,其中有很多。 * 我們計算各種匯總指標,位圖等,并將它們與向量一起存儲。 這對于不需要訪問每個點的操作很有用,例如批量讀取。 它還使我們能夠進行諸如下采樣之類的事情,而不會犯諸如“平均值”問題之類的愚蠢數學錯誤。 ## API 和服務 我們的外部和內部 API 主要是基于 HTTPS 的 JSON。 一些內部服務使用協議緩沖區而不是 JSON,但是這些附加的依賴項和模式定義是我們僅在需要時添加的內容,并且在我們認為事情已經解決了足夠的時間之后,我們就不會浪費額外的程序員時間來重做我們沒有做的事情。 沒錯。 我們最初只有一個整體的 API 流程來處理所有內容。 當然,這僅僅是原型,我們很快就放棄了。 整體式 API 存在許多問題,例如成為單點故障。 我想建議一些通用的良好 API 做法; * 想一想 API 設計。 不要為自己設計 API,而是為可能想要創建一個假設的應用程序來做與您自己的工作截然不同的人設計 API。 請參閱 [Interagent 指南](https://github.com/interagent/http-api-design)。 * 分離出您的讀寫路徑。 讀取和寫入工作負載通常非常不同。 對于我們這樣的服務,最重要的是我們不會丟失傳入的數據。 長時間運行的繁重請求在某些情況下會輕易導致讀取錯誤影響寫入,并因此而丟失數據的情況。 使用單獨的進程,甚至可能使用單獨的服務器進行讀寫。 * 構建 12 要素應用程序。 首先,我不理解或不同意 12 因子原則,尤其是日志記錄,守護進程和配置準則。 我已悔改了自己的罪過。 * 使用“干凈”的體系結構,并像洋蔥一樣分層。 拒絕讓內部層的任何事物知道,更不用說與外部事物進行交互。 這意味著內部工作或服務不應與您的公共端點進行交互。 將相同的原理應用于您的代碼:代碼中的依賴項應僅指向內部,而不能指向外部。 隨著時間的推移,我們已經轉向了一組準微服務的外部 API,其中(通常是很小的)一組相關端點位于單個二進制文件和進程中,但通過讀寫分開。 我們通過在某些代碼庫中構建讀寫路徑來重用某些代碼,但是通過 a)僅啟用一組讀寫功能的配置和 b)代理規則,僅激活一組。 當此類服務成為我們要在內部構建的東西時,我們將進行下推重構,充分利用此 API 的實質并將其作為內部服務提供。 然后,外部服務將其大部分工作委托給該內部服務,我們也可以在內部其他地方使用它。 我們還將視情況處理特殊類型的數據和工作負載。 我們有專門用于時間序列數據提取的 API 和內部服務的單獨集合,以確保我們可以快速而可靠地持久保存數據,并將寫回確認給發送數據的代理或其他 API 客戶端。 這些 API 是相當薄的前端:它們將寫操作發送到 Kafka,然后完成。 然后,Kafka 使用者將數據從 Kafka 中流回并保留下來,大部分存儲到 MySQL 中。 還有少量的 Redis。 卡夫卡很棒。 我希望它不使用 Zookeeper,并且對分區管理和故障轉移有些不滿意,但總的來說它是簡單而可靠的。 但是,更重要的是它啟用和鼓勵的架構模式。 如果您還沒有研究 [Jay Kreps 的有關基于日志的體系結構](https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying)的博客文章,我鼓勵您這樣做。 尤其是如果您不希望消息隊列/總線使您的整體體系結構更加簡單明了,則應獨立于 Kafka 本身考慮該文章中的想法。 我們非常感謝 Kafka 的 Go Sarama 客戶。 非常感謝 Shopify 團隊和其他人編寫它。 沒有它,Kafka 對我們來說將不是一個可用的解決方案。 通過使用 VividCortex,我們發現并解決了 VividCortex 的許多問題。 我們使用暫存來監視生產。 我強烈推薦這種模式。 生產永遠不要自我監控,因為如果出現問題,您將無權訪問數據以對其進行診斷。 我們還依賴于許多外部服務,包括 GitHub,HipChat,Dyn,Pingdom,CircleCI,VictorOps,BugSnag,還有很多我可能會為遺漏而感到不適。 最重要的兩個是 GitHub 和 CircleCI。 我們還有一個幾乎完全自動化的測試,構建和部署系統,我們可以通過 Hubot 聊天機器人(chatops)對其進行控制。 它與各種服務交互,包括自定義 Go 內部服務,Jenkins 和 Ansible。 CircleCI 立即對新代碼進行了測試-我是否提到了它們的驚人程度-如果通過了,則取決于是否在分支中,將其部署到開發或暫存中。 在“窮人的 Heroku”之后,我們創建了一個稱為“ Pooroku”的系統,以將每個人的 dev 分支推送到我們 dev 服務器的適當命名的子域。 合并的主人可能會自動將其部署到生產中,具體取決于相關代碼的重要性和潛在后果。 如果沒有自動部署到生產環境,則快速`/deploy foo to prod`會處理它。 所有這一切都發生在我們的聊天頻道中,因此每個人都可以準確了解正在發生的事情并學習如何做。 如果您不熟悉 ChatOps 的好處,我建議您在[上寫 Owen 的博客文章](https://vividcortex.com/blog/2014/06/02/chatops-at-vividcortex/)。 我們每天使用 Owen 等人構建的出色系統部署代碼數十次。 我們還做大量其他事情。 在經歷了 ChatOps 和持續交付之前,我不知道自己缺少什么。 ## 使用圍棋 我們從一開始就是 Go 用戶,我們很自豪以各種方式支持 Go 社區并為 Go 社區做出貢獻。 我們所有的外部和內部 API,服務,Kafka 使用者,工作者和其他系統程序都是用 Go 編寫的。 這是我們能夠擴展到已經達到的點的一個重要原因。 它的效率,對并發的支持,簡單明了,出色的標準庫以及靜態編譯的二進制文件,使它通過多種方式實現了夢想。 如果不是 Go 語言,那么我們在產品開發方面的距離可能只有一半,所支付的費用是 EC2 賬單的五倍,或兩者兼而有之。 我們在 Go 和數據庫上寫了[電子書](https://vividcortex.com/resources/building-database-driven-apps-with-go/)。 我們犯了很多錯誤,而這本書主要是關于我們在此過程中學到的知識。 ## 硬件 我們將整個應用程序托管在 EC2 中。 我們故意使用相對弱的實例,這些實例具有有限的 CPU,RAM 和 EBS 卷,且配置的 IOPS 數量適中。 這在很多方面都是錯誤的。 面對大多數人關于在 EC2 中運行數據庫的建議,這種說法不成立。 值得注意的是,這正是我 5 年前譴責的那種情況。 但是,它運作良好,并且是一門金融學科。 這也是避免短期解決硬件問題并突然意識到我們遠遠不能擴展以滿足嚴重問題的需求的一種方法。 我最近沒有看我們正在攝取多少數據(這是那些不方便的跨環境任務之一),但是截至幾個月前,我們平均每秒攝取 332,000 個時間序列點,或每天超過 280 億個 。 那時我們只有三個分片,因此從本質上講,每臺服務器每秒存儲大約 100,000 點。 除了執行扇出批量查詢時,服務器幾乎完全處于空閑狀態。 請記住,這是完全 ACID 事務存儲,而不是希望與祈禱。 我們還在本地和跨區域復制每個分片,以進行讀取擴展,故障轉移/ DR 和進行備份,因此,我們的基礎架構中實際上有 3 臺以上的服務器! 對于讀者來說,將這個數字與與此主題相關的類似博客文章進行比較,以了解我們在原始硬件效率和成本方面的表現如何,可能是一個有趣的練習。 我認為,將我們與其他公司進行比較的要點之一是,您經常可以進行很多優化,并且可以獲得 1-2 個數量級的效率,甚至更多。 同時,我的誠實評估是,相對于我們的同齡人,我們既不是超效率的,也不是完全低效率的; 我們有點中間。 很好 但是精益運營需要付出一定的代價,您必須全面優化業務,而不僅僅是 EC2 賬單。 上市速度是其中的一個重要因素。 ## 沒有銀彈 在我描述我們的系統(尤其是我們的時間序列后端系統)的很多時候,人們會問:“為什么不只使用 X?” 該建議通常聽起來很合理,直到您更深入地了解我們的要求為止。 這不足為奇,因為我們很長時間都不知道我們的要求。 我寫了的[博客文章中解釋了許多要求。 該職位似乎引起了很多人和公司的共鳴。 我認為在問我們為什么不使用特定解決方案之前,有必要先了解我們要解決的一些問題。 (建議賣方在閱讀一些愚蠢的解決方案之前,先閱讀該文章并進行每節點每秒的運算,以使我們使用的服務器數量是目前的 100,000 倍。)](http://www.xaprb.com/blog/2014/06/08/time-series-database-requirements/) 我不想表現出防御性,好像我的自我讓我不愿意考慮更好的解決方案的可能性。 但是請記住,實際上有數百種可能的解決方案需要考慮和/或評估。 那是不切實際的。 快速淘汰是我的首選策略。 人們提出的許多銀彈包括 HBase,OpenTSDB,Cassandra 和 Hadoop。 這并不是要批評他們中的任何一個。 結合數學,基準測試和對我們發現的要求的檢查,我得出的結論是,其中一些條件較差,一些條件相同,有時甚至更好,但沒有一個是即用型的 證明切換的合理性。 某些解決方案可能會取得成功,但這樣做的代價是要通過大量的緩存和批處理使事情進一步復雜化,換句話說,不是交鑰匙的解決方案。 當然,我們不使用 MySQL 作為統包解決方案,但它仍然設置了很高的標準。 同樣,保持技術表面積簡單和清潔本身也是我們的一大勝利。 我的確會繼續關注特定的解決方案或組合以及某些數據庫中正在出現的工作。 我對 NDB(MySQL Cluster,您從未聽說過的最令人難以置信的分布式實時數據庫),InfluxDB,Cassandra 和 Spark 尤其感興趣。 否定所有選項的詳細信息將花費很長時間,但是作為特定反駁的*樣本*,使用 Cassandra(通常用于時間序列數據)將需要傳輸數百 GB(或更多)的數據 通過網絡為我們的一些客戶提供服務。 那是因為它尚不支持對數據庫本身內的表達式求值。 人們建議的大多數系統通常都存在類似的反對意見。 通常的解釋是,當人們將解決方案 X 應用于一個類似的問題并獲得成功時,規模的一個維度是他們處境中的 O(1),而這是我們的交叉乘積因子。 許多事情在許多情況下都可以很好地工作,但是當您將一個或三個額外的叉積投入組合時,將變得很困難。 有其他公司存在的證據,證明它們對非常大的數據集進行批量讀取類型的查詢并使其快速。 例如, [Scalyr 在智能暴力數據處理](http://blog.scalyr.com/2014/05/searching-20-gbsec-systems-engineering-before-algorithms/)上有一篇不錯的博客文章。 New Relic Analytics(nee Rubicon)使用蠻力。 我的一些顧問還熟悉內部系統,這些內部系統會在非常大的公司內部大規模進行這種分析。 我們已經詳細討論了技術,成本和結果。 關于這一點,需要注意幾件事。 一是我們已經自己做了很多扇出。 Go 使此操作非常容易。 當我們進行批量讀取查詢時,大量的計算和存儲變得很忙,而且我們的處理效率很高,因此我認為沒有很多未開發的潛力。 但是,另一件事是,您可以執行 100%扇出的數量有實際限制。 如果設計使一個龐大的查詢停止了除一個用戶之外的所有用戶的訪問,那么您將遇到可伸縮性問題。 這就是至少其中一些系統的工作方式; Scalyr 博客文章很好地解釋了這里的一些細微差別和折衷。 最后,大規模扇出自動需要大量數據復制,這加劇了寫入擴展問題。 最后,盡管有一小段時間我以為使用批量讀取查詢可能會使用蠻力,但我堅信這不會。 后來,我的一位顧問向我介紹了一位存儲專家,他比我更了解硬件的物理功能。 我們在白板上重新進行了數學運算,并找到了類似的答案-要用數千美元的最昂貴的機器以不可思議的成本運行最先進的 PCIe 存儲,以足夠快地對它進行暴力破解,以滿足我們的要求。 正如他所說的那樣,“離可行的解決方案還有很多零”。 ## 批量讀取優化的演變 通過上述上下文,您可能會看到,我們基本上是在使用 MySQL 而不是簡單地將其用作構建為分布式 Go 服務集的大規模時間序列數據庫的存儲引擎。 我們利用 SUM()和其他幾個函數使計算盡可能地靠近數據并避免網絡傳輸,并且利用了 InnoDB 的聚集索引,這是一個巨大的勝利。 否則,我們將在 MySQL 之外進行很多繁重的工作。 希望批量讀取的一些挑戰現在對您顯而易見。 您可能可以做一些數學運算,并弄清楚存儲多少數據,擁有多少行等等的粗略數。 但是,批量讀取操作的實際實現具有許多重要的細節。 冒著太多的香腸制作方法的風險,我將更深入地研究其中的一些。 回顧一下: * 可以按多種維度對多種事物進行排名 * 很多稀疏指標 * 很多主持人 * 時間范圍大 * 搭配包包的 Top-N 早在最初只是原型時,我們的第一個實現在每個地方都存在很大的效率低下的問題。 我們基本上采用了您可以想象的最簡單的嵌套循環方法,如下所示: 1. 查詢指標表并找到與所需模式匹配的所有可能指標。 2. 對于每個主機,每個指標, 3. 獲取時間范圍內的指標并對其重新采樣。 4. 使用插入排序來保留前 N 個。 5. 總結剩下的全部內容。 6. 返回。 即使對于較小的數據集,這也需要花費很長時間,并且往往會使內存不足。 我們立即注意到的最大的效率低下之一是,在大多數情況下,步驟 3 不會返回任何數據。 另一個明顯的問題是,即使查詢非常快,對數據庫執行如此多的查詢確實非常耗時。 下一步是停止執行單指標讀取,并在查詢中使用指標/主機對的組合,例如`metric IN(...) and host IN(...)`。但是,這些列表非常大。 MySQL 基本上將此類列表的每種組合視為單獨的查詢,就像在將其推送到數據庫中之前所做的一樣。 很好,除了用于此類查詢的計劃過程一次重新計劃每個組合而且在 InnoDB 中并不便宜。 我在高性能 MySQL 中寫了更多有關此的內容。 這些查詢花了很長時間來計劃,即使我們是小批量地進行。 同樣,由于數據的稀疏性質,大多數組合在任何給定的時間范圍內都沒有產生任何行。 顯然,我們需要粗粒度的時間戳優先索引只是為了消除考慮中的查詢組合,因此我們沒有嘗試尋找它們。 我們仍將大部分時間用于尋找不存在的數據。 知道這并不是一個真正的大勝利,但是還沒有找到更好的方法,所以我們引入了今天內部稱為“選擇不同”的內容。 針對我們的數據庫可能有很多 SELECT DISTINCT 查詢,但是這個臭名昭著,以至于用這兩個詞就足以識別它。 該查詢找到了一個時間范圍內存在的指標和主機的不同組合。 過了不久,我們才找到解決方案,對此進行了很大的改進,因此我們遭受了如此之久的痛苦,難以接受。 要解決此問題,我們需要一種時間序列事實,即“在時間范圍 X 內,主機 Z 上的指標 Y 具有值”。 理想情況下,我們可以問“對于時間范圍 X 和主機 Z,存在哪些度量標準”的問題。 一種方法是在時間序列指標表上創建二級索引,但這是一種超昂貴的方法。 另一個方法是創建大致的時間段(例如,按小時計算),并針對在該小時內進入我們的 API 的主機/指標的每種組合簡單地記錄“是”。 即使已設置為真,也要設置為真,這種行為在 MySQL 中是殘酷的低效,因為它仍然是事務,它仍然會被記錄下來,并且還會執行許多其他工作。 我們添加了 Redis 來處理該索引。 這大大改善了情況,使我們能夠更快地制定特定客戶的熱門查詢。 我們還向指標表添加了 firstseen / lastseen 列,我們在嵌套循環算法的第 1 步中使用了它來進一步修剪。 我們也將 Redis 用作更新這些列的寫緩沖區,因為使用每個傳入指標更新這些行將花費太多精力。 另一個改進是在重新采樣和生成前 N 個和總體的過程中。 我們消除了重采樣,只是尋找所需維度的前 N 個最大和。 直接將其推入 MySQL 是簡單有效的,查詢類似于 ``` SELECT host, metric, SUM(val) FROM tbl WHERE ts BETWEEN x AND y GROUP BY host, metric ORDER BY SUM(val) DESC LIMIT N ``` 我們利用向量的摘要列和其他一些次要的東西來盡可能地改善此問題,但是當我們開始計算每個類別的摘要序列并使用它們來避免產生總包時,取得了巨大的成功。 以前我們需要包包,所以我們可以顯示每個系列構成了多少。 這一點很重要,因為沒有它,您將不知道是否要優化等其他不重要的東西。 由于總系列只是類別中所有系列的總和,我們可以通過將前 N 個加在一起并從總數中減去它們來消除此步驟。 這消除了對單個系列的絕大多數訪問,因為對于大多數客戶而言,大多數數據都處于拖尾狀態。 另一個優化來自觀察大多數客戶要么查看所有主機上的熱門查詢,要么僅查看一臺或幾臺主機。 當然,最壞的情況是查看所有這些。 為解決此問題,我們引入了由該系列的所有主機聚合啟用的所有主機快速路徑。 通過這種優化,Top Queries 對于 1000 個主機的效率幾乎與單個主機一樣。 (這是讀者要弄清楚為什么它效率不高的一種練習。) 最后,我們消除了與查找與類別模式匹配的指標相關的效率低下的問題,并消除了將系列數據歸為一個類別時將其數據聚類的方法。 為此,我們識別并編號了指標的類別,并在“觀察”表中添加了類別列作為主鍵的一部分。 這再次依賴于 InnoDB 的集群主鍵存儲的有效性。 它還使 Redis 成為 MySQL 外部的“存在”索引而過時了,從而消除了整個類別的故障場景和不一致情況。 Redis 很棒,但是更少就是更多。 所有這些優化都有很多很多細節,我在掩蓋一些細節并引入了不準確性,但是我已經涉及了很多細節,所以我就此止步。 所有這些的要點在于,我們現在能夠支持跨大量的稀疏指標與大量主機交叉產生的批量讀取,以及通過利用數據中的各種模式來優化單個指標的讀取 各種匯總指標,數學和 InnoDB 的聚集索引。 ## 集群主鍵 集群主鍵是 MySQL + InnoDB 的巨大好處之一。 我[在其他地方](https://vividcortex.com/blog/2014/04/30/why-mysql/)寫了關于為什么使用 MySQL 的信息。 我們將數據存儲在 MySQL 中的方式,使它為數據進行索引以進行讀取優化,同時使寫入盡可能地高效,其工作方式比我們期望的要好。 它之所以如此出色的原因之一是 InnoDB 是一個非常復雜的存儲引擎。 相對于您認為效率更高的許多數據庫和存儲引擎,您會驚訝于 InnoDB 算法的真正先進程度以及使用它免費獲得的收益。 這包括事務日志記錄,寫優化,檢查點和緩沖池管理等。 如果我們必須自己構建這些東西,或者使用諸如新一代 LSM 存儲引擎之類的東西,那么我們幾乎肯定會遇到諸如可怕的邊緣情況,大量的寫入停頓以及極其不穩定的性能之類的事情。 我們采用 VividCortex 進行設計的原因之一是[,因此我們可以準確地檢測出這類問題](https://support.vividcortex.com/faults/)。 對于 InnoDB 和 MySQL 工程師來說,我們在低端 EC2 實例上獲得了出色的性能,這證明了這一點。 運作良好的另一個原因是我們分割數據的方式。 這使索引樹足夠小,以至于當它們的工作集大于內存時,我們不會碰到寫懸崖。 其結果與 InnoDB 復雜的刷新算法相結合,是我們的讀取優化實際上并沒有對我們的寫入造成太大的損失。 最后,InnoDB 的聚集索引。 如果我們沒有使用按索引順序對數據進行物理聚類的數據庫,那么它的工作效率將不那么高。 在 MongoDB 或 Postgres 中可能有多種方法可以做(在 Postgres 中有一些功能,例如部分索引,可以為我們提供更多優化)。 但是,如果沒有集群主鍵,很難想象如何使另一個基礎數據庫對我們來說像 InnoDB 一樣高效。 但是,MySQL 和 InnoDB 中的某些功能仍然不盡如人意。 存儲引擎接口固有的效率低下,并且存在許多種鎖定方式。 對于我們根本不需要的許多 ACID 東西,還有額外的開銷; 如果它們能夠更好地工作,那么某些事情將使我們受益,例如數據壓縮(在 InnoDB 中,這對我們來說不是勝利,但可以實現更好的實現)。 這種低效率的清單很長,我懷疑機會成本是幾個數量級。 不過,與我研究過的幾乎所有其他廣泛使用的數據庫技術相比,MySQL / InnoDB 似乎處于良好的狀態。 ## 結論 希望您發現這對我們擴展服務規模以滿足不斷增長的客戶群和異常需求是一個有用且有趣的旅程。 我們一直在改進我們的系統。 可以這么說,我們還有很多干粉,但是我們并沒有過早地解決我們既看不到開始發生又無法避免的問題。 最后,這不是魔術。 有很多創造力和很多數學,大部分是餐巾紙。 數據受物理學定律的約束,我們可以在很大程度上預測挑戰的規模以及解決挑戰所應采用的技術的局限性。 更加困難的是預測客戶將為產品找到的用例類型,以及由此需要向數據庫提出的問題類型。 VividCortex 的出色團隊給我留下了深刻的印象,他們每天都在解決棘手的問題,并且看起來很容易。 非常感謝他們,我們的投資者和顧問,許多慷慨的朋友和客戶。 ## 相關文章 * [關于黑客新聞](https://news.ycombinator.com/item?id=9291237) * [GopherCon 2014 使用數據庫/ SQL 構建數據庫應用程序](https://www.youtube.com/watch?v=m879N2rzn2g),作者:Baron Schwartz * [男爵 Schwartz:2014 年及以后的 MySQL,SQL,NoSQL 和開源](https://www.youtube.com/watch?v=f1hSvfDIfbo) * [MySQL 中的時間序列數據](https://www.youtube.com/watch?v=kTD1NuQXr4k) by Baron Schwartz * [轉到數據庫/ SQL 教程](http://go-database-sql.org/) * [VividCortex / dbcontrol](https://github.com/VividCortex/dbcontrol) -Go 的數據庫/ sql 程序包的包裝,提供了連接池限制 HighScalability 很幸運能夠擔任該職位-Baron 的文章中總結了很多經驗。 :) 不能再同意蘭德爾了。 男爵是個地獄的家伙。 運氣還不止于此。 如此之多的人選擇在 HighScalability 上共享如此高質量的內容,這讓我感激不已。 很棒的文章,內容豐富! 我有一個問題。 對于此類數據,TokuDB 似乎是一個很好的解決方案。 是否有不使用它的特定原因,或者只是不考慮使用它?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看