序列號要素
唯一性
生成的序列號需要全局唯一跷睦。這個唯一也不是絕對的缚够,因為首先要理解全局的概念娶聘,全局可以理解為一種業(yè)務(wù)范圍煮剧,比如說訂單生成斥滤,也可以到數(shù)據(jù)庫表一級(因為業(yè)務(wù)建模最終會在表結(jié)構(gòu)進行體現(xiàn))。也要考慮到分表情況勉盅,一張訂單表可能分為多張訂單表并存在不同的分布式數(shù)據(jù)庫中佑颇,那序列號也必須保證唯一。
支持高可用草娜,高并發(fā)
出現(xiàn)不可用的情況后挑胸,必然影響業(yè)務(wù)進件,并發(fā)量支持不高的話宰闰,也無法適應高頻場景茬贵。
有序
由于序列號大多為數(shù)據(jù)庫表主鍵,也是查詢的關(guān)鍵字段移袍,保證有序解藻,可方便查詢。
業(yè)務(wù)意義
序列號生成后不能是一堆有序的亂糟糟的數(shù)字字母組合葡盗,它必須要體現(xiàn)出一定的業(yè)務(wù)或者時間意義舆逃。這樣可以一目了然找到這筆業(yè)務(wù)的實際業(yè)務(wù)場景或者發(fā)生時間,并且方便范圍查詢。但是要保證每段序列號分配路狮,比如說前幾位代表什么虫啥,后幾位是什么意義。
舉例說明:[業(yè)務(wù)相關(guān)字典項A]+[業(yè)務(wù)相關(guān)字典項B]+[業(yè)務(wù)相關(guān)字典項C]+...+[時間]+[分段遞增]奄妨,這樣可以根據(jù)不同業(yè)務(wù)選項進行l(wèi)ike查詢(這種%在右邊的like查詢是會走索引的)涂籽。
生成方法
網(wǎng)上有很多的生成方法,我這只是展示我們公司使用的方式砸抛,并指出優(yōu)缺點评雌。其實流水號前幾位已經(jīng)是固定的了,因為業(yè)務(wù)相關(guān)分段號和發(fā)生時間分段號在業(yè)務(wù)進件的時候便已知直焙,那么我們需要生成的便是最后后幾位景东。
UUID
最差的id生成方法,它只能滿足唯一性奔誓,對業(yè)務(wù)不友好斤吐,對數(shù)據(jù)庫不友好,我覺得它只能作為接口間傳遞的接口流水號厨喂,可以定位接口日志和措。我們也是這么用的。
數(shù)據(jù)庫
獨立數(shù)據(jù)庫蜕煌,就是說我們有一臺數(shù)據(jù)庫派阱,專門用于生成自增id,由于是單庫斜纪,所以可用性不能保證贫母,性能也有瓶頸。但是優(yōu)勢是結(jié)構(gòu)簡單盒刚,維護容易颁独,對于并發(fā)量不高的業(yè)務(wù)場景是可以使用的。
oracle可以使用sequence伪冰,mysql需要獨立建一張表誓酒,并寫一個函數(shù)用于獲取id,伸手拉一個實現(xiàn)方案如下:
建表語句
CREATE TABLE SEQUENCE_ID (
id bigint(20) unsigned NOT NULL auto_increment,
stub char(10) NOT NULL default '',
PRIMARY KEY (id),
UNIQUE KEY stub (stub)
);
存儲過程
begin;
replace into SEQUENCE_ID (stub) VALUES ('anyword');
select last_insert_id();
commit;
其實贮聂,滴滴有一個開源的基于數(shù)據(jù)庫和segment(分段)模式的id生成器tinyid靠柑。可以白嫖吓懈,可用性要比上面的方式高(因為應用會將一部分序列號預存到內(nèi)存中歼冰,即使數(shù)據(jù)庫宕機也可使用),具體使用方式可以去github上觀摩一下https://github.com/didi/tinyid耻警。
Redis
跟數(shù)據(jù)庫差不多的特點隔嫡,只不過比數(shù)據(jù)庫要快甸怕,核心是incr和incrby兩個命令。但要開啟持久化AOF腮恩,宕機后可以恢復梢杭。