架構視角:檔案的通用儲存原理
架構師是網際網路行業高薪又緊俏的資源。成為架構師最基本的是設計能力。設計與設計的區別主要體現在兩方面:
1,深度:要解決哪些問題?這個問題背後的根本問題是什麼?還有什麼問題沒有發現?對應的能力是發現和解決問題的能力。
2,體系:要解決的問題的屬於哪一類的問題?這類問題能否進一步抽象,讓系統解決更大的問題?對應的抽象歸納和體系化思維的能力。
而做架構的基本功就是研究成熟成功的系統,並總結歸納為一種設計方法新增到自己的設計庫中。今天我們來看看 檔案儲存機制的通用實現及原理-談Kafka、Redis、基於Lucene的搜尋引擎等中介軟體和資料庫的檔案儲存機制。如果你有九年義務教育以上學歷,並且覺得看不懂這篇文章,請給我留言~~
順便插一句,不知道大家有沒有奇怪靜兒最近都沒有寫高可用方面的文章。事情是這樣的,對於高可用的很多設計、架構,靜兒都在進行專利申請中,為了避免對公司造成影響和損失,暫時處於多想不說的階段。
一個商業化中介軟體的效能好壞,其檔案儲存機制設計是衡量一個訊息佇列服務技術水平和最關鍵指標之一。下面先介紹一下各個中介軟體的儲存機制。看不懂可直接跳到最後。
各個中介軟體的儲存機制
1 Kafka
Kafka是最初由Linkedin公司開發,是一個分散式、分割槽的、多副本的、多訂閱者,基於zookeeper協議的分散式日誌系統(也可以當做MQ系統),常見可以用於web/nginx日誌、訪問日誌,訊息服務等等,Linkedin於2010年貢獻給了Apache基金會併成為頂級開源專案。
設計特點
-
Kafka把topic中一個partition(大塊)大檔案分成多個小檔案段,通過多個小檔案段,就容易定期清除或刪除已經消費完的檔案,減少磁碟佔用。
-
通過索引資訊可以快速定位message和確定response的最大大小。
-
通過index(索引)元資料全部對映到memory(記憶體),可以避免segment file(檔案片段)的IO磁碟操作。
-
通過索引檔案稀疏儲存,可以大幅降低index檔案元資料佔用空間大小。
一張圖解釋一下剛才說的特點:
從上面的圖中可以看到,生產者和消費者並沒有直接的互動。這就達到了生產者消費者模式的第一個好處:解耦。作為消費者,可以自定義消費執行緒數和消費者數量。這就達到了生產者消費者模式的第二個好處:限流。這也是大家使用MQ一般想要達到的目的。
2 Redis
談redis首先要糾正一個誤區:快取比mysql快。為了說明這個問題,下面是靜兒自己動手得到的資料。
結論:使用了快取後響應時間不穩定
結論:mysql的響應時間非常穩定
從cat監控中可以看到快取的get時間確實較長。
順便跑題一下,靜兒測試中還發現在jvm正常執行業務邏輯,不太複雜無外部呼叫,一般是1到2毫秒。但是如果丟擲了異常,這段程式碼執行時間要變成100ms+。所以一般程式碼中禁止用丟擲非必要異常來代替正常邏輯。
之所以給大家打這個預防針,是因為下面要給出Redis的描述了。
Redis本質上是一個key-value型別的記憶體資料庫,整個資料庫載入在記憶體當中進行操作,定期通過非同步操作把資料庫資料flush到硬碟上進行儲存。因為是純記憶體操作,Redis的效能非常出色,每秒可以處理超過10萬次讀寫操作,是已經效能最快的key-value DB。
Redis儲存機制分成兩種Snapshot和AOF。無論是哪種機制,Redis都是將資料儲存在記憶體中。
AOF工作原理:是將資料先存在記憶體,但是在儲存的時候會使用fsync(無阻塞程序的)來完成對本次寫操作的日誌記錄。AOF最關鍵的配置就是關於呼叫fsync追加日誌檔案的頻率,有兩種預設頻率。always:每次記錄進來都新增。everysecond每秒新增一次。
儲存模式效能和安全比較:
1.效能
snapshot效能是要明顯高於AOP方式的,原因有兩點:
1>採用二進位制方式儲存,資料檔案小,載入快速。
2>儲存的時候是按照配置中的save策略來儲存,每次都是聚合很多資料批量儲存,寫入的效率高。AOF一般都是工作在實時或準實時模式下。相對儲存頻率高,效率低。
2.資料安全
AOF資料安全性高於Snapshot,原因:
snapshot儲存是基於累積批量的思想,累積的資料越多寫入效率越高,但是如果長時間資料不寫入RDB(redis的資料庫),redis遇到了崩潰,沒寫入的資料就無法恢復。
Redis中有rewrite功能,AOF的儲存是按照記錄日誌的方式去工作的,成千上萬的資料插入必然導致日誌檔案的擴大,Redis這個時候會根據配置合理觸發rewrite操作。這個操作是將最終保留值記錄到日誌檔案中,從而縮小日誌檔案的大小。這個類似於lucene的optimize操作。
3 Mysql/MariaDB
Mysql與目前流行的nosql資料庫最大的不同是規定了嚴格的資料型別。資料型別因為在建立表時在記憶體中嚴格劃定了地址空間,所以能限定欄位的資料儲存長度。
資料型別限定範圍的方式有兩種:1是嚴格限定空間,劃分了多少空間就只能儲存多少資料,超出的資料將被切斷;2是使用額外的位元組的bit位來標記某個資料,儲存了就進行標記,不儲存就不標記。下面介紹3種類型的儲存方式。
1.整形的儲存
它嚴格限定空間,每個已劃分的位元組上的bit位上的0和1直接可以計算出數值,所以它的範圍是根據bit位的數量值來計算的。一個位元組有8個bit位,一個bit位可以構成2的8次方=256個數值。同理2位元組共2的16次方=65526個數值。也就是說,在0-255之間的數字都只佔用1個位元組,256-65535之間的數字佔用2個位元組。
2.char的儲存
char型別是常被成為定長字串型別,它嚴格限定空間長度,但它限定的是字元數不是位元組數。它有“短了就使用空格補足”的能力。
3.varchar常被稱為“變長字串型別”。它儲存資料時使用額外的位元組的bit位來標記某個位元組是否儲存了資料。每儲存1個位元組佔用一個bit位進行標記。一個額外的位元組可以標記256個位元組,2個額外的位元組可以標記65536個位元組。MySQL/mariadb限制了最大能儲存65536個位元組。這表示,如果是單位元組的字元,它最多能儲存65536個字元,如果是多位元組字元,如UTF8的每個位元組佔用3個位元組,它最多能儲存65536/3=21845個字元。
4 基於Lucene的搜尋引擎
提到 基於Lucene的搜尋引擎,大家可能更熟悉ElasticSearch(ES)。 靜兒有段時間專門負責公司的搜尋,沒有現在這麼忙,本應該是非常難得和寶貴的機會。有大把的時間研究原始碼,可惜當時整個心都不在工作上。當時用的還是solr4.X.X(solr是基於 Lucene的搜尋引擎系統, lucene和solr版本同步更新,目前最新是7.6.0),當時solr每月釋出一個新版本。版本有很多bug,我當時是在原始碼上做了很多修復補丁的。但是當時各種意識都很薄弱。如果當初給apache提patch,有些應該是能通過的。這就能形成了一個良好的反饋迴圈,可能現在的狀況截然不同。
想起dubbo有段時間已經不更新版本,後來捐給了apache,給這個專案又重新注入了生機。其中的一個教訓就是大家一定要有很強的開源意識。這種意識還包括在自己做功能方案和開發實現的時候要有業界調研和對標的意識。
靜兒的夢想是實現自己的搜尋引擎中介軟體,所以目前從事的是和這個原理有很深淵源的工作:容器排程。什麼?看不出來聯絡?那這個以後再慢慢聊。
Lucene的資料都存在一個目錄下,一個目錄構成了一個索引。Lucene經多年演進優化,現在的一個索引檔案可以分為4個部分:詞典、倒排表、正向檔案、列式儲存。一張表解釋lucene的儲存原理:
通用原理
1.高頻讀取操作放於記憶體。
2.檔案分段減少磁碟IO操作。
3.磁碟儲存防止資料丟失。
4.更好的結構化可以提升儲存和讀取效率。
以上です。不解釋。
總結
之前和同事聊天,同事說他們架構師基於原來的版本設計了一個非常完美的2.0方案。但是這個方案並不解決現有的任何問題。這也是靜兒想做架構並且可以很輕鬆的找到一個架構師職位,但一直都是在專案組內自己動手寫程式碼的原因:一個旁觀者想了解內部的痛點很困難。
前段時間大家紛紛剖析拼多多優惠券事件背後的技術問題。不可否認,技術是有點菜。但是拼多多目前整體看是成功的。成功在哪裡呢?它成功的解決了商家(其他平臺門檻高,入住不了)和消費者(以更低的價格買到商品)的痛點。所以這點狀況並未傷及元氣。
在做設計的時候,我會首先問自己一個問題:現在哪裡最痛?架構講究深度和體系。深度解決的是痛點根本性的問題。體系解決的是未來的痛點問題。不以解決問題為目的設計的系統都是“成功之母”。