作為一名開發人員,我最有成就感的一項工作就是最佳化:使我的產品Kiln中的某些部分運行得更快。因為即便你的產品有著最好的特性,或者最迷人的介面,如果它的速度慢到令人無法忍受,那它依然是毫無價值的。去年,我的團隊得到一個機會,去最佳化Kiln中運行最慢的一部分,並且最終提高了它的速度,遠比之前快得多。
本文描述的就是一個名為Elasticsearch的優秀工具如何協助我們將Kiln的速度提高1000倍的過程。
Kiln是一個原始程式碼控制工具,它提供Mercurial及Git存放庫的寄宿服務,並且包括代碼審查及一些其它的實用特性。我們在2010年早期發布了Kiln,不可否認的是,v1版本的功能非常基礎,僅包括存放庫管理、代碼審查和包含許可權管理的代碼push和pull。不過作為一個核心產品來說,我們知道它能為使用者帶來實際作用。但當我們自己使用Kiln來開發Kiln的時候,就開始注意到了某些不足。
我們所期望的功能中最重要的一項是搜尋。在堆積如山的原始碼中進行人工尋找是近乎不可能的任務,而像grep這樣的工具又需要你在自己電腦中保留一份原始碼的拷貝。因此當我們在首個版本發布後的第二年夏天進行2.0版本的開發時,我們決定對提交(Commit)資訊、檔案名稱及檔案內容的搜尋功能將作為這一次更新的標誌性特性之一。
SQL Server
在那段時間內,我們對一系列不同的搜尋引擎進行了評估。在搜尋提交資訊這一點上,我們最終選擇了使用我們已熟知的工具:Microsoft SQL Server。提交以一種易於查詢的格式儲存在資料庫表中,因此我們只需開啟MSSQL的全文檢索搜尋特性就能輕鬆實現查詢。我們對檔案名稱搜尋也使用了類似的處理方式:將它們儲存一張資料庫表中,讓SQL Server進行接下來的工作。
OpenGrok
尋找代碼本身就是一項巨大的挑戰,它需要使用各種不同的工具。我們對各種程式碼搜尋引擎進行了比較之後,將目光放在了OpenGrok上,這是一個優秀的工具,而且看起來符合我們的需求。OpenGrok從某個檔案夾下的代碼開始尋找,並使用ctag以解析代碼(本身支援多種語言),並使用Apache Lucene建立索引。OpenGrok不僅僅能為代碼中的每個類、方法及變數建立索性,它還能夠區分代碼的定義與引用,因此你不僅能夠搜尋方法的定義,還能搜尋到所有被引用到的地方。
我們發布Kiln 2時加入了搜尋及其它許多特性,我們對所完成的功能十分滿意,它允許你深入地探索你的代碼,包括了代碼曆史,提交資訊以及你的團隊所提交的每一行代碼。
但隨著Kiln的發展,我們漸漸意識到它的搜尋功能並沒有和我們希望的那麼好。說實話,即使在最好的運行環境下,搜尋速度也低於我們的期望。而在峰值的時候,它基本上就完全不能用了。OpenGrok雖然是令人印象深刻的工具,但它在應對幾萬個存放庫和幾個TB的代碼時的負載能力就不太好了。索引更新失敗的情況時有發生,並且即時索引代碼需要將每個存放庫的代碼都做一個簽出的備份,而不僅僅是記錄,這就使儲存的需求成倍地增長。SQL Server全文檢索搜尋在我們的規模中也開始顯得緩慢,並且對資料庫伺服器造成了極大的負擔。另外,它還有著許多不足之處,限制了我們想加入Kiln中的新功能。
在2012年初期,我們發現是時候重新思考我們的搜尋結構描述了。Kiln的最初設計為每一個Kiln帳戶建立一個新的資料庫。SQL Server在一台伺服器上維護數千個資料庫的時候伸縮性表現不佳,因此我們決定以一種多租戶(multi-tenant)的資料庫應用方式重新設計Kiln的架構,在這種方式下所有帳戶的資料都儲存在同一個資料庫中。由於我們需要將每個帳戶的獨立資料庫遷移至新的資料庫中,我們正好有機會為我們的資料存放區做出一些根本性的改變。這代表我們也可以開始改造我們的搜尋引擎了。放棄已有的OpenGrok和全文檢索搜尋固然是一個很大的遺憾,但隨之而來是更大的優勢。
Solr
再次回到畫板前,我們重新思考在2012年內如何做出一個優秀的搜尋功能來。我們希望把搜尋做成Kiln最好的特性,打造成人們會熱烈討論的金字招牌。經過研究,我們瞄準了兩個不同但看起來頗為相似的兩個搜尋引擎:Elasticsearch和Apache Solr,它們在底層都使用了Lucene,這是目前最強大的開源搜尋引擎了,在它的基礎上各自提供了一個友好的使用者介面,並隱藏了一些Lucene的複雜性。它們都提供了基於JSON的API,各自具有不同的查詢功能,並且發揮了Lucene的全部能量。那麼哪一個更適合於Kiln呢?
通過對每一個工具大量的相關材料閱讀及試用之後,Elastcicsearch看起來佔了上風:它易用、強大、伸縮性良好且速度飛快。運行Elasticsearch非常簡單:如果你已經安裝了Java,只需要去下載它的最新版本就可以運行了。只需幾分鐘時間,我就能夠開發出一個結構並儲存測試資料了。雖然Elasticsearch的文檔中已經描述了一些可用的查詢,但能夠運行我自己的樣本查詢對於學習使用Elasticsearch的最佳方式還是大有協助的。最後一項測試是確保Elasticsearch能夠承受Kiln On Demand的整個資料集,我們不打算為了這項測試採購新的伺服器,因此用上了一點駭客的手段。Elasticsearch在商業硬體上運行良好,它能夠利用你的所有機器上所能提供的所有資源,然後建立一個獨立的叢集。於是我們發動了公司裡幾乎每一個開發人員,讓他們下載Elasticsearch並加入某個辦公室網路的叢集。在一個下午的測試中,我們為Elasticsearch載入並輸出了幾百個G的資料。而它不僅沒被壓跨,甚至在大量寫操作的壓力下也能夠在幾毫秒之內返回結果。Solr則沒能達到我們的期望,它在大量寫操作的情況下讀取效能會嚴重降低,而ES依然保持飛快。很顯然Elasticsearch正是我們的解決方案。