1. 分庫(kù)分表的 9種分布式主鍵ID 生成方案,挺全乎的

        共 6289字,需瀏覽 13分鐘

         ·

        2021-12-23 03:54

        點(diǎn)擊“?程序員內(nèi)點(diǎn)事?”關(guān)注,選擇“?設(shè)置星標(biāo)?”

        堅(jiān)持學(xué)習(xí),好文每日送達(dá)!


        《sharding-jdbc 分庫(kù)分表的 4種分片策略》 中我們介紹了 sharding-jdbc 4種分片策略的使用場(chǎng)景,可以滿足基礎(chǔ)的分片功能開(kāi)發(fā),這篇我們來(lái)看看分庫(kù)分表后,應(yīng)該如何為分片表生成全局唯一的主鍵 ID。

        引入任何一種技術(shù)都是存在風(fēng)險(xiǎn)的,分庫(kù)分表當(dāng)然也不例外,除非庫(kù)、表數(shù)據(jù)量持續(xù)增加,大到一定程度,以至于現(xiàn)有高可用架構(gòu)已無(wú)法支撐,否則不建議大家做分庫(kù)分表,因?yàn)樽隽藬?shù)據(jù)分片后,你會(huì)發(fā)現(xiàn)自己踏上了一段踩坑之路,而分布式主鍵 ID 就是遇到的第一個(gè)坑。

        不同數(shù)據(jù)節(jié)點(diǎn)間生成全局唯一主鍵是個(gè)棘手的問(wèn)題,一張邏輯表 t_order 拆分成多個(gè)真實(shí)表 t_order_n,然后被分散到不同分片庫(kù) db_0、db_1... ,各真實(shí)表的自增鍵由于無(wú)法互相感知從而會(huì)產(chǎn)生重復(fù)主鍵,此時(shí)數(shù)據(jù)庫(kù)本身的自增主鍵,就無(wú)法滿足分庫(kù)分表對(duì)主鍵全局唯一的要求。

        ?db_0--
        ????|--?t_order_0
        ????|--?t_order_1
        ????|--?t_order_2
        ?db_1--
        ????|--?t_order_0
        ????|--?t_order_1
        ????|--?t_order_2

        盡管我們可以通過(guò)嚴(yán)格約束,各個(gè)分片表自增主鍵的 初始值步長(zhǎng) 的方式來(lái)解決 ID 重復(fù)的問(wèn)題,但這樣會(huì)讓運(yùn)維成本陡增,而且可擴(kuò)展性極差,一旦要擴(kuò)容分片表數(shù)量,原表數(shù)據(jù)變動(dòng)比較大,所以這種方式不太可取。

        ?步長(zhǎng)?step?=?分表張數(shù)

        ?db_0--
        ????|--?t_order_0??ID:?0、612、18...
        ????|--?t_order_1??ID:?17、1319...
        ????|--?t_order_2??ID:?2、8、14、20...
        ?db_1--
        ????|--?t_order_0??ID:?3、9、1521...
        ????|--?t_order_1??ID:?410、16、22...
        ????|--?t_order_2??ID:?5、11、17、23...

        目前已經(jīng)有了許多第三放解決方案可以完美解決這個(gè)問(wèn)題,比如基于 UUID、SNOWFLAKE算法 、segment號(hào)段,使用特定算法生成不重復(fù)鍵,或者直接引用主鍵生成服務(wù),像美團(tuán)(Leaf)和 滴滴(TinyId)等。

        sharding-jdbc 內(nèi)置了兩種分布式主鍵生成方案,UUID、SNOWFLAKE,不僅如此它還抽離出分布式主鍵生成器的接口,以便于開(kāi)發(fā)者實(shí)現(xiàn)自定義的主鍵生成器,后續(xù)我們會(huì)在自定義的生成器中接入 滴滴(TinyId)的主鍵生成服務(wù)。

        前邊介紹過(guò)在 sharding-jdbc 中要想為某個(gè)字段自動(dòng)生成主鍵 ID,只需要在 application.properties 文件中做如下配置:

        #?主鍵字段
        spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
        #?主鍵ID?生成方案
        spring.shardingsphere.sharding.tables.t_order.key-generator.type=UUID
        #?工作機(jī)器?id
        spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=123

        key-generator.column 表示主鍵字段,key-generator.type 為主鍵 ID 生成方案(內(nèi)置或自定義的),key-generator.props.worker.id 為機(jī)器ID,在主鍵生成方案設(shè)為 SNOWFLAKE 時(shí)機(jī)器ID 會(huì)參與位運(yùn)算。

        在使用 sharding-jdbc 分布式主鍵時(shí)需要注意兩點(diǎn):

        • 一旦 insert 插入操作的實(shí)體對(duì)象中主鍵字段已經(jīng)賦值,那么即使配置了主鍵生成方案也會(huì)失效,最后SQL 執(zhí)行的數(shù)據(jù)會(huì)以賦的值為準(zhǔn)。
        • 不要給主鍵字段設(shè)置自增屬性,否則主鍵ID 會(huì)以默認(rèn)的 SNOWFLAKE 方式生成。比如:用 mybatis plus@TableId 注解給字段 order_id 設(shè)置了自增主鍵,那么此時(shí)配置哪種方案,總是按雪花算法生成。

        下面我們從源碼上分析下 sharding-jdbc 內(nèi)置主鍵生成方案 UUID、SNOWFLAKE 是怎么實(shí)現(xiàn)的。

        UUID

        打開(kāi) UUID 類型的主鍵生成實(shí)現(xiàn)類 UUIDShardingKeyGenerator 的源碼發(fā)現(xiàn),它的生成規(guī)則只有 UUID.randomUUID() 這么一行代碼,額~ 心中默默來(lái)了一句臥槽

        UUID 雖然可以做到全局唯一性,但還是不推薦使用它作為主鍵,因?yàn)槲覀兊膶?shí)際業(yè)務(wù)中不管是 user_id 還是 order_id 主鍵多為整型,而 UUID 生成的是個(gè) 32 位的字符串。

        它的存儲(chǔ)以及查詢對(duì) MySQL 的性能消耗較大,而且 MySQL 官方也明確建議,主鍵要盡量越短越好,作為數(shù)據(jù)庫(kù)主鍵 UUID 的無(wú)序性還會(huì)導(dǎo)致數(shù)據(jù)位置頻繁變動(dòng),嚴(yán)重影響性能。

        public?final?class?UUIDShardingKeyGenerator?implements?ShardingKeyGenerator?{
        ????private?Properties?properties?=?new?Properties();

        ????public?UUIDShardingKeyGenerator()?{
        ????}

        ????public?String?getType()?{
        ????????return?"UUID";
        ????}

        ????public?synchronized?Comparable?generateKey()?{
        ????????return?UUID.randomUUID().toString().replaceAll("-",?"");
        ????}

        ????public?Properties?getProperties()?{
        ????????return?this.properties;
        ????}

        ????public?void?setProperties(Properties?properties)?{
        ????????this.properties?=?properties;
        ????}
        }

        SNOWFLAKE

        SNOWFLAKE(雪花算法)是默認(rèn)使用的主鍵生成方案,生成一個(gè) 64bit的長(zhǎng)整型(Long)數(shù)據(jù)。

        sharding-jdbc 中雪花算法生成的主鍵主要由 4部分組成,1bit符號(hào)位、41bit時(shí)間戳位、10bit工作進(jìn)程位以及 12bit 序列號(hào)位。

        符號(hào)位(1bit位)

        Java 中 Long 型的最高位是符號(hào)位,正數(shù)是0,負(fù)數(shù)是1,一般生成ID都為正數(shù),所以默認(rèn)為0

        時(shí)間戳位(41bit)

        41位的時(shí)間戳可以容納的毫秒數(shù)是 2 的 41次冪,而一年的總毫秒數(shù)為 1000L * 60 * 60 * 24 * 365,計(jì)算使用時(shí)間大概是69年,額~,我有生之間算是夠用了。

        Math.pow(2,?41)?/?(365?*?24?*?60?*?60?*?1000L)?=?=?69年?

        工作進(jìn)程位(10bit)

        表示一個(gè)唯一的工作進(jìn)程id,默認(rèn)值為 0,可通過(guò) key-generator.props.worker.id 屬性設(shè)置。

        spring.shardingsphere.sharding.tables.t_order.key-generator.props.worker.id=0000

        序列號(hào)位(12bit)

        同一毫秒內(nèi)生成不同的ID。

        時(shí)鐘回?fù)?/h3>

        了解了雪花算法的主鍵 ID 組成后不難發(fā)現(xiàn),這是一種嚴(yán)重依賴于服務(wù)器時(shí)間的算法,而依賴服務(wù)器時(shí)間的就會(huì)遇到一個(gè)棘手的問(wèn)題:時(shí)鐘回?fù)?/code>。

        為什么會(huì)出現(xiàn)時(shí)鐘回?fù)苣兀?/span>

        互聯(lián)網(wǎng)中有一種網(wǎng)絡(luò)時(shí)間協(xié)議 ntp 全稱 (Network Time Protocol) ,專門用來(lái)同步、校準(zhǔn)網(wǎng)絡(luò)中各個(gè)計(jì)算機(jī)的時(shí)間。

        這就是為什么,我們的手機(jī)現(xiàn)在不用手動(dòng)校對(duì)時(shí)間,可每個(gè)人的手機(jī)時(shí)間還都是一樣的。

        我們的硬件時(shí)鐘可能會(huì)因?yàn)楦鞣N原因變得不準(zhǔn)( 快了慢了 ),此時(shí)就需要 ntp 服務(wù)來(lái)做時(shí)間校準(zhǔn),做校準(zhǔn)的時(shí)候就會(huì)發(fā)生服務(wù)器時(shí)鐘的 跳躍 或者 回?fù)?/code> 的問(wèn)題。

        雪花算法如何解決時(shí)鐘回?fù)?/span>

        服務(wù)器時(shí)鐘回?fù)軙?huì)導(dǎo)致產(chǎn)生重復(fù)的 ID,SNOWFLAKE 方案中對(duì)原有雪花算法做了改進(jìn),增加了一個(gè)最大容忍的時(shí)鐘回?fù)芎撩霐?shù)。

        如果時(shí)鐘回?fù)艿臅r(shí)間超過(guò)最大容忍的毫秒數(shù)閾值,則程序直接報(bào)錯(cuò);如果在可容忍的范圍內(nèi),默認(rèn)分布式主鍵生成器,會(huì)等待時(shí)鐘同步到最后一次主鍵生成的時(shí)間后再繼續(xù)工作。

        最大容忍的時(shí)鐘回?fù)芎撩霐?shù),默認(rèn)值為 0,可通過(guò)屬性 max.tolerate.time.difference.milliseconds 設(shè)置。

        #?最大容忍的時(shí)鐘回?fù)芎撩霐?shù)
        spring.shardingsphere.sharding.tables.t_order.key-generator.max.tolerate.time.difference.milliseconds=5

        下面是看下它的源碼實(shí)現(xiàn)類 SnowflakeShardingKeyGenerator,核心流程大概如下:

        最后一次生成主鍵的時(shí)間 lastMilliseconds 與 當(dāng)前時(shí)間currentMilliseconds 做比較,如果 lastMilliseconds > currentMilliseconds則意味著時(shí)鐘回調(diào)了。

        那么接著判斷兩個(gè)時(shí)間的差值(timeDifferenceMilliseconds)是否在設(shè)置的最大容忍時(shí)間閾值 max.tolerate.time.difference.milliseconds內(nèi),在閾值內(nèi)則線程休眠差值時(shí)間 Thread.sleep(timeDifferenceMilliseconds),否則大于差值直接報(bào)異常。

        ?
        /**
        ?*?@author?xiaofu
        ?*/

        public?final?class?SnowflakeShardingKeyGenerator?implements?ShardingKeyGenerator{
        ????@Getter
        ????@Setter
        ????private?Properties?properties?=?new?Properties();
        ????
        ????public?String?getType()?{
        ????????return?"SNOWFLAKE";
        ????}
        ????
        ????public?synchronized?Comparable?generateKey()?{
        ?????/**
        ??????*?當(dāng)前系統(tǒng)時(shí)間毫秒數(shù)?
        ??????*/
        ?
        ????????long?currentMilliseconds?=?timeService.getCurrentMillis();
        ????????/**
        ?????????*?判斷是否需要等待容忍時(shí)間差,如果需要,則等待時(shí)間差過(guò)去,然后再獲取當(dāng)前系統(tǒng)時(shí)間?
        ?????????*/
        ?
        ????????if?(waitTolerateTimeDifferenceIfNeed(currentMilliseconds))?{
        ????????????currentMilliseconds?=?timeService.getCurrentMillis();
        ????????}
        ????????/**
        ?????????*?如果最后一次毫秒與?當(dāng)前系統(tǒng)時(shí)間毫秒相同,即還在同一毫秒內(nèi)?
        ?????????*/

        ????????if?(lastMilliseconds?==?currentMilliseconds)?{
        ?????????/**
        ??????????*?&位與運(yùn)算符:兩個(gè)數(shù)都轉(zhuǎn)為二進(jìn)制,如果相對(duì)應(yīng)位都是1,則結(jié)果為1,否則為0
        ??????????*?當(dāng)序列為4095時(shí),4095+1后的新序列與掩碼進(jìn)行位與運(yùn)算結(jié)果是0
        ??????????*?當(dāng)序列為其他值時(shí),位與運(yùn)算結(jié)果都不會(huì)是0
        ??????????*?即本毫秒的序列已經(jīng)用到最大值4096,此時(shí)要取下一個(gè)毫秒時(shí)間值
        ??????????*/

        ????????????if?(0L?==?(sequence?=?(sequence?+?1)?&?SEQUENCE_MASK))?{
        ????????????????currentMilliseconds?=?waitUntilNextTime(currentMilliseconds);
        ????????????}
        ????????}?else?{
        ?????????/**
        ??????????*?上一毫秒已經(jīng)過(guò)去,把序列值重置為1?
        ??????????*/

        ????????????vibrateSequenceOffset();
        ????????????sequence?=?sequenceOffset;
        ????????}
        ????????lastMilliseconds?=?currentMilliseconds;
        ????????
        ????????/**
        ?????????*?XX......XX?XX000000?00000000?00000000?時(shí)間差?XX
        ?????????*????XXXXXX?XXXX0000?00000000?機(jī)器ID?XX
        ?????????*???????????????XXXX?XXXXXXXX?序列號(hào)?XX
        ?????????*??三部分進(jìn)行|位或運(yùn)算:如果相對(duì)應(yīng)位都是0,則結(jié)果為0,否則為1
        ?????????*/

        ????????return?((currentMilliseconds?-?EPOCH)?<????}
        ????
        ????/**
        ?????*?判斷是否需要等待容忍時(shí)間差
        ?????*/

        ????@SneakyThrows
        ????private?boolean?waitTolerateTimeDifferenceIfNeed(final?long?currentMilliseconds)?{
        ?????/**
        ??????*?如果獲取ID時(shí)的最后一次時(shí)間毫秒數(shù)小于等于當(dāng)前系統(tǒng)時(shí)間毫秒數(shù),屬于正常情況,則不需要等待?
        ??????*/

        ????????if?(lastMilliseconds?<=?currentMilliseconds)?{
        ????????????return?false;
        ????????}
        ????????/**
        ?????????*?===>時(shí)鐘回?fù)艿那闆r(生成序列的時(shí)間大于當(dāng)前系統(tǒng)的時(shí)間),需要等待時(shí)間差?
        ?????????*/

        ????????/**
        ?????????*?獲取ID時(shí)的最后一次毫秒數(shù)減去當(dāng)前系統(tǒng)時(shí)間毫秒數(shù)的時(shí)間差?
        ?????????*/

        ????????long?timeDifferenceMilliseconds?=?lastMilliseconds?-?currentMilliseconds;
        ????????/**
        ?????????*?時(shí)間差小于最大容忍時(shí)間差,即當(dāng)前還在時(shí)鐘回?fù)艿臅r(shí)間差之內(nèi)?
        ?????????*/

        ????????Preconditions.checkState(timeDifferenceMilliseconds?????????????????"Clock?is?moving?backwards,?last?time?is?%d?milliseconds,?current?time?is?%d?milliseconds",?lastMilliseconds,?currentMilliseconds);
        ????????/**
        ?????????*?線程休眠時(shí)間差?
        ?????????*/

        ????????Thread.sleep(timeDifferenceMilliseconds);
        ????????return?true;
        ????}
        ????
        ????//?配置的機(jī)器ID
        ????private?long?getWorkerId()?{
        ????????long?result?=?Long.valueOf(properties.getProperty("worker.id",?String.valueOf(WORKER_ID)));
        ????????Preconditions.checkArgument(result?>=?0L?&&?result?????????return?result;
        ????}
        ????
        ????private?int?getMaxTolerateTimeDifferenceMilliseconds()?{
        ????????return?Integer.valueOf(properties.getProperty("max.tolerate.time.difference.milliseconds",?String.valueOf(MAX_TOLERATE_TIME_DIFFERENCE_MILLISECONDS)));
        ????}
        ????
        ????private?long?waitUntilNextTime(final?long?lastTime)?{
        ????????long?result?=?timeService.getCurrentMillis();
        ????????while?(result?<=?lastTime)?{
        ????????????result?=?timeService.getCurrentMillis();
        ????????}
        ????????return?result;
        ????}
        }

        但從 SNOWFLAKE 方案生成的主鍵ID 來(lái)看,order_id 它是一個(gè)18位的長(zhǎng)整型數(shù)字,是不是發(fā)現(xiàn)它太長(zhǎng)了,想要 MySQL 那種從 0 遞增的自增主鍵該怎么實(shí)現(xiàn)呢?別急,后邊已經(jīng)會(huì)給出了解決辦法!

        自定義

        sharding-jdbc 利用 SPI 全稱( Service Provider Interface) 機(jī)制拓展主鍵生成規(guī)則,這是一種服務(wù)發(fā)現(xiàn)機(jī)制,通過(guò)掃描項(xiàng)目路徑 META-INF/services 下的文件,并自動(dòng)加載文件里所定義的類。

        實(shí)現(xiàn)自定義主鍵生成器其實(shí)比較簡(jiǎn)單,只有兩步。

        第一步,實(shí)現(xiàn) ShardingKeyGenerator 接口,并重寫(xiě)其內(nèi)部方法,其中 getType() 方法為自定義的主鍵生產(chǎn)方案類型、generateKey() 方法則是具體生成主鍵的規(guī)則。

        下面代碼中用 AtomicInteger 來(lái)模擬實(shí)現(xiàn)一個(gè)有序自增的 ID 生成。

        /**
        ?*?@Author:?xiaofu
        ?*?@Description:?自定義主鍵生成器
        ?*/

        @Component
        public?class?MyShardingKeyGenerator?implements?ShardingKeyGenerator?{


        ????private?final?AtomicInteger?count?=?new?AtomicInteger();

        ????/**
        ?????*?自定義的生成方案類型
        ?????*/

        ????@Override
        ????public?String?getType()?{
        ????????return?"XXX";
        ????}

        ????/**
        ?????*?核心方法-生成主鍵ID
        ?????*/

        ????@Override
        ????public?Comparable?generateKey()?{
        ????????return?count.incrementAndGet();
        ????}

        ????@Override
        ????public?Properties?getProperties()?{
        ????????return?null;
        ????}

        ????@Override
        ????public?void?setProperties(Properties?properties)?{

        ????}
        }

        第二步,由于是利用 SPI 機(jī)制實(shí)現(xiàn)功能拓展,我們要在 META-INF/services 文件中配置自定義的主鍵生成器類路勁。

        com.xiaofu.sharding.key.MyShardingKeyGenerator

        上面這些弄完我們測(cè)試一下,配置定義好的主鍵生成類型 XXX,并插入幾條數(shù)據(jù)看看效果。

        spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
        spring.shardingsphere.sharding.tables.t_order.key-generator.type=XXX

        通過(guò)控制臺(tái)的SQL 解析日志發(fā)現(xiàn),order_id 字段已按照有序自增的方式插入記錄,說(shuō)明配置的沒(méi)問(wèn)題。

        舉一反九

        既然可以自定義生成方案,那么實(shí)現(xiàn)分布式主鍵的思路就很多了,又想到之前我寫(xiě)的這篇 《9種 分布式ID生成方案》,發(fā)現(xiàn)可以完美兼容,這里挑選其中的 滴滴(Tinyid)來(lái)實(shí)踐一下,由于它是個(gè)單獨(dú)的分布式ID生成服務(wù),所以要先搭建環(huán)境了。

        Tinyid 的服務(wù)提供HttpTinyid-client 兩種接入方式,下邊使用 Tinyid-client 方式快速使用,更多的細(xì)節(jié)到這篇文章里看吧,實(shí)在是介紹過(guò)太多次了。

        Tinyid 服務(wù)搭建

        先拉源代碼 https://github.com/didi/tinyid.git。

        由于是基于號(hào)段模式實(shí)現(xiàn)的分布式ID,所以依賴于數(shù)據(jù)庫(kù),要?jiǎng)?chuàng)建相應(yīng)的表 tiny_id_info 、tiny_id_token 并插入默認(rèn)數(shù)據(jù)。


        CREATE?TABLE?`tiny_id_info`?(
        ?`id`?BIGINT?(20)?UNSIGNED?NOT?NULL?AUTO_INCREMENT?COMMENT?'自增主鍵',
        ?`biz_type`?VARCHAR?(63)?NOT?NULL?DEFAULT?''?COMMENT?'業(yè)務(wù)類型,唯一',
        ?`begin_id`?BIGINT?(20)?NOT?NULL?DEFAULT?'0'?COMMENT?'開(kāi)始id,僅記錄初始值,無(wú)其他含義。初始化時(shí)begin_id和max_id應(yīng)相同',
        ?`max_id`?BIGINT?(20)?NOT?NULL?DEFAULT?'0'?COMMENT?'當(dāng)前最大id',
        ?`step`?INT?(11)?DEFAULT?'0'?COMMENT?'步長(zhǎng)',
        ?`delta`?INT?(11)?NOT?NULL?DEFAULT?'1'?COMMENT?'每次id增量',
        ?`remainder`?INT?(11)?NOT?NULL?DEFAULT?'0'?COMMENT?'余數(shù)',
        ?`create_time`?TIMESTAMP?NOT?NULL?DEFAULT?'2010-01-01?00:00:00'?COMMENT?'創(chuàng)建時(shí)間',
        ?`update_time`?TIMESTAMP?NOT?NULL?DEFAULT?'2010-01-01?00:00:00'?COMMENT?'更新時(shí)間',
        ?`version`?BIGINT?(20)?NOT?NULL?DEFAULT?'0'?COMMENT?'版本號(hào)',
        ?PRIMARY?KEY?(`id`),
        ?UNIQUE?KEY?`uniq_biz_type`?(`biz_type`)
        )?ENGINE?=?INNODB?AUTO_INCREMENT?=?1?DEFAULT?CHARSET?=?utf8?COMMENT?'id信息表';

        CREATE?TABLE?`tiny_id_token`?(
        ?`id`?INT?(11)?UNSIGNED?NOT?NULL?AUTO_INCREMENT?COMMENT?'自增id',
        ?`token`?VARCHAR?(255)?NOT?NULL?DEFAULT?''?COMMENT?'token',
        ?`biz_type`?VARCHAR?(63)?NOT?NULL?DEFAULT?''?COMMENT?'此token可訪問(wèn)的業(yè)務(wù)類型標(biāo)識(shí)',
        ?`remark`?VARCHAR?(255)?NOT?NULL?DEFAULT?''?COMMENT?'備注',
        ?`create_time`?TIMESTAMP?NOT?NULL?DEFAULT?'2010-01-01?00:00:00'?COMMENT?'創(chuàng)建時(shí)間',
        ?`update_time`?TIMESTAMP?NOT?NULL?DEFAULT?'2010-01-01?00:00:00'?COMMENT?'更新時(shí)間',
        ?PRIMARY?KEY?(`id`)
        )?ENGINE?=?INNODB?AUTO_INCREMENT?=?1?DEFAULT?CHARSET?=?utf8?COMMENT?'token信息表';

        INSERT?INTO?`tiny_id_token`?(`id`,?`token`,?`biz_type`,?`remark`,?`create_time`,?`update_time`)?VALUES?('1',?'0f673adf80504e2eaa552f5d791b644c',?'order',?'1',?'2017-12-14?16:36:46',?'2017-12-14?16:36:48');

        INSERT?INTO?`tiny_id_info`?(`id`,?`biz_type`,?`begin_id`,?`max_id`,?`step`,?`delta`,?`remainder`,?`create_time`,?`update_time`,?`version`)?VALUES?('1',?'order',?'1',?'1',?'100000',?'1',?'0',?'2018-07-21?23:52:58',?'2018-07-22?23:19:27',?'1');

        并在 Tinyid 服務(wù)中配置上邊表所在數(shù)據(jù)源信息

        datasource.tinyid.primary.url=jdbc:mysql://47.93.6.e:3306/ds-0?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
        datasource.tinyid.primary.username=root
        datasource.tinyid.primary.password=root

        最后項(xiàng)目 maven install ,右鍵 TinyIdServerApplication 啟動(dòng)服務(wù), Tinyid 分布式ID生成服務(wù)就搭建完畢了。

        自定義 Tinyid 主鍵類型

        Tinyid 服務(wù)搭建完下邊在項(xiàng)目中引入它,新建個(gè) tinyid_client.properties 文件其中添加 tinyid.servertinyid.token 屬性,token 為之前 SQL 預(yù)先插入的用戶數(shù)據(jù)。

        #?tinyid?分布式ID
        #?服務(wù)地址
        tinyid.server=127.0.0.1:9999
        #?業(yè)務(wù)token
        tinyid.token=0f673adf80504e2eaa552f5d791b644c

        代碼中獲取 ID更簡(jiǎn)單,只需一行代碼,業(yè)務(wù)類型 order 是之前 SQ L 預(yù)先插入的數(shù)據(jù)。

        Long?id?=?TinyId.nextId("order");

        我們開(kāi)始自定義 Tinyid ?主鍵生成類型的實(shí)現(xiàn)類 TinyIdShardingKeyGenerator 。

        /**
        ?*?@Author:?xiaofu
        ?*?@Description:?自定義主鍵生成器
        ?*/

        @Component
        public?class?TinyIdShardingKeyGenerator?implements?ShardingKeyGenerator?{
        ????
        ????/**
        ?????*?自定義的生成方案類型
        ?????*/

        ????@Override
        ????public?String?getType()?{
        ????????return?"tinyid";
        ????}

        ????/**
        ?????*?核心方法-生成主鍵ID
        ?????*/

        ????@Override
        ????public?Comparable?generateKey()?{
        ????????
        ????????Long?id?=?TinyId.nextId("order");
        ????????
        ????????return?id;
        ????}

        ????@Override
        ????public?Properties?getProperties()?{
        ????????return?null;
        ????}

        ????@Override
        ????public?void?setProperties(Properties?properties)?{

        ????}
        }

        并在配置文件中啟用 Tinyid ?主鍵生成類型,到此配置完畢,趕緊測(cè)試一下。

        #?主鍵字段
        spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
        #?主鍵ID?生成方案
        spring.shardingsphere.sharding.tables.t_order.key-generator.type=tinyid

        測(cè)試 Tinyid 主鍵

        向數(shù)據(jù)庫(kù)插入訂單記錄測(cè)試發(fā)現(xiàn),主鍵ID字段 order_id 已經(jīng)為趨勢(shì)遞增的了, Tinyid ?服務(wù)成功接入,完美!

        瀏覽 56
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
            
            

                      • 午夜视频91 | 欧美国产日韩视频 | 免费看污的视频网站 | 简陋出租屋嫖妓露脸 | 99精品观看 |