先來(lái)參考文章:https://mp.weixin.qq.com/s/AHRCYOjnXAgcy2j6vziukQ?tdsourcetag=s_pctim_aiomsg
方案
一般情況下,MySQL分表會(huì)有幾種方案:
1、初始點(diǎn)不同但是步數(shù)相同,比如A表起點(diǎn)是1步數(shù)是2,B表起點(diǎn)是2步數(shù)是2;
2、使用一個(gè)中間服務(wù)生成id,不再使用表自增,自己負(fù)責(zé)自增id;
3、使用UUID做id,就不怕沖突啦;
4、使用雪花算法做id,全數(shù)字還相對(duì)自增,一秒幾百萬(wàn)種可能,就不怕沖突啦。
實(shí)際上,如果系統(tǒng)是新開(kāi)發(fā),推薦你是用雪花算法,因?yàn)閡uid不能保證順序性,而第一增加運(yùn)維壓力,第二個(gè)增加開(kāi)發(fā)壓力。
問(wèn)題
問(wèn)題來(lái)咯。
如果是舊系統(tǒng),舊系統(tǒng)是自增id表,現(xiàn)在要分表怎么辦呢?
我們每個(gè)方法預(yù)演一下:
1、舊表是單表,分表必定要根據(jù)業(yè)務(wù)id取模分到各個(gè)子表里面,那么大家的起點(diǎn)就會(huì)有問(wèn)題,設(shè)置步長(zhǎng)也是會(huì)沖突。除非手動(dòng)將每個(gè)子表的maxid重新設(shè)置一個(gè)新的而且各個(gè)表的maxid是有序的,再去設(shè)置相同步長(zhǎng)。這里涉及到運(yùn)維成本!
2、新的中間服務(wù),考慮可用性必須是分布式的,考慮線(xiàn)程安全必須做騷操作,開(kāi)發(fā)成本?。?!
3、呵呵了,人家int的主鍵被你這樣一改,怕是代碼都得重寫(xiě)吧。
4、同上。
所以說(shuō):舊系統(tǒng)是自增id表,分表要么運(yùn)維成本,要么開(kāi)發(fā)成本!
我肯定選擇開(kāi)發(fā)成本,因?yàn)椴介L(zhǎng)法也有一個(gè)很大的缺點(diǎn),不能保證有序性,A子表的id大,B子表的id小,但是實(shí)際上是A子表先插入的。
解決
解決方案:
1、留下一個(gè)表,一行參數(shù)。
|max_id|
| 100|
2、寫(xiě)一個(gè)生成id的服務(wù),每次去表里面那max_id,然后虛擬出100個(gè)id在服務(wù)內(nèi)部(后面業(yè)務(wù)要用的時(shí)候分配),再把max_id設(shè)置為200。
|max_id|
| 200|
3、為了保證可用性,這個(gè)生成id的服務(wù)是分布式的,有多個(gè)。
4、然后就出現(xiàn)并發(fā)問(wèn)題了,每個(gè)服務(wù)同時(shí)取到了100,同時(shí)生成100個(gè)相同的id,同時(shí)去更新100為200.
5、解決辦法,更新的時(shí)候做手腳,如果更新條數(shù)為0就重新拿id。
6、只要實(shí)施類(lèi)似CAS樂(lè)觀鎖的思路(對(duì)比成功才替換的思路),在寫(xiě)回時(shí)對(duì)max-id的初始條件進(jìn)行比對(duì),就能避免數(shù)據(jù)的不一致,寫(xiě)回SQL由:
update T set max_id=200;
升級(jí)為:
update T set max_id=200 where max_id=100;
7、但是實(shí)際上上述是悲觀鎖,利用了mysql的排他鎖機(jī)制,update語(yǔ)句會(huì)變成兩條語(yǔ)句:
update T set max_id=200 where max_id=100;
變種為:
1、 a = select max_id from T where max_id=100;//無(wú)鎖
2、 update T set max_id=a.max_id where max_id=100;//排他鎖
- 先執(zhí)行第一句是無(wú)鎖的,每個(gè)服務(wù)都拿到相同的a。
- 但是第二句是排它鎖,只能有一個(gè)請(qǐng)求執(zhí)行sql,第一個(gè)執(zhí)行之后max_id就變了,那么后續(xù)的update語(yǔ)句都會(huì)執(zhí)行條數(shù)為0.
8、如果update會(huì)自帶排他鎖,但是不能代表一定能夠保證線(xiàn)程安全,需要有技巧的使用才行的,比如:
這里改成name。
update T set name =200 where max_id=100;
變種為:
1、 a = select name from T where max_id=100;//無(wú)鎖
2、 update T set name=a.name where max_id=100;//排他鎖
大家拿到相同的a,第二條語(yǔ)句的max_id不變,就會(huì)所有線(xiàn)程都能成功執(zhí)行update語(yǔ)句?。?!