基于Redis實現(xiàn)分布式單號及分布式ID(自定義規(guī)則生成)
背景
一些業(yè)務(wù)背景下,業(yè)務(wù)要求單號需要有區(qū)分不同的前綴,那么在分布式的架構(gòu)下如何自定義單號而且還能保證唯一呢?
注:分布式ID也可以此方式
Redis實現(xiàn)方式
Redis的所有命令操作都是單線程的,本身提供像 incr 和 increby 這樣的自增原子命令,所以能保證生成的 ID 肯定是唯一有序的。
優(yōu)點:不依賴于數(shù)據(jù)庫,靈活方便,且性能優(yōu)于數(shù)據(jù)庫;數(shù)字ID天然排序,對分頁或者需要排序的結(jié)果很有幫助。
缺點:如果系統(tǒng)中沒有Redis,還需要引入新的組件,增加系統(tǒng)復(fù)雜度;需要編碼和配置的工作量比較大。
考慮到單節(jié)點的性能瓶頸,可以使用 Redis 集群來獲取更高的吞吐量。
使用 Redis 集群也可以方式單點故障的問題。
代碼實例
創(chuàng)建常量類
/**
* 單號生成常量
*
* @author mq
*/
public class FormNoConstants {
/**
* 單號流水號緩存Key前綴
*/
public static final String SERIAL_CACHE_PREFIX = "FORM_NO_CACHE_";
/**
* 單號流水號yyMMdd前綴
*/
public static final String SERIAL_YYMMDD_PREFIX = "yyMMdd";
/**
* 單號流水號yyyyMMdd前綴
*/
public static final String SERIAL_YYYYMMDD_PREFIX = "yyyyMMdd";
/**
* 默認緩存天數(shù)
*/
public static final int DEFAULT_CACHE_DAYS = 7;
}
單號生成枚舉
注:為了方便擴展,方便復(fù)用,使用枚舉方式,可以自定義枚舉值來生成不同的單號
/**
* 單號生成類型枚舉
*
* @author mq
* 注:隨機號位于流水號之后,流水號使用redis計數(shù)據(jù),每天都是一個新的key,長度不足時則自動補0
* <p>
* 生成規(guī)則 =固定前綴+當(dāng)天日期串+流水號(redis自增,不足長度則補0)+隨機數(shù)
*/
public enum FormNoTypeEnum {
/**
* 應(yīng)付單單號:
* 固定前綴:YF
* 時間格式:yyyyMMdd
* 流水號長度:7(當(dāng)單日單據(jù)較多時可根據(jù)業(yè)務(wù)適當(dāng)增加流水號長度)
* 隨機數(shù)長度:3
* 總長度:20
*/
YF_ORDER("YF", FormNoConstants.SERIAL_YYYYMMDD_PREFIX, 7, 3, 20),
/**
* 付款單單號:
* 固定前綴:FK
* 時間格式:yyyyMMdd
* 流水號長度:7
* 隨機數(shù)長度:3
* 總長度:20
*/
FK_ORDER("FK", FormNoConstants.SERIAL_YYYYMMDD_PREFIX, 7, 3, 20),
/**
* 測試單單號:
* 固定前綴:""
* 時間格式:yyyyMMdd
* 流水號長度:10
* 隨機數(shù)長度:0
* 總長度:20
*/
TEST_ORDER("te", FormNoConstants.SERIAL_YYYYMMDD_PREFIX, 10, 0, 20),
;
/**
* 單號前綴
* 為空時填""
*/
private String prefix;
/**
* 時間格式表達式
* 例如:yyyyMMdd
*/
private String datePattern;
/**
* 流水號長度
*/
private Integer serialLength;
/**
* 隨機數(shù)長度
*/
private Integer randomLength;
/**
* 總長度
*/
private Integer totalLength;
FormNoTypeEnum(String prefix, String datePattern, Integer serialLength, Integer randomLength, Integer totalLength) {
this.prefix = prefix;
this.datePattern = datePattern;
this.serialLength = serialLength;
this.randomLength = randomLength;
this.totalLength = totalLength;
}
//省略 get 方法
}
單號生成工具類
/**
* 單號生成工具類
*
* @author mq
*/
public class FormNoSerialUtil {
/**
* 生成單號前綴
*/
public static String getFormNoPrefix(FormNoTypeEnum formNoTypeEnum) {
//格式化時間
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formNoTypeEnum.getDatePattern());
StringBuffer sb = new StringBuffer();
sb.append(formNoTypeEnum.getPrefix());
sb.append(formatter.format(LocalDateTime.now()));
return sb.toString();
}
/**
* 構(gòu)建流水號緩存Key
*
* @param serialPrefix 流水號前綴
* @return 流水號緩存Key
*/
public static String getCacheKey(String serialPrefix) {
return FormNoConstants.SERIAL_CACHE_PREFIX.concat(serialPrefix);
}
/**
* 補全流水號
*
* @param serialPrefix 單號前綴
* @param incrementalSerial 當(dāng)天自增流水號
* @author mengqiang
* @date 2019/1/1
*/
public static String completionSerial(String serialPrefix, Long incrementalSerial,
FormNoTypeEnum formNoTypeEnum) {
StringBuffer sb = new StringBuffer(serialPrefix);
//需要補0的長度=流水號長度 -當(dāng)日自增計數(shù)長度
int length = formNoTypeEnum.getSerialLength() - String.valueOf(incrementalSerial).length();
//補零
for (int i = 0; i < length; i++) {
sb.append("0");
}
//redis當(dāng)日自增數(shù)
sb.append(incrementalSerial);
return sb.toString();
}
/**
* 補全隨機數(shù)
*
* @param serialWithPrefix 當(dāng)前單號
* @param formNoTypeEnum 單號生成枚舉
* @author mengqiang
* @date 2019/1/1
*/
public static String completionRandom(String serialWithPrefix, FormNoTypeEnum formNoTypeEnum) {
StringBuffer sb = new StringBuffer(serialWithPrefix);
//隨機數(shù)長度
int length = formNoTypeEnum.getRandomLength();
if (length > 0) {
Random random = new Random();
for (int i = 0; i < length; i++) {
//十以內(nèi)隨機數(shù)補全
sb.append(random.nextInt(10));
}
}
return sb.toString();
}
}
單號生成接口
/**
* 單號生成接口
*
* @author mq
*/
public interface FormNoGenerateService {
/**
* 根據(jù)單據(jù)編號類型 生成單據(jù)編號
*
* @param formNoTypeEnum 單據(jù)編號類型
* @author mengqiang
* @date 2019/1/1
*/
String generateFormNo(FormNoTypeEnum formNoTypeEnum);
}
單號生成接口實現(xiàn)
/**
* 單號生成接口實現(xiàn)
*
* @author mengqiang
* @version FormNoGenerateServiceImpl.java, v 1.0 2019-01-01 18:10
*/
@Service
public class FormNoGenerateServiceImpl implements FormNoGenerateService {
/**
* redis 服務(wù)
* demo 項目沒有加redis相關(guān),若有需要請參考,redis的博客
*/
@Autowired
private RedisCache redisCache;
/**
* 根據(jù)單據(jù)編號類型 生成單據(jù)編號
*
* @param formNoTypeEnum 單據(jù)編號類型
* @author mengqiang
* @date 2019/1/1
*/
@Override
public String generateFormNo(FormNoTypeEnum formNoTypeEnum) {
//獲得單號前綴
//格式 固定前綴 +時間前綴 示例 :YF20190101
String formNoPrefix = FormNoSerialUtil.getFormNoPrefix(formNoTypeEnum);
//獲得緩存key
String cacheKey = FormNoSerialUtil.getCacheKey(formNoPrefix);
//獲得當(dāng)日自增數(shù)
Long incrementalSerial = redisCache.incr(cacheKey);
//設(shè)置失效時間 7天
redisCache.expire(cacheKey, FormNoConstants.DEFAULT_CACHE_DAYS, TimeUnit.DAYS);
//組合單號并補全流水號
String serialWithPrefix = FormNoSerialUtil
.completionSerial(formNoPrefix, incrementalSerial, formNoTypeEnum);
//補全隨機數(shù)
return FormNoSerialUtil.completionRandom(serialWithPrefix, formNoTypeEnum);
}
}
使用測試

