近年來,愈來愈多的NoSql產品不斷的以技術革命的者的身份跳出來:"你看哥是多麼的快,大家關型型數據庫真是戰五渣阿"。是的,高性能的場景下NoSql真的很出彩。而咱們關係型數據庫只能在牆角哭泣"是的,沒錯,他們真的好快"。sql
可是他們爲啥哪麼快?用了雷政富光環了嗎?咱們從瞭解數據庫引摯執行過程來了解一些基礎知識,看看咱們關係型數據庫到底慢在哪?我只粗懂一些SqlServer,只能從SqlServer講起。可是SQLServer是一個很是複雜的軟件。咱們將經過一個查詢的執行過程讓你對SQlServer的核心引摯和運行過程作一個簡單的瞭解。數據庫
因爲Select語句和Update語句基本同樣,可是Update涉及到修改語句,因此咱們直接從一條Sql語句開講。update 碼農表 set 女朋友=1 where 女朋友=0
當咱們碼農寫下這樣一條Sql,小手輕按F5。 一個女友就產生了 一條語句在客戶端被以TDS(Tabular Data Stream)協議形式中發給SqlServer服務端的網絡接口(SQL Server Network Interface)。 什麼是TDS協議這不重要。 由於TDS是一個專屬協議,最先由Sysbase公司設計用於與數據庫服務交互。你無須過多關心。SNI是一個 協議層用於在服務端和客戶端創建網絡鏈接,自SqlServer2005之後纔有。它由一系列同時使用API組成。 這些都不是咱們要講的重點,咱們接着往下說。SNI收到TDS包解包後發現,喲,小子又提交Sql語句了。就將這個包標識爲一個Sql命令,將此Sql命令發給命令解析器 command parser。 command Parser首先檢查語法錯誤,若是發現語法錯誤,將錯誤經過協議層發回給客戶端。若是語法驗證經過,則會生成查詢樹。緩存
查詢樹生成之後,SqlServer要進行一個很是消耗CPU的工做,就是根據查詢樹生成查詢計劃。SqlServer 並非簡單的將一個查詢樹翻譯成查詢計劃,而是通過不斷的比較和權衡。一條SqlServer可能有不少種查詢計劃.好比Select Name from table where a=1
若是這個表上沒有任何索引,那麼可能生成的查詢計劃就是表掃描。若是在A列上有索引,那麼可能應用A列上的索引,也可能不用。總之,SqlServer比較各類方案找到一個開銷最小的執行計劃。而這個找到最小執行計劃的過程,也是比較消耗CPU資源的。性能優化
爲了更加有效的利用資源,SqlServer會對查詢計劃行緩存。將以前執行過的查詢計劃保存在內存中反覆使用。 這裏有個頗有意思的性能優化點。由於內存對SqlServer來講也是很是重要的。若是緩存了大量的查詢計劃也會影響到SqlServer性能。那麼不緩存費CPU,緩存費內存。咱們該怎麼辦?咱們應該更加高效的利用查詢計劃緩存。舉個例子。剛纔咱們執行的語句update 碼農表 set 女朋友=1 where 女朋友=0
而後又執行 update 碼農表 set 女朋友=0 where 女朋友=1
對於SqlServer來講,它會把這當成二條徹底不一樣的語句來處理。服務器
這裏有一個定義:以單獨的SQL語句的形式執行的查詢就是即席查詢(Adhoc)。咱們常常在程序裏拼的sql,就是這種既席查詢。網絡
若是你的系統中有大量的既席查詢,會產生大量查詢計劃緩存。因此推薦你們在程序中寫Sql時,儘可能的使用參數化。update 碼農表 set 女朋友=1 where 女朋友=@女朋友數
既高效又防Sql注入,何樂而不爲呢?性能
到上面爲止,完成這些工做的組件都屬於Sqlserver的關係引摯部分。關係查詢能夠說是SqlServer中最複雜的組件。用於肯定查詢的最佳執行方案。而市面上大多NoSql數據庫沒有關係引摯這部分。語句處理比較簡單。 這就節約了一部分開銷。學習
而數據的訪問和管理則由存儲引擎負責。事實上我也不算說存儲引摯部分的細節。咱們只談談關係型數據庫和NoSql不太同樣的部分。SqlServer和大多數NoSql數據庫同樣,都是很是依賴內存的。內存比磁盤快。這是人所共知的。 SqlServer也有將數據緩存中內存中的機制。每次查詢請求數據時 SqlServer先在在內存中查詢有沒有對應的頁。 這裏的頁是指一個段連續的8KB內存。這段內存是將數據庫文件中的頁(每一個頁8KB)直接映射到內存中的。若是發現有查詢須要的頁將直接將對應的頁的內容打包成結果返回。若是沒有,SqlServer則先去磁盤中找到對應的頁將它載入內存。再將內存中的頁返回。而查詢結束,內存中的頁也不在回收。將一直保存到內存中。直到SqlServer發現操做系統可用內存不足,纔會將一些不常使用的內存頁還給操做系統(多聰明)。並且SqlServer會預先分配一些空白的內存頁。這樣等到用時就不用現分配了。 知道了上面原理,你就能明白 爲何Page Life Expectancy (PLE) 性能記數器的數值越大越好了。Page Life Expectancy (PLE) 越大表示一個頁面在內存在呆的時間越長。也就是說你的內存壓力越小。優化
但是,若是要是更新時SqlServer怎麼辦?咱們知道關係型數據庫對持久性的要求比好多NoSql產品都有節操多了。那數據庫是如何保證持久性(喂不要想太多)和儘可能提升性能的呢? SqlServer使用了一種叫預寫式日誌的技術。簡單來講就是你不是叫我幹活嗎?活還沒幹呢,我先把我要乾的活寫下來。而後等我有空時再幹活。這麼作有什麼好處呢? 首先,咱們知道磁盤的隨機寫入性能是很低的,相反,順序寫入性能要比隨機寫入高不少。若是每次用戶更新數據時,都寫入到數據庫文件,那麼頗有可能產生一個隨機讀寫。這樣可能會影響性能。而SqlServer採用的是先寫入日誌。而後只更新內存中的對應的數據頁。而日誌通常都是順序寫入文件尾部的。這樣一次隨機的讀寫就被轉換成一次順序寫加一次內存修改。性能天然有效提高。 而通常的NoSql數據庫默認狀況下並不保證寫入的持久性。有的是定時刷到硬盤,可是並無預寫式日誌,有的是先讓你返回,成功不成功你再來問一次。操作系統
那SqlServer又是如何保證內存中的數據被回寫到硬盤呢?SqlServer和上面提到有些NoSqL有點類型。SqlServer有一個叫 Lazy Writer 線程,用於週期的檢測 空閒緩存頁的值,若是這個值較低,他將掃描正個數據緩存將較長時間沒有沒使用的頁面過時。若是他發現一個很長時間沒有被使用的髒頁,他也會將被更的頁面但尚未回寫到硬盤的頁--也叫髒頁寫入到磁盤,並將他在內存中標記爲空閒頁。 那若是SqlServer忽然斷電,而內存中的髒頁並無來得及寫入到硬盤腫麼辦?SqlServer使用一種叫檢查點的機制。 檢查點進程確保任何和已提交事務相關的髒頁能被寫入到磁盤,也將未提交事務的髒頁寫入磁盤以確保效率。和Lazy Writer 不一樣,檢點查並不將頁面移出緩存。他只是將髒頁刷入磁盤,而後頁面頭標識這個頁面爲 Clean Page。 默認狀況下,在一個忙碌的服務器上,SQL Server 大約每一分鐘發起一個檢查點並記錄在事務日誌裏。若是SqlServer 實現或數據庫從新啓動,那麼恢復進程經過讀取事務日誌就知道那些工做是在檢查點以前作的。檢查點以前作的工做,他不用理會。注意:CheckPoint的觸發條件,是在CheckPoint期間生成日誌的大小。所以,你們見過內存中有不少髒頁,卻不引起CheckPoint的狀況。 當SQL Server非正常緣由關閉時,也就是在沒有走CheckPoint(會在下面提到)時關閉了數據庫,此時數據庫中數據自己可能存在不一致的問題。所以在數據庫再次啓動的時候,會去掃描日誌,找出那些未提交卻寫入持久化存儲的數據,或已提交卻未寫入持久化存儲的數據,來進行Undo和Redo來保證事務的一致性。SqlServer 會嘗試保證數據庫恢復時間小於1分鐘,可是,至少須要有10M數據寫入日誌,sQLServer纔會觸發CheckPorint
到這,你也就明白了爲何數據庫會慢於NoSql產品了。固然這也只是我所理解的一方面。可能還有不少地方NoSql有優化的地方。若是簡化掉關係引摯,去掉預寫式日誌,數據庫性能會不會也有質的飛躍呢?可這一切又值得嗎? 另外,若是你的數據文件和日誌若是在一個磁盤上,那個可能這個預入式日誌優點會大打折扣。緣由你必定想的到。 其實我只是借這NoSQL火抱一下大腿。這篇貼子並無大多NoSql的內容。我對NoSql也不太瞭解。可是如今你不會NoSql你都當了意思和人家打招呼了。NoSql在一些對事務性要求不過高的地方大有用武之地。我打算看完硬盤裏的波多老師做品就去學習Nosql 了。
波多老師我來了!