本文從"數據庫是如何處理一個 SQL 查詢的?"這一基本數據庫操作來討論關系數據庫的工作原理。
cost based optimization(基於成本的優化)
為了解成本,需要了解一下復雜度的概念,具體考慮時間復雜度,一般用O表示,對應某個算法(查詢),對於其隨着數據量的增加復雜度增加趨勢,而非具體值,O給出了一個很好的描述。時間復雜度一般用最壞時間復雜度表示,除此還有算法內存復雜度,算法I/O復雜度。
歸並排序:
歸並排序是諸多排序算法中的一種,理解歸並排序有助於之后的查詢優化以及meger join連接。
歸並(merge):
Fig.1
歸並排序的大概過程如圖1所示:把兩個長度為4(N/2)的已排序數組組合成一個有序的長度為8(N)的數組,總計算次數為8(N),即將兩個長度為N/2的數組遍歷次數。整個算法可以分為兩步:
-
分解:把整個大數組分解為多個小數組;
-
排序:幾個小數組被(按順序) 合並起來(使用 merge)構成大數組。
分解:
Fig.2
如圖2,將N維數組逐層分解為一元數組,分解次數為log(N)。
排序
Fig.3
從圖3可知,merge的次數與分解的次數是一致的,每次merge對數組元素排序的次數是相同的(N,這里是8):
Step1: 4次merge,每次對2個元素排序,共4*2次運算。
Step1: 2次merge,每次對4個元素排序,共2*4次運算。
Step1: 1次merge,每次對8個元素排序,共1*8次運算。
故排序的總運算次數為N*log(N)。
算法偽代碼:
array mergeSort(array a)
if(length(a)==1)
return a[0];
end if
//recursive calls
[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);
//merging the 2 small ordered arrays into a big one
array result := merge(new_left_array,new_right_array);
return result;
歸並排序的擴展:
-
在merge 時可以不必創建新數組,而是直接修改原數組,以減少內存,in_place算法即如此。
-
只對內存中正在處理的數據進行加載從而降低內存和磁盤占用,該類算法為external sorting。
-
把歸並過程在多個進程/線程/服務器上執行,這便是hadoop關鍵步驟。
三種重要的數據結構:
數組
數據庫中的表可以理解為數組,如圖4:
Fig.4
每行代表一個對象;
每列代表一個對象屬性,每個屬性有一個固定類型(integer, string…);
二維數組較好的抽象出了數據的存儲,但是當對數據進行過濾尤其是有多個過濾條件時,難度非常大,所以用數組抽象數據是不可取的。
樹(二叉樹/B樹)
二叉樹是一種特殊樹結構,滿足一下特點:
左子樹在整個對應分支最小,右子樹在整個對應分支最大。如圖5:
Fig.5
這是一個簡單的B樹,共15個節點,如要找40,從根節點136開始,136>40,故查詢根節點的左子樹80,80>40,再查80的左子樹,此時40=40。
對應具體問題,如圖4,假設查詢某位員工國籍(即column3字段)是UK的,可將國籍作為樹節點進行搜索,找到節點值為UK,即可找到UK值所在的行,查詢該表中的行,得到其他信息。
B樹只需要log(N)次運算,可作為較好的索引搜索,節點存儲值的類型可以是多種類型,只要有相應類型的對比函數,就可以進行一次或多次查詢過濾。
B+樹
B樹較好的解決了等值過濾問題,但當出現范圍過濾時,就有較大麻煩,比如當要過濾圖5中兩個值之間數值時,復雜度達N,且為獲取整個值不得不加載整個樹,增加了I/O。B+樹只在最后一層節點才存儲數據,其他節點只做路由功能,如圖6.
Fig.6
可以看到B+樹的每個葉子節點都指向其后續節點,因此當查詢t->(M-t)范圍內的數據時,復雜度為M+Log(N),相比B樹N,當N很大時,B+樹顯然速度更快,且因不用遍歷整棵樹所以I/O很小。
Hash表
哈希表是一種通過元素的key快速查詢到數據元素的數據結構,當數據庫做查詢操作時,通過哈希表更快。哈希表一般有幾個部分:
-
給個元素定義一個key值
-
定義一個哈希函數,hash函數通過key找到元素位置(bucket)。
-
定義key值比較函數,通過key值比較函數,在找到的bucket查找對應的值。
Fig.7
如圖7,共10個bucket,哈希函數是modulo,比如此時要找59,通過hash函數找到bucket9,然后在bucket9中通過key值對比函數,59!=99,經過7次運算逐值對比發現沒有59.
經上列可知,查詢復雜度與hash函數相關,hash的值越多(bucket越多),bucket含有的值越少(key值深度越小),復雜度越低,但空間占用越大,hash值越小,則相反。如果哈希函數選擇得足夠好,那么查詢的時間復雜度可以是 O(1)。
Hash與數組:
哈希表可以只將部分bucket存入內存(比如常用),其他的Bucket存入磁盤,而數組不得不分配一塊連續內存空間,尤其當數組很大時,極困難。
----------------------
文章為How does a relational database work筆記,參考其中文翻譯Franklin Yang。