如何在不增加投入的情況下讓你的數據庫快上200倍
幾乎每個人都在以這樣或那樣的方式抱怨數據庫的性能問題,數據庫管理員和程序員時常要面臨服務器資源不夠用或數據庫查詢一直執行不完的情況。這些情況對我們來說太正常不過了。
解決辦法多種多樣,最典型的一種就是把槍口指向查詢語句,并譴責程序員沒能寫出高效的查詢。他們本可以使用恰當的索引和物化視圖,或者寫出更好的查詢。你需要增加更多的節點來緩解這些壓力。在某些情況下,你的服務器因執行太多低效的查詢而過載,你會考慮為不同的查詢設置不同的優先級,這樣那些緊急的查詢(比如CEO要求的查詢)可以得到優先處理。如果你使用的數據庫不支持優先級隊列,那么數據庫管理員有可能會取消掉一些查詢,以便騰出資源處理緊急的查詢。
不管你所經歷的是哪一種情況,你都深諳其苦,要等待低效的查詢執行完畢,或者購買更多的云服務實例或更快跟大的服務器。大多數人對傳統的數據庫調優和查詢優化技術都很熟悉,這些技術各有優缺點,不過我們不打算在這里討論它們。在這篇文章里,我們將會討論最近出現的技術,它們不為眾人所知,但在多數情況下它們會為我們帶來更好的性能,幫助我們把握住機會。
我們將著重探討三種場景。
場景一(探索性分析)。作為一個分析師,你通過搗鼓數據來挖掘其中的價值,或者對業務、客戶或服務進行驗證測試。在這些情況下,你一般不知道該關注哪些方面的問題。你運行了一個查詢,查看結果,然后決定要不要再運行另外一個查詢。換句話說,你周旋于一系列的探測性查詢之中,直到找到你想要的結果。
這些查詢中只有一小部分是有用的,它們可能被用于生成公司報表、填充表格或為客戶生成圖表。不過每次在提交查詢之后,你可能需要等上幾分鐘才能拿到結果,等待時間的長短取決于數據量的大小以及并發運行的查詢個數。在等待結果期間,你無法決定下一步該做怎樣的查詢,因為下一步的查詢依賴上一個查詢的結果!
解決方案:在等待結果期間,你可以立即看到“近似***”的結果。這里的“近似***”指的是什么?請比較下面的兩個圖表。
這兩個圖表分別是同一個BI工具兩次從后端數據庫加載查詢數據的結果。右邊的查詢用了71分鐘從1B的數據里得出結果,而左邊的查詢只用了3秒鐘從1M的數據里得出結果!確實,跟右邊的結果相比,左邊的結果不是很精確。不過這樣做是否值得呢?你可以為了得到完整的結果等上71分鐘,或者如果不想等,那么可以立即得到一個幾乎相同的結果。看著左邊的那副圖,很難想象會有人認為71分鐘的等待是值得的!
當然,這并非什么新點子!實際上,所有的瀏覽器都在使用這種模式。當你使用瀏覽器加載一張高分辨率圖片時,可以看到瀏覽器先是加載一張粗糙的圖片,然后圖片會慢慢變清晰。不過想到在數據庫和SQL查詢上使用這種模式的人并不多。
也許你會有所疑問:在實際應用當中如何實現這種提速呢?如果數據不是正態分布的可以嗎?還能看到那些特征值嗎?它是否需要一個特定的數據庫?我希望能在這篇文章結束之前回答所有問題,不過先讓我介紹完其它的場景,在這些場景里你會看到一些更有意思的想法:以快200倍的速度看到99.9%準確度的結果!
場景二(過載的集群)。現今大多數數據庫用戶都無法擁有自己的專用數據庫集群。也就是說,你通常要跟團隊共享集群,或者是其它的報告和BI工具,它們在相同的數據庫資源上執行SQL查詢。當這些集群發生過載,那么只有三種結果:
A. 全局癱瘓。你什么事情都做不了,其他人也一樣。也就是說,一旦數據庫請求隊列被掛起,并且沒有更多的CPU資源可用,那么就沒有人能以他們所期望的速度得到查詢結果。
B. 部分癱瘓。你可以終結或掛起一些低優先級的查詢,讓緊急的查詢(例如你的上司要求執行的查詢)先執行。也就是說,你讓一小部分重要的人開心,但會讓其他人不高興!
C. 如果經常發生上述情況,你可能會購買更多更強大的服務器,或者把系統遷移到云端,根據實際情況使用更多的節點。當然,這需要投入更多的錢,而且會帶來不便,而且這是一個長期的方案。
很多人不知道還有第四種情況,它比前面兩種更好,而且不像第三種那么費錢。那么它是什么?
為低優先級的查詢返回99.9%準確度的結果,而為高優先級的查詢返回100%準確度的結果!根據統計法則,通常使用0.1%的數據就可以得到99.9%準確度的結果。這就是為什么犧牲0.1%的準確性卻能換來100到200倍的速度。我知道沒有人愿意接受只有99.9%準確度的結果,不過除此以外你所能做的,要么中斷查詢,排很長的隊等候,要么空等查詢結束。
在場景一里已經提到過,大多數情況下不需要很長時間都能夠得到100%準確度的結果,但對于那些需要等待很久的查詢,可以嘗試使用99.9%準確性的結果。在文章結束部分我會告訴你們“如何”做到這些。現在只要記住,99.9%的準確性不代表你會失去0.1%的結果。你仍然可以看到所有的東西,只是少了0.1%,而在大多數時候你根本無法看出區別,除非你非常在意。比較下面兩張圖表:
這些查詢結果來自著名的NYC出租車數據集,它們所展示的是到市區所需要的時間。
你能分辨出哪個是100%準確的結果,哪個是99.9%的結果嗎?對大多數人來說,它們是沒有區別的。但上面那個查詢只用了1.7秒,而下面那個用了42.7秒。也就是說,雖然犧牲了0.1%的準確性,卻節省了25倍的CPU時間!讓我們再來看一種場景,然后我會告訴你們“如何”做到這些。
場景三(機器學習和數據科學)。如果你是一個機器學習專家或數據科學家,你會發現自己經常做一些諸如訓練統計模型、參數調優、特性選型或工程方面的事情。最讓人感到崩潰的是需要逐個嘗試大量的參數和特性,而且機器學習模型的訓練會占用很長的時間。集群總是忙于運行和測試模型訓練,數據科學家無法在上面嘗試更多的模型和參數,因此拖慢了整個進程。
對于大多數應用來說,你完全可以基于非***的結果做出合理的決策。比如,A/B測試、問題根源分析、特性選型、可視化、噪音數據或包含缺失數據的數據集。不過如果你是在財務部門工作,那么你應該不會考慮這么做的!
我想另寫一篇文章專門講解如何通過參數調優和特性選型來提速。
那么,我們“如何”做到只犧牲一點準確性就可以換來200倍查詢速度的提升?
答案是使用近似查詢處理技術(AQP)。實現AQP有很多種方式,最簡單的做法是使用隨機取樣數據。事實表明,如果你的數據是非正態的,那么使用隨機取樣數據會導致大部分特征值丟失,而只有少數幾個會出現在你的原始表里。所以更常用的是一種叫作“分層取樣”的技術。為什么要分層取樣?考慮以下情況:
假設你想在這個表上面運行下面的查詢:
- SELECT avg(salary) FROM table WHERE city = ‘Ann Arbor’
運行這個查詢是沒有問題的,不過如果這個表有一億條數據或者這些數據分布在多個機器上,那么這個查詢可能需要運行幾分鐘才能得到結果。你可以在隨機的取樣數據上運行這個查詢,比如:
因為Ann Arbor元組相比原始表里完整的NYC元組會少很多,所以你從取樣數據里可能只能看到少許的幾個,或者根本看不到。而分層取樣會先對數據表按照City進行分層(例如,分區),然后針對每個City的數據進行取樣:
ID | City | Age | Salary | Sampling Rate |
---|---|---|---|---|
3 | NYC | 67 | 62,492 | 1/4 |
5 | Ann Arbor | 25 | 120,242 | 1/2 |
不需要太多的統計數據,我們可以看到這樣的分層取樣可以保證得到非常精確的結果,而我們只使用了原始數據的一小部分。
接下來的問題是你該怎么對數據進行分層取樣以及如何衡量結果的準確性。我在一本書里寫了一個章節介紹這方面的內容。不過我們可以借助一些工具自動完成這些工作。有一些現成的產品可以使用,你只需要按下按鈕,它們會自動幫你完成剩下的工作,并很快地返回結果。有時候,根據這些結果,你完全可以決定如何在準確性和速度之間做出權衡。
BlinkDB/G-OLA
盡管有很多的AQP技術可選擇,不過BlinkDB仍然可以算是***個開源的分布式(高并行)AQP引擎。我參與了這個項目,所以我可能會偏袒它。我很喜歡BlinkDB的解決方案,我認為它給后來出現的學院派或商業的解決方案帶來了靈感。Databricks公司接手了BlinkDB后續的開發工作(這個公司把Apache Spark商業化了)。不久前,Databricks發布了BlinkDB的一個插件,這個插件可以允許用戶對結果進行隨意的調整,直到滿意為止。這個插件叫作G-OLA,不過它并沒有被公開發布,而且BlinkDB也很久沒有更新了。
SnappyData
SnappyData是一個開源的內存混合分析平臺,它的引擎同時支持OLTP、OLAP和流。這個數據庫引擎直接對Apache Spark進行了擴展(所以與Spark完全兼容),并提供了可控的分層取樣和概率性結構來支持AQP。它的查詢語法跟BlinkDB類似,允許用戶指定準確度,也就是說準確度是可以調整的。例如,如果你需要完整的結果,那么你可以指定100%的準確度(默認就是這樣的)。不過如果你想快點得到結果,可以使用99%的準確度,這樣可能在一秒鐘內就可以得到結果。在我看來,SnappyData的一個優勢是它使用了可控的分層取樣。也就是說,你可以在幾秒內運行完一個查詢,就算查詢的是幾T的數據,或者查詢是運行在筆記本上或在同時運行著幾個查詢的集群上。SnappyData還內置了對流的支持,你可以實時地對輸入流進行取樣。
SnappyData另一個優點是它提供了很多上層的用戶界面,你不需要具備很專業的統計知識也能使用AQP特性。例如,他們現在提供了一個云服務,叫作iSight,它會在后臺運行查詢的同時使用Apache Zeppelin作為前端來可視化查詢結果。
爆料:我是SnappyData的推崇者。
Presto
Facebook的Presto有一些實驗性的特性可以滿足基本的近似聚合查詢。我不知道這些特性是否是***的,不過它的不足之處在于你需要使用不一樣的查詢語法(需要修改SQL)才能使用這些特性。對現有的BI工具和應用程序來說,這樣會有點麻煩,因為這樣就無法體現潛在的提速價值,除非我們使用新的語法對原有的查詢進行重寫。
InfoBright
InfoBright提供了近似查詢特性(IAQ)。跟其它系統不一樣的是,IAQ完全不使用樣本。可惜的是,對于IAQ的工作原理,我們也知之甚少,也不知道它是如何提供準確性保證的。不過通過閱讀他們的博客,我認為他們針對底層數據進行了建模,然后使用這些結果代替樣本。IAQ不是開源的,在他們的網站也找不到更多的細節信息,不過他們的解決方案看起來挺有意思。
ABS
Analytical Bootstrap System(ABS)是另一款近似查詢引擎,它使用樣本和高效的統計技術來檢查錯誤。不過它的代碼有點舊了,而且只支持早期版本的Apache Hive。這個項目目前處在不活躍狀態。
Verdict
Verdict是一款中間件,它的客戶端端是應用程序或BI工具,后端是SQL數據庫。你可以像往常一樣在現有數據庫上運行查詢,并立即得到近似結果。原則上,可以在任何SQL數據庫上使用Verdict,也就是說,它不會限制你使用特定的關系型數據庫。不過目前它只提供了Spark SQL、Hive和Impala的驅動。它的優點在于,它可以通用于任何SQL數據庫,而且它是開源的。它的不足之處在于,因為它是一個中間件,所以它可能不像InfroBright或SnappyData那么高效。
爆料:我是Verdict的設計者。
Oracle 12C
Oracle 12C支持近似count distinct和近似百分率。這些近似聚合不僅改進了性能而且使用更少的內存。Oracle 12C還支持物化視圖,這樣用戶就可以對近似聚合進行預處理。不過,雖然近似count distinct和近似百分率很有用,也很常用,不過Oracle 12C并沒有提供對其它類型查詢的支持,不過這些特性已經給用戶帶來很大的好處了。不過據我所知,有很多數據庫廠商一直以來都支持count distinct(例如,使用HyperLogLog算法)。這里有一篇論文,如果你對Oracle 12C的這些新特性感興趣,可以讀一讀。