redis截圖

總結(jié)
以上還不是最優(yōu)雅的方式,最好是能做成jar包方式,做成通用的服務(wù)
到此這篇關(guān)于基于Redis實現(xiàn)分布式單號及分布式ID(自定義規(guī)則生成)的文章就介紹到這了,更多相關(guān)基于Redis實現(xiàn)分布式單號及分布式ID(自定義規(guī)則生成)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis過期Key刪除策略和內(nèi)存淘汰策略的實現(xiàn)
當(dāng)內(nèi)存使用達到上限,就無法存儲更多數(shù)據(jù)了,為了解決這個問題,Redis內(nèi)部會有兩套內(nèi)存回收的策略,過期Key刪除策略和內(nèi)存淘汰策略,本文就來詳細的介紹一下這兩種方法,感興趣的可以了解一下2024-02-02
深度剖析Redis字符串操作指南從入門到實戰(zhàn)應(yīng)用
Redis字符串類型二進制安全,支持文本、數(shù)字、二進制等數(shù)據(jù),涵蓋基礎(chǔ)操作、數(shù)字計算、過期管理及分布式鎖等應(yīng)用,結(jié)合優(yōu)化策略提升系統(tǒng)性能,本文給大家介紹Redis字符串操作指南,感興趣的朋友一起看看吧2025-07-07
使用redis實現(xiàn)延遲通知功能(Redis過期鍵通知)
這篇文章主要介紹了使用redis實現(xiàn)延遲通知功能(Redis過期鍵通知)的相關(guān)知識,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-09-09
redis集群搭建_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了redis集群搭建,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
淺析PHP分布式中Redis實現(xiàn)Session的方法
這篇文章主要介紹了PHP分布式中Redis實現(xiàn)Session的方法,文中詳細介紹了兩種方法的使用方法,并給出了測試的示例代碼,有需要的朋友可以參考借鑒,下面來一起看看吧,2016-12-12
使用SpringBoot?+?Redis?實現(xiàn)接口限流的方式
這篇文章主要介紹了SpringBoot?+?Redis?實現(xiàn)接口限流,Redis?除了做緩存,還能干很多很多事情:分布式鎖、限流、處理請求接口冪等,文中給大家提到了限流注解的創(chuàng)建方式,需要的朋友可以參考下2022-05-05
Redis實現(xiàn)排行榜及相同積分按時間排序功能的實現(xiàn)
這篇文章主要介紹了Redis實現(xiàn)排行榜及相同積分按時間排序,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-08-08

