架構師是互聯網行業高薪又緊俏的資源。成為架構師最基本的是設計能力。設計與設計的區別主要體現在兩方面:
1,深度:要解決哪些問題?這個問題背后的根本問題是什么?還有什么問題沒有發現?對應的能力是發現和解決問題的能力。
2,體系:要解決的問題的屬於哪一類的問題?這類問題能否進一步抽象,讓系統解決更大的問題?對應的抽象歸納和體系化思維的能力。
而做架構的基本功就是研究成熟成功的系統,並總結歸納為一種設計方法添加到自己的設計庫中。今天我們來看看文件存儲機制的通用實現及原理-談Kafka、Redis、基於Lucene的搜索引擎等中間件和數據庫的文件存儲機制。如果你有九年義務教育以上學歷,並且覺得看不懂這篇文章,請給我留言~~
順便插一句,不知道大家有沒有奇怪靜兒最近都沒有寫高可用方面的文章。事情是這樣的,對於高可用的很多設計、架構,靜兒都在進行專利申請中,為了避免對公司造成影響和損失,暫時處於多想不說的階段。
一個商業化中間件的性能好壞,其文件存儲機制設計是衡量一個消息隊列服務技術水平和最關鍵指標之一。下面先介紹一下各個中間件的存儲機制。看不懂可直接跳到最后。
各個中間件的存儲機制
1Kafka
Kafka是最初由Linkedin公司開發,是一個分布式、分區的、多副本的、多訂閱者,基於zookeeper協議的分布式日志系統(也可以當做MQ系統),常見可以用於web/nginx日志、訪問日志,消息服務等等,Linkedin於2010年貢獻給了Apache基金會並成為頂級開源項目。
設計特點
Kafka把topic中一個partition(大塊)大文件分成多個小文件段,通過多個小文件段,就容易定期清除或刪除已經消費完的文件,減少磁盤占用。
通過索引信息可以快速定位message和確定response的最大大小。
通過index(索引)元數據全部映射到memory(內存),可以避免segment file(文件片段)的IO磁盤操作。
通過索引文件稀疏存儲,可以大幅降低index文件元數據占用空間大小。
一張圖解釋一下剛才說的特點:
從上面的圖中可以看到,生產者和消費者並沒有直接的交互。這就達到了生產者消費者模式的第一個好處:解耦。作為消費者,可以自定義消費線程數和消費者數量。這就達到了生產者消費者模式的第二個好處:限流。這也是大家使用MQ一般想要達到的目的。
2Redis
談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操作。
3Mysql/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方案。但是這個方案並不解決現有的任何問題。這也是靜兒想做架構並且可以很輕松的找到一個架構師職位,但一直都是在項目組內自己動手寫代碼的原因:一個旁觀者想了解內部的痛點很困難。
前段時間大家紛紛剖析拼多多優惠券事件背后的技術問題。不可否認,技術是有點菜。但是拼多多目前整體看是成功的。成功在哪里呢?它成功的解決了商家(其他平台門檻高,入住不了)和消費者(以更低的價格買到商品)的痛點。所以這點狀況並未傷及元氣。
在做設計的時候,我會首先問自己一個問題:現在哪里最痛?架構講究深度和體系。深度解決的是痛點根本性的問題。體系解決的是未來的痛點問題。不以解決問題為目的設計的系統都是“成功之母”。
