好久以前(在一個遙遠的銀河系中。。。),開發者不得不徹底地知道他們編碼時全部的細節。他們對算法和數據結構必需要十分理解,由於他們接受不了浪費慢速計算機的CPU和內存的時間。java
在這部分,我會提醒你一些概念,由於他們對理解數據庫必不可少。我也會介紹數據庫索引的概念。node
如今,不少開發者不關係時間複雜度。。。 他們是對的! 但當你處理茫茫大的數據時(我不是在說數千),或者若是你再和毫秒在戰鬥時,理解這個這個概念即爲重要。你知道嗎,數據庫是要處理以上二者狀況!我不會耽誤你不少時間,只是過個概念。這會幫助咱們理解基於成本優化(Cost-Based Optimization) 的概念。算法
時間複雜度是用來觀察算法在給定數量的數據的狀況下會耗費多長時間。爲了描述這個複雜度,計算機科學們使用數學的大O表示法。這個符號與一個函數一塊兒使用,這個函數用於描述一個算法在給定數量的輸入數據下須要執行多少次操做。數據庫
舉個栗子,當我講這算法在「O(some_function())」時,這意味着在必定數量的數據中,算法須要執行 some_funtion(a_certain_amount_of_data) 次操做。數組
更重要的不是數據量,而是當數量的量增大時操做次數增長的方式。而時間複雜的雖然沒有給出準確的操做數,可是它依然是一個很好的想法。
在這圖中,你能夠看到不一樣類型的複雜度的走向。我用對數座標去繪製的。換句話說,這些數據是從 1 到 10億 的快速增加的。咱們能夠看到:服務器
在數據量較少的時候,O(1) 和 O(n²) 的差別能夠基本忽略不算。舉個例子*1,假設你有一個算法,須要處理 2000 個元素數據結構
O(1) 和 O(n²) 看起來相差不少(4百萬),可是你最多失去的時候更多隻有 2ms,知識一眨眼的時間。實際上,當前處理器能夠處理每秒數億次操做。這就是不少IT項目中性能和優化不是問題的緣由。 正如我說的,面對十分龐大的數據時知曉時間複雜度這個概念仍是很是重要的。若是這算法須要處理 1,000,000 個元素(這對數據庫來說也不是個很大的數據)多線程
我不用算也知道 O(n²) 的算法可讓你有時間喝杯咖啡(甚至是第二杯),若是在數據量上再加多個0,就能夠有時間小睡一下了。框架
再給你一些概念:分佈式
注意:在下一個部分,咱們將看到這些算法和數據結構 有多種的複雜度類型:
時間複雜度一般使用最快的狀況 我只會談及時間複雜度,可是複雜度也能用於:
固然,還有比 n² 更可怕的時間複雜度,如:
注意:我沒有給你大O符號的真正定義,只是個概念。你能夠去維基百科閱讀這篇文章關於大O的真正定義。
當你須要對一個集合進行排序的時候你要作什麼?什麼?你調用 sort() 函數 。。。 ok,好的答案。。。可是想了解數據庫,你必須明白 sort() 函數是如何工做的。 有幾個很好的排序算法,可是我將專一於最重要的一個:合併排序。你如今可能不明白爲何排序數據如何有用,也要在查詢優化部分纔去作。此外,明白合併排序將會幫助咱們在以後理解一個普通數據庫的操做叫合併關聯(merge join)
像不少有用的算法同樣,合併排序是基本一個技巧的:合併兩個長度是 N/2 的已排序的數組到一個長度爲 N 的數組中,只須要 N 次操做。這個操做叫合併。 咱們用一個簡單的例子來看看這是什麼意思
從上面的圖中能夠看到,最想最終能構造出這長度爲8的有序數組,你只需在那2個長度是4的有序數組中遍歷一次。而因爲那兩個數組已經排序了,因此能夠這樣作:
1) 比較兩個數組中的當前元素 (開始的時候,當前元素就是第一個元素了)
2) 把兩個元素中數字最小的放到 最終數組(長度爲8的) 中
3) 已被提取最小數字的數組訪問下一個元素
4) 重複 1,2,3 直到有個數組訪問到的最後一個元素
5) 而後你把另一個數組的剩餘的元素都放在最終數組中去。 這樣作是可行的,由於兩個長度是4的數組都是已排序的,所以你不須要從這些數組中來回進行訪問。 如今咱們已經理解了這個技巧,這是個人合併排序的僞代碼。
array mergeSort(array a) if(length(a)==1) return a[0]; end if //遞歸調用 [left_array right_array] := split_into_2_equally_sized_arrays(a); array new_left_array := mergeSort(left_array); array new_right_array := mergeSort(right_array); //將兩個有序的數組合併成一個大數組 array result := merge(new_left_array,new_right_array); return result;
合併排序將一個問題分解成較小的問題,而後找到較小問題的結果再去獲取最初的問題的結果(注意:這種算法叫分而治之)。若是你不明白這種算法,不要懼怕;我第一次看到它的時候也不名錶。我對這類的算法會把它分紅2個部分去看,這可能會幫助到你。
在切分的階段,會用3個步將數組會被切分到單個元素的數組。正式步驟數應該是 log(N)(由於 N=8 ,log(N) = 3) 我怎樣知道的? 我是天才 一句話:數字。想一下,每一個步驟都將初始數組的大小除以 2。步數是能夠將初始數組除以2的次數。這是對數的精肯定義(在以 2 爲底的對數中)。
在排序階段,你能夠從單個元素開始排序。在每一步中,你能夠執行屢次的合併,總成本(每次合併的成本)是 N=8 次操做
由於有 log(N) 步,因此總共要 N*log(N) 個操做。
爲何這算法恐怖如斯? 由於:
注意:這種算法叫原地排序(我國亦有書稱爲內排序)
你能夠對這算法進行修改,以便用磁盤空間來減小內容佔用同時也不會有巨大的磁盤 I/O 損失。想法就是隻對加載到內存的數據進行處理。這很重要,特別是當你的內存緩衝區僅有100MB而要對幾GB的數據進行排序。 注意:這種算法叫外排序
你能夠對這算法進行修改,可讓他在多線程/線程/服務器中使用
例如:分佈式合併排序就是 Hadoop(大數據框架)的一個關鍵組件
這排序算法是絕大多數(可能不是全部)數據庫會使用的,但不是爲一種算法。 若是你想知道更多,你能夠到看這篇論文,這論文說的數據庫中常見排序算法的優缺點。
如今咱們瞭解了時間複雜度和排序的概念,我也必須在告訴你3種數據結構。這挺重要的,由於他們也是現代數據庫的支柱,我還會介紹數據庫索引的概念。
二維數組是最簡單的數據結構。表能夠看做是一個數組。
例如:
這個二維數組是一個包含行和列的表:
雖然這很容易存儲和可視化數據,但當你須要尋找一個特種的值時,它就顯得很糟糕。
例如,若是你想找到全部在英國工做的人,你不得不查看每行看看這我的是否是屬於英國的。這會耗費 n 個的操做(N 就是行數)這不算太差,但有更快的方式嗎?這就輪到樹的發揮了。
注意:大多數現代數據庫會提供高級數組來高效存儲表格,好比 堆組織表(heap-organized tables) 或者是索引組織表(index-organized tables)。但它不能改變在特定條件下 的按列進行快速搜索的問題。
二叉搜索樹是具備特殊屬性的二叉樹,每一個節點的鍵(key) 都必須是知足
下面讓咱們來看看二叉樹可視化後是什麼一回事
這棵樹有 N=15 個元素。假設我要找鍵值爲208的結點:
如今,假設我要找鍵值爲40的結點
最後,這兩次搜索都用了 樹的層數 次,若是你仔細閱讀了合併排序那部分,你應該會知道這是 log(N) 級別的時間複雜度。搜索的成本的 log(n),還不錯
但這東西是挺抽象的,仍是回到咱們原來的問題吧。不用那些愚蠢的整數,想象下用字符串去表示上面那個表的人的國家。假設你有一個表有一個「國家(country)」的列(column):
這種搜索只花費你 log(N) 次操做,而若是直接用數組搜索就要用 O(N) 此操做了。你剛纔想到的東西就是 數據庫索引。 你能夠爲任何一組列(1個字符串,1個整數,2個字符串,1個整數和1個字符串,日期) 構建索引,只要你有一個函數去對比它們的鍵(keys)來創建鍵與鍵之間的順序(數據庫任何基本類型都能這樣)
就像你看到那些,這裏多了不少結點(以前的兩倍以上)。其實,有額外的結點叫「決策結點」(decision nodes? 應該是藍色的部分)這會幫你找到正確的結點(存儲了相關表中的行的位置)。但搜索的複雜度仍然是 O(log(N)) (仍是同一個級別)。差異最大的是最底部的葉子結點和後繼結點都連在一塊兒了。 使用這 B+ 樹,若是在找 40 到 100 之間的全部值:
假設你找到了 M個後繼結點而樹有 N 個結點。這特徵結點的搜索就像以前的樹那些會耗費 log(N) 次操做。但一旦你找到這個結點了,你就能在 M 次操做內經過他們的鏈接知道 M 個後繼結點了。這搜索只花費 M+log(N) 次操做,相對於以前的樹要用 N 次操做。此外,你不用去讀完整的樹(只需讀 M+log(N) 個結點),這意味着磁盤用得更少。若是 M 不多(好比是 200個)而 N 很大(有1,000,000行)兩個算法就有很大的不一樣了。
但這也會帶來新的問題,若是在數據庫中添加或者修改一行(因此對應的B+樹中去索引):
總之,B+樹須要自排序和自平衡。值得慶幸的是,能夠經過智能刪除和智能插入的操做是實現。但這也帶來一個成本,在B+中插入和刪除都會是 log(N) 。這就是爲什麼你會聽到,使用太多索引不是好主意。其實,索引會減慢在表中插入/更新/刪除行的速度,這是由於每條索引數據庫都須要耗費 log(N) 的操做爲進行更新維護。還有,添加索引意味着事務管理器有更多的負載(咱們在文件的最後能看到這個管理器)
更多細節,你能夠看維基百科的 B+樹的B+樹的文章。若是你想要在一個數據庫中實現B+s樹的例子你能夠看這篇文章和這篇文章,這兩篇文章都是 MySQL 的核心開發者寫的。這兩篇文章都關注 innoDB(MySQL引擎) 怎樣處理索引
注意:讀者告訴我,由於要底層化,因此 B+ 樹須要徹底平衡
咱們最好一個重要的數據結果就是哈希表。這是很是有用的當你想快速尋找值。此外,明白哈希表會幫主咱們再以後理解數據庫一個基本鏈接操做叫哈希鏈接(hash join)。這數據結構也被數據庫用來存儲一些內部數據(像是鎖表和緩衝池,咱們會在後面的內容中看到這兩個概念) 哈希表是能用鍵(key)快速尋找到元素的數據結構。要建立哈希表你須要定義:
這哈希表有10個桶。我很懶,因此只畫了5個桶,但我知道大家很聰明,全部我讓你想象其餘5個桶。我使用的哈希函數是將鍵 模10(即key % 10)。換句話說,要找到桶我只要用元素的鍵(key)的最後一位數字
我使用的比較函數僅僅是2個整數間的是否相等。 假設你想元素78:
如今,假設你想得到元素59
如你所見,不一樣的值查找的成本的不一致的,這取決於你要找的值。 若是我如今講哈希函數改爲對鍵模 1,000,000 (即取最後6位),上面的第二次搜索也只花費1次操做,由於 000059 中沒有元素。因此真正的挑戰是尋找一個好的哈希函數來建立只包含不多元素的桶。 在個人例子中,找到一個好的哈希函數很容易。但那只是一個簡單的例子,找一個好的哈希函數是很困難的,尤爲是遇到(鍵)key是:
好的散列函數,會讓哈希表搜索在 O(1)
爲何不用數組 恩,你問了個好的問題
更多的信息,你能夠讀個人文章,java HashMap 一個高效的哈希表的實現;在這篇文章中你不須要理解 java 的概念