以下哪个不是迭代算法的缺点_海量数据分库分表方案(一)算法方案
本文主要描述分庫分表的算法方案、按什么規(guī)則劃分。循序漸進(jìn)比較目前出現(xiàn)的幾種規(guī)則方式,最后第五種增量遷移方案是我設(shè)想和推薦的方式。后續(xù)章再講述技術(shù)選型和分庫分表后帶來的問題。
背景
隨著業(yè)務(wù)量遞增,數(shù)據(jù)量遞增,一個(gè)表將會(huì)存下大量數(shù)據(jù),在一個(gè)表有一千萬行數(shù)據(jù)時(shí),通過sql優(yōu)化、提升機(jī)器性能還能承受。為了未來長遠(yuǎn)角度應(yīng)在一定程度時(shí)進(jìn)行分庫分表,如出現(xiàn)數(shù)據(jù)庫性能瓶頸、增加字段時(shí)需要耗時(shí)比較長的時(shí)間的情況下。解決獨(dú)立節(jié)點(diǎn)承受所有數(shù)據(jù)的壓力,分布多個(gè)節(jié)點(diǎn),提供容錯(cuò)性,不必一個(gè)掛整個(gè)系統(tǒng)不能訪問。
目的
本文講述的分庫分表的方案,是基于水平分割的情況下,選擇不同的規(guī)則,比較規(guī)則的優(yōu)缺點(diǎn)。 一般網(wǎng)上就前三種,正常一點(diǎn)的會(huì)說第四種,但不是很完美,前面幾種遷移數(shù)據(jù)都會(huì)很大影響,推薦我認(rèn)為比較好的方案五。
- 方案一:對Key取模,除數(shù)逐步遞增
- 方案二:按時(shí)間劃分
- 方案三:按數(shù)值范圍
- 方案四:一致性Hash理念——平均分布方案(大眾點(diǎn)評用這種,200G并且一步到位)
- 方案五:一致性Hash理念——按迭代增加節(jié)點(diǎn)(為了方便增量遷移)
- 方案六:一致性Hash理念——按范圍分庫(迭代遷移)
方案選擇
方案一:對Key取模,除數(shù)逐步遞增
公式:key mod x (x為自然數(shù))
Key可以為主鍵,也可以為訂單號,也可以為用戶id,這個(gè)需要根據(jù)場景決定,哪個(gè)作為查詢條件概率多用哪個(gè)。
優(yōu)點(diǎn):
- 按需增加庫、表,逐步增加
- 分布均勻,每一片差異不多
缺點(diǎn):
- 很多時(shí)候會(huì)先從2開始分兩個(gè)庫逐級遞增,然后分3個(gè)、4個(gè)、5個(gè)。如在mod 3 變 mod 5的情況下,取模后的大部分的數(shù)據(jù)的取模結(jié)果會(huì)變化,如key=3時(shí),mod 3=0,突然改變?yōu)閙od 5=3,則將會(huì)從第0表遷移到第3表,將會(huì)造成很多數(shù)據(jù)多重復(fù)移動(dòng)位置。
- 會(huì)重復(fù)遷移數(shù)據(jù),當(dāng)分2個(gè)時(shí),有數(shù)據(jù)A在第0號表,分3個(gè)時(shí)數(shù)據(jù)A去了第1號表、到分4個(gè)時(shí)數(shù)據(jù)A會(huì)回到第0號表
方案二:按時(shí)間劃分
可以按日、按月、按季度。
tb_20190101 tb_20190102 tb_20190103 ……這個(gè)算法要求在訂單號、userId上添加年月日或者時(shí)間戳,或者查詢接口帶上年月日,才能定位在哪個(gè)分片。
優(yōu)點(diǎn):
- 數(shù)據(jù)按時(shí)間連續(xù)
- 看數(shù)據(jù)增長比較直觀
缺點(diǎn):
- 因?yàn)榭紤]到歷史數(shù)據(jù)一開始沒分庫分表后續(xù)進(jìn)行分庫分表時(shí),歷史數(shù)據(jù)的訂單號不一定有時(shí)間戳,歷史數(shù)據(jù)可能為自增或者自定義算法得出的分布式主鍵,導(dǎo)致查詢時(shí)必須要上游系統(tǒng)傳訂單號、創(chuàng)建時(shí)間兩個(gè)字段。
- 若上游系統(tǒng)沒有傳時(shí)間,或者上游系統(tǒng)的創(chuàng)建時(shí)間與當(dāng)前系統(tǒng)對應(yīng)訂單的創(chuàng)建時(shí)間不在同一天的情況下,則當(dāng)前數(shù)據(jù)庫表的數(shù)據(jù)記錄需要有時(shí)間字段。因?yàn)樯嫌蜗到y(tǒng)只傳訂單號,這個(gè)時(shí)候需要獲取創(chuàng)建時(shí)間,當(dāng)前系統(tǒng)就必須要有一個(gè)主表維護(hù)訂單號和創(chuàng)建時(shí)間的關(guān)系,并且每次查詢時(shí)都需要先查當(dāng)前系統(tǒng)主表,再查具體表,這樣就會(huì)消耗性能。
- 分布不一定均勻:每月增長數(shù)據(jù)不一樣,可能會(huì)有些月份多有些月份少
推薦使用場景:日志記錄
方案三:按數(shù)值范圍
優(yōu)點(diǎn):
分布均勻
缺點(diǎn):
因?yàn)槲粗畲笾?#xff0c;所以無法用時(shí)間戳作為key,這個(gè)方法不能用表的自增主鍵,因?yàn)槊總€(gè)表都自增數(shù)量不是統(tǒng)一維護(hù)。所以需要有一個(gè)發(fā)號器或發(fā)號系統(tǒng)做統(tǒng)一維護(hù)key自增的地方。
表2 [20000000,30000000)
表3 [30000000,40000000)
……
Version:1.0 StartHTML:000000208 EndHTML:000018668 StartFragment:000004002 EndFragment:000018582 StartSelection:000004002 EndSelection:000018578 SourceURL:https://my.oschina.net/u/4200053/blog/4255903
優(yōu)點(diǎn):
- 分布均勻
缺點(diǎn):
- 因?yàn)槲粗畲笾?#xff0c;所以無法用時(shí)間戳作為key,這個(gè)方法不能用表的自增主鍵,因?yàn)槊總€(gè)表都自增數(shù)量不是統(tǒng)一維護(hù)。所以需要有一個(gè)發(fā)號器或發(fā)號系統(tǒng)做統(tǒng)一維護(hù)key自增的地方。
說后續(xù)推薦的方案中先簡單說說一致性hash
先說一下一致性hash,有些文章說一致性Hash是一種算法,我認(rèn)為它并不是具體的計(jì)算公式,而是一個(gè)設(shè)定的思路。
1.先假定一個(gè)環(huán)形Hash空間,環(huán)上有固定最大值和最小值,頭尾相連,形成一個(gè)閉環(huán),如int,long的最大值和最小值。 很多文章會(huì)假定2^32個(gè)位置,最大值為2^32-1最小值為0,即0~(2^32)-1的數(shù)字空間,他們只是按照常用的hash算法舉例,真實(shí)分庫分表的情況下不是用這個(gè)數(shù)字,所以我才會(huì)認(rèn)為一致性hash算法其實(shí)是一個(gè)理念,并不是真正的計(jì)算公式。 如下圖
2. 設(shè)計(jì)一個(gè)公式函數(shù) value = hash(key),這個(gè)公式將會(huì)有最大值和最小值,如 key mod 64 = value; 這個(gè)公式最大為64,最小為0。然后把數(shù)據(jù)都落在環(huán)上。
3. 設(shè)定節(jié)點(diǎn)node。設(shè)定節(jié)點(diǎn)的方式如對ip進(jìn)行hash,或者自定義固定值(后續(xù)方案是使用固定值)。然后node逆時(shí)針走,直到前一個(gè)節(jié)點(diǎn)為止,途經(jīng)value=hash(key)的所有數(shù)據(jù)的都?xì)w這個(gè)節(jié)點(diǎn)管。 如 hash(node1)=10,則hash(key)=0~10的數(shù)據(jù)都?xì)wnode1管。
概括
這里不詳細(xì)說明這個(gè)理論,它主要表達(dá)的意思是固定好最大值,就不再修改最大值到最小值范圍,后續(xù)只修改節(jié)點(diǎn)node的位置和增加node來達(dá)到減少每個(gè)node要管的數(shù)據(jù),以達(dá)到減少壓力。
備注:* 不推薦對ip進(jìn)行hash,因?yàn)榭赡軙?huì)導(dǎo)致hash(ip)得出的結(jié)果很大,例如得出60,若這個(gè)節(jié)點(diǎn)的前面沒有節(jié)點(diǎn),則60號位置的這個(gè)節(jié)點(diǎn)需要管大部分的數(shù)據(jù)了。* 最好生成key的方式用雪花算法snowFlake來做,至少要是不重復(fù)的數(shù)字,也不要用自增的形式。* 推薦閱讀銅板街的方案 訂單號末尾添加user%64方案四:一致性Hash理念——平均分布方案
利用一致性hash理論,分庫選擇hash(key)的公式基準(zhǔn)為 value= key mod 64,分表公式value= key / 64 mod 64,key為訂單號或者userId等這類經(jīng)常查詢的主要字段。(后續(xù)會(huì)對這個(gè)公式有變化) 我們假定上述公式,則可以分64個(gè)庫,每個(gè)庫64個(gè)表,假設(shè)一個(gè)表1千萬行記錄。則最大64 * 64 * 1000萬數(shù)據(jù),我相信不會(huì)有這一天的到來,所以我們以這個(gè)作為最大值比較合理,甚至選擇32 * 32都可以。
因?yàn)榍捌谟貌簧线@么多個(gè)表,一開始建立這么多表每個(gè)表都insert數(shù)據(jù),會(huì)造成浪費(fèi)機(jī)器,所以在我們已知最大值的情況下,我們從小的數(shù)字開始使用,所以我們將對上述計(jì)算得出的value進(jìn)行分組。
分組公式:64 = 每組多少個(gè)count * group需要分組的個(gè)數(shù) 數(shù)據(jù)所在環(huán)的位置(也就是在哪個(gè)庫中):value = key mode 64 / count * count以下舉例為16組,也就是16個(gè)庫,group=16 這個(gè)時(shí)候庫的公式為value = key mode 64 / 4 * 4,除以4后,會(huì)截取小數(shù)位得出一個(gè)整數(shù),然后 * 4倍,就是數(shù)據(jù)所在位置。
// 按4個(gè)為一組,分兩個(gè)表count = 4:Integer dbValue = userId % 64 / count * count ;hash(key)在0~3之間在第0號庫hash(key)在4~7之間在第4號庫hash(key)在8~11之間在第8號庫……備注:其實(shí)一開始可以64個(gè)為一組就是一個(gè)庫,后續(xù)變化32個(gè)為一組就是兩個(gè)庫,從一個(gè)庫到兩個(gè)庫,再到4個(gè)庫,逐步遞進(jìn)。
從分1庫開始擴(kuò)容的迭代:
下圖中舉例分16組后,變?yōu)榉值?2組,需要每個(gè)庫都拿出一半的數(shù)據(jù)遷移到新數(shù)據(jù),擴(kuò)容直到分64個(gè)組。
可以看到當(dāng)需要進(jìn)行擴(kuò)容一倍時(shí)需要遷移一半的數(shù)據(jù)量,以2^n遞增,所以進(jìn)行影響范圍會(huì)比較大。
優(yōu)點(diǎn):
- 如果直接拆分32組,那么就比較一勞永逸
- 如果數(shù)據(jù)量比較大,未做過分表可以用一勞永逸方式。
- 分布均勻
- 遷移數(shù)據(jù)時(shí)不需要像方案一那樣大部分的數(shù)據(jù)都需要進(jìn)行遷移并有重復(fù)遷移,只需要遷移一半
缺點(diǎn):
- 可以擴(kuò)展,但是影響范圍大。
- 遷移的數(shù)據(jù)量比較大,雖然不像方案一那樣大部分?jǐn)?shù)據(jù)遷移,當(dāng)前方案每個(gè)表或庫都需要一半數(shù)據(jù)的遷移。
- 若要一勞永逸,則需要整體停機(jī)來遷移數(shù)據(jù)
方案五:一致性Hash理念——按迭代增加節(jié)點(diǎn)
(我認(rèn)為比較好的方案)
一致性hash方案結(jié)合比較范圍方案,也就是方案三和方案四的結(jié)合。
解析方案四問題所在
方案四是設(shè)定最大范圍64,按2^n指數(shù)形式從1增加庫或者表數(shù)量,這樣帶來的是每次拆分進(jìn)行遷移時(shí)會(huì)影響當(dāng)總體數(shù)據(jù)量的1/2的數(shù)據(jù),影響范圍比較大,所以要么就直接拆分到32組、64組一勞永逸,要么每次1/2遷移。
方案四對應(yīng)遷移方案:
方案五詳解
現(xiàn)在我想方法時(shí),保持一致性hash理念,1個(gè)1個(gè)節(jié)點(diǎn)來增加,而不是方案四的每次增加2^n-n個(gè)節(jié)點(diǎn)。但是代碼上就需要進(jìn)行對新節(jié)點(diǎn)內(nèi)的數(shù)據(jù)hash值判斷。
我們基于已經(jīng)發(fā)生過1次迭代分了兩個(gè)庫的情況來做后續(xù)迭代演示,首先看看已經(jīng)拆分兩個(gè)庫的情況:
數(shù)據(jù)落在第64號庫名為db64和第32號庫名為db32
迭代二: 區(qū)別與方案四直接增加兩個(gè)節(jié)點(diǎn),我們只增加一個(gè)節(jié)點(diǎn),這樣遷移數(shù)據(jù)時(shí)由原本影響1/2的用戶,將會(huì)只影響1/4的用戶。
在代碼中,我們先把分組從32個(gè)一組改為16個(gè)一組,再給代碼特殊處理 0~16的去到新的節(jié)點(diǎn) 16~32走回原來的32號節(jié)點(diǎn) 32~63走回原來64號節(jié)點(diǎn) 所以下面就要對節(jié)點(diǎn)特殊if else
// 按32改為16個(gè)為一組,分兩變?yōu)?個(gè)庫count = 16;Integer dbValue = userId % 64 / count * count ;if(dbValue<16){ // 上一個(gè)迭代這些數(shù)據(jù)落在db32中,現(xiàn)在走新增節(jié)點(diǎn)名為db16號的那個(gè)庫 dbValue = 16; return dbValue;} else { // 按原來規(guī)則走 return dbValue;}迭代三:
這樣就可以分迭代完成方案四種的一輪的遷移
遷移前可以先上線,增加一段開關(guān)代碼,請求接口特殊處理hash值小于16的訂單號或者用戶號,這樣就只會(huì)影響1/4的人
// 在請求接口中增加邏輯 public void doSomeService(Integer userId){ if(遷移是否完成的開關(guān)){ // 如果未完成 Integer dbValue = userId % 64 / count * count ; if(dbValue<16){ //這部分用戶暫時(shí)不能走下面的邏輯 return ; } } return dbValue; }}// 在分片時(shí)按32個(gè)為一組,分兩個(gè)庫count = 16;Integer dbValue = userId % 64 / count * count ;if(dbValue<16){ // 上一個(gè)迭代這些數(shù)據(jù)落在db32中,有一半需要走新增節(jié)點(diǎn)名為db16號的那個(gè)庫 if(遷移是否完成的開關(guān)){ // 如果已經(jīng)完成,就去db16的庫 dbValue = 16; } return dbValue;} else { // 按原來規(guī)則走 return dbValue;}如此類推,下一輪總共8個(gè)節(jié)點(diǎn)時(shí),每次遷移只需要遷移1/8。
其實(shí)也可以在第一個(gè)迭代時(shí),不選擇dbValue小于16號的來做。直接8個(gè)分一組,只選擇dbValue<8的來做,這樣第一個(gè)迭代的影響范圍也會(huì)比較案例中小。上述案例用16只是比較好演示
優(yōu)點(diǎn):
- 易于擴(kuò)展
- 數(shù)據(jù)逐漸增大過程中,慢慢增加節(jié)點(diǎn)
- 影響用戶數(shù)量少
- 按迭代進(jìn)行,減少風(fēng)險(xiǎn)
- 遷移時(shí)間短,如敏捷迭代思想
缺點(diǎn):
- 一段時(shí)間下不均勻
方案六:一致性Hash理念——按范圍分庫(迭代遷移)
如同上述方案五是方案四+方案一,可以達(dá)到逐步遷移數(shù)據(jù),還有一種方案。就是方案四+方案三,只是不用取模后分組。
userId % 64 / count * count
因?yàn)樯鲜龉?#xff0c;得出結(jié)果中,不一定每一片數(shù)據(jù)都是平均分布的。其實(shí)我們可以取模后,按范圍劃分分片,如下公式。
第一片 0當(dāng)然范圍可以自定義,看取模后落入哪個(gè)值的數(shù)量比較多,就切某一片數(shù)據(jù)就好了,具體就不畫圖了,跟方案四類似。
因?yàn)檫w移數(shù)據(jù)的原因,方案四中,如果數(shù)據(jù)量大,達(dá)到1000萬行記錄,每次遷移都需要遷移很多的數(shù)據(jù),所以很多公司會(huì)盡早分庫分表。
但是在業(yè)務(wù)優(yōu)先情況下,一直迭代業(yè)務(wù),數(shù)據(jù)一進(jìn)達(dá)到很多的情況下16分支一也是很多的數(shù)據(jù)時(shí),我們就可以用一致性Hash理念--按范圍分庫
總結(jié)
以上是生活随笔為你收集整理的以下哪个不是迭代算法的缺点_海量数据分库分表方案(一)算法方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 治理甲醛可以用乐净石吗,效果怎么样?
- 下一篇: tcl空调e0是什么故障