Oracle hash分區的秘密
轉自: http://www.hellodb.net/2009/12/hash_partition.html
在面試時經常會問一個問題,請列舉出hash在數據庫內部的應用,hash的原理雖然簡單,但是它在數據庫中可以說是無處不在。其中hash partition是hash在數據庫中一個簡單的應用,雖然它沒有range partition那麽常用,但是我們在做數據庫水平拆分時,其實就是利用了hash partition的原理,利用hash函數對某個key進行運算,然後將其分布到不同的主機上,原理很簡單。
我們在設計時遇到了一個問題,當分區的數量需要變化時,基於hash的原理,數據可能會從一個分區移動到另外一個分區,因為某個key在4個分區時,可能被分布在分區3,而在8個分區時,可能被分布在分區5。這樣每當分區數量變化時,就需要全部重新分布數據,代價很高。
那麽Oracle是怎麽做的?首先可以肯定的是Oracle的hash partition在分區增加時,不需要做全部數據的重新分布。有人告訴我Oracle的hash函數比較牛,可以保證分區數量增加時,這個hash函數可以讓原來的數據還在舊的分區中,而新的數據可以分布在新的分區。Oracle的函數無非就是get_hash_value或ora_hash(10g),從hash的原理上來說,這也是不可能做到的。
我們對hash partition都有一個常識,就是partition的數量最好是2的次方,也就是2,4,8,16……,否則分區會出現不分區均衡的現象,按照hash的原理,不管是幾個分區,都可以做到完全均衡的,為什麽會不均衡,其實答案已經出來了,Oracle為了能夠增加分區,為你預留了幾個看不到的分區。
假設我們有6個分區,一共8000條數據,數據的分布如下圖:
hash partition不能直接增加分區,而是split當前分區,當需要增加到8個分區時,實際上是分區3和分區4分別split產生新的分區7和分區8,如下圖:
Oracle如何做到分區數量增加後,其他分區的數據不受影響呢,其實很簡單,Oracle在做hash運算時,預留了分區,比如6個分區,實際上是用8個分區的hash來運算的,只不過把缺少的分區的數據合並到其他分區,這樣就會出現數據不均衡的情況。Oracle的公式是這樣的,用等於或者大於當前分區數量的最小的一個2的N次方,比如6個分區做8個hash bucket。我們再來考慮一下2,4,8,16(2的N次方)的情況,比如要把4個分區加為5個分區,因為已經是2的N次方,所以數據會均勻分布,而且Oracle還是使用4個hash bucket。這時新增的分區5實際上把分區1 split後產生的,這時因為有5個分區了,所以會使用8個hash bucket。這時Oracle的hash函數就比較牛了,它可以保證2,4,8,16個分區時,同一個鍵值分布在相同的分區或者是對應可以合並的分區,看下面的SQL:
select ora_hash(‘hellodba’,1)+1 par2,ora_hash(‘hellodba’,3)+1 par4,ora_hash(‘hellodba’,7)+1 par8,ora_hash(‘hellodba’,15)+1 par16 from dual;
PAR2 PAR4 PAR8 PAR16 ---------- ---------- ---------- ---------- 2 4 4 12
上面的SQL我們看到分區的數量在2,4,8,16時,hellodba這個key分別落在在2,4,4,12號分區,雖然落在不同的分區上,但是分區4和分區12是對應可合並的,這樣就保證了數據是不需要移動的。一句話總結就是hash bucket總是2的N次方,如果分區數不足,則會合並數據,產生不均衡的情況,這樣增加分區時,只需要對應分區的數據做split即可。同理,減少分區也不是簡單的drop,而是合並分區。
再回到我們的項目中,我們為了解決這個問題,采用了更簡單的處理方案,直接就做了1024個分區,我們有8個物理數據庫,每個數據庫中有128個表,以後再分拆時,只要移動這些表,並修改應用中的對應關系就可以了。其實和Oracle合並再拆分的思路是一樣的。
這個問題其實在大牛lewis的Practical Oracle8i中講過,當時我並沒有仔細想清楚,現在想清楚了,特此記錄。有些東西,明白了就覺得它挺簡單的,希望對大家有幫助。
Oracle hash分區的秘密