【04期】分庫(kù)分表之后,id 主鍵如何處理?
閱讀本文大概需要 6 分鐘。
來(lái)自:java面試題精選
面試官心理分析
面試題剖析
基于數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方案
數(shù)據(jù)庫(kù)自增 id
設(shè)置數(shù)據(jù)庫(kù) sequence 或者表自增字段步長(zhǎng)

UUID
UUID.randomUUID().toString().replace(“-”,?“”)?->?sfsdf23423rr234sfdaf
獲取系統(tǒng)當(dāng)前時(shí)間
snowflake 算法
1 bit:不用,為啥呢?因?yàn)槎M(jìn)制里第一個(gè) bit 為如果是 1,那么都是負(fù)數(shù),但是我們生成的 id 都是正數(shù),所以第一個(gè) bit 統(tǒng)一都是 0。
41 bit:表示的是時(shí)間戳,單位是毫秒。41 bit 可以表示的數(shù)字多達(dá)?
2^41 - 1,也就是可以標(biāo)識(shí)?2^41 - 1?個(gè)毫秒值,換算成年就是表示69年的時(shí)間。10 bit:記錄工作機(jī)器 id,代表的是這個(gè)服務(wù)最多可以部署在 2^10臺(tái)機(jī)器上哪,也就是1024臺(tái)機(jī)器。但是 10 bit 里 5 個(gè) bit 代表機(jī)房 id,5 個(gè) bit 代表機(jī)器 id。意思就是最多代表?
2^5個(gè)機(jī)房(32個(gè)機(jī)房),每個(gè)機(jī)房里可以代表?2^5?個(gè)機(jī)器(32臺(tái)機(jī)器)。12 bit:這個(gè)是用來(lái)記錄同一個(gè)毫秒內(nèi)產(chǎn)生的不同 id,12 bit 可以代表的最大正整數(shù)是?
2^12 - 1 = 4096,也就是說(shuō)可以用這個(gè) 12 bit 代表的數(shù)字來(lái)區(qū)分同一個(gè)毫秒內(nèi)的 4096 個(gè)不同的 id。
0?|?0001100?10100010?10111110?10001001?01011100?00?|?10001?|?1?1001?|?0000?00000000
public?class?IdWorker?{
????private?long?workerId;
????private?long?datacenterId;
????private?long?sequence;
????public?IdWorker(long?workerId,?long?datacenterId,?long?sequence)?{
????????//?sanity?check?for?workerId
????????//?這兒不就檢查了一下,要求就是你傳遞進(jìn)來(lái)的機(jī)房id和機(jī)器id不能超過(guò)32,不能小于0
????????if?(workerId?>?maxWorkerId?||?workerId?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????String.format("worker?Id?can't?be?greater?than?%d?or?less?than?0",?maxWorkerId));
????????}
????????if?(datacenterId?>?maxDatacenterId?||?datacenterId?0)?{
????????????throw?new?IllegalArgumentException(
????????????????????String.format("datacenter?Id?can't?be?greater?than?%d?or?less?than?0",?maxDatacenterId));
????????}
????????System.out.printf(
????????????????"worker?starting.?timestamp?left?shift?%d,?datacenter?id?bits?%d,?worker?id?bits?%d,?sequence?bits?%d,?workerid?%d",
????????????????timestampLeftShift,?datacenterIdBits,?workerIdBits,?sequenceBits,?workerId);
????????this.workerId?=?workerId;
????????this.datacenterId?=?datacenterId;
????????this.sequence?=?sequence;
????}
????private?long?twepoch?=?1288834974657L;
????private?long?workerIdBits?=?5L;
????private?long?datacenterIdBits?=?5L;
????//?這個(gè)是二進(jìn)制運(yùn)算,就是?5?bit最多只能有31個(gè)數(shù)字,也就是說(shuō)機(jī)器id最多只能是32以?xún)?nèi)
????private?long?maxWorkerId?=?-1L?^?(-1L?<
????//?這個(gè)是一個(gè)意思,就是?5?bit最多只能有31個(gè)數(shù)字,機(jī)房id最多只能是32以?xún)?nèi)
????private?long?maxDatacenterId?=?-1L?^?(-1L?<????private?long?sequenceBits?=?12L;
????private?long?workerIdShift?=?sequenceBits;
????private?long?datacenterIdShift?=?sequenceBits?+?workerIdBits;
????private?long?timestampLeftShift?=?sequenceBits?+?workerIdBits?+?datacenterIdBits;
????private?long?sequenceMask?=?-1L?^?(-1L?<
????private?long?lastTimestamp?=?-1L;
????public?long?getWorkerId()?{
????????return?workerId;
????}
????public?long?getDatacenterId()?{
????????return?datacenterId;
????}
????public?long?getTimestamp()?{
????????return?System.currentTimeMillis();
????}
????public?synchronized?long?nextId()?{
????????//?這兒就是獲取當(dāng)前時(shí)間戳,單位是毫秒
????????long?timestamp?=?timeGen();
????????if?(timestamp?????????????System.err.printf("clock?is?moving?backwards.??Rejecting?requests?until?%d.",?lastTimestamp);
????????????throw?new?RuntimeException(String.format(
????????????????????"Clock?moved?backwards.??Refusing?to?generate?id?for?%d?milliseconds",?lastTimestamp?-?timestamp));
????????}
????????if?(lastTimestamp?==?timestamp)?{
????????????//?這個(gè)意思是說(shuō)一個(gè)毫秒內(nèi)最多只能有4096個(gè)數(shù)字
????????????//?無(wú)論你傳遞多少進(jìn)來(lái),這個(gè)位運(yùn)算保證始終就是在4096這個(gè)范圍內(nèi),避免你自己傳遞個(gè)sequence超過(guò)了4096這個(gè)范圍
????????????sequence?=?(sequence?+?1)?&?sequenceMask;
????????????if?(sequence?==?0)?{
????????????????timestamp?=?tilNextMillis(lastTimestamp);
????????????}
????????}?else?{
????????????sequence?=?0;
????????}
????????//?這兒記錄一下最近一次生成id的時(shí)間戳,單位是毫秒
????????lastTimestamp?=?timestamp;
????????//?這兒就是將時(shí)間戳左移,放到 41 bit那兒;
????????//?將機(jī)房 id左移放到 5 bit那兒;
????????//?將機(jī)器id左移放到5 bit那兒;將序號(hào)放最后12 bit;
????????//?最后拼接起來(lái)成一個(gè)?64?bit的二進(jìn)制數(shù)字,轉(zhuǎn)換成?10?進(jìn)制就是個(gè)?long?型
????????return?((timestamp?-?twepoch)?<????????????????|?(workerId?<????}
????private?long?tilNextMillis(long?lastTimestamp)?{
????????long?timestamp?=?timeGen();
????????while?(timestamp?<=?lastTimestamp)?{
????????????timestamp?=?timeGen();
????????}
????????return?timestamp;
????}
????private?long?timeGen()?{
????????return?System.currentTimeMillis();
????}
????//?---------------測(cè)試---------------
????public?static?void?main(String[]?args)?{
????????IdWorker?worker?=?new?IdWorker(1,?1,?1);
????????for?(int?i?=?0;?i?30;?i++)?{
????????????System.out.println(worker.nextId());
????????}
????}
}
推薦閱讀:
【01期】Spring,SpringMVC,SpringBoot,SpringCloud有什么區(qū)別和聯(lián)系?
【02期】你能說(shuō)說(shuō)Spring框架中Bean的生命周期嗎?
【03期】如何決定使用 HashMap 還是 TreeMap?
微信掃描二維碼,關(guān)注我的公眾號(hào)
朕已閱?

