全局唯一序列號生成器-支持分佈式
場景:在某些業務場景,需要生成唯一的序列號,來定位某一個條數據,且必須保證全局唯一,例如:交易流水號等。
核心類
/**
* 序列號生成服務
*/
@Service
public class SnGenerator {
private static final Logger logger = LoggerFactory.getLogger(SnGenerator.class);
private HashMap<String,SeqFragmentDefine> seqMap;
@Resource
TblXxxBatseqCtrlMapper tblXxxBatseqCtrlMapper;
SnGenerator()
{
seqMap=new HashMap();
}
public synchronized String getSeq(String seqName,int length)
{
//從內存中去取
SeqFragmentDefine seqDefine= seqMap.get(seqName);
if(seqDefine==null)
{
//如果爲空,存入新對象
seqMap.put(seqName,new SeqFragmentDefine());
getNewFragment(seqName) ;
}
else
{
//如果不爲空,將內存中seqDefine對象seqCurrent+1
seqDefine.setSeqCurrent(seqDefine.getSeqCurrent()+1);
//如果當前值大於最大值,重新生成seqDefine對象
if(seqDefine.getSeqCurrent()>seqDefine.getSeqFragmentMax())
{
getNewFragment(seqName) ;
}
}
return String.format(String.format("%%0%dd",length),seqMap.get(seqName).getSeqCurrent());
}
@Transactional
protected void getNewFragment(String seqName)
{
logger.debug("獲取流水號段 事務開始");
//從緩存中取出Seq序列號對象
SeqFragmentDefine seqDefine= seqMap.get(seqName);
TblXxxBatseqCtrl TblXxxBatseqCtrl=tblXxxBatseqCtrlMapper.selectForUpdateByPrimaryKey(seqName);
//如果對象存在,則賦值+1
if(TblXxxBatseqCtrl!=null)
{
seqDefine.setSeqFragmentMax(TblXxxBatseqCtrl.getSeqNo()+TblXxxBatseqCtrl.getStepLength());//130000
seqDefine.setSeqCurrent(TblXxxBatseqCtrl.getSeqNo()+1);
TblPipXxxBatseqCtrl.setSeqNo(seqDefine.getSeqFragmentMax());//130000
//TblPipXxxBatseqCtrl.setSeqNo(seqDefine.getSeqCurrent());//120001
tblPipXxxBatseqCtrlMapper.updateByPrimaryKey(TblPipXxxBatseqCtrl);
}
else
{//如果不存在對象,重新創建TblPipXxxBatseqCtrlInsert值,SeqCurrent從0開始,SeqFragmentMax(10000)
TblPipXxxBatseqCtrl TblPipXxxBatseqCtrlInsert=new TblPipXxxBatseqCtrl();
TblPipXxxBatseqCtrlInsert.setSeqName(seqName);
TblPipXxxBatseqCtrlInsert.setStepLength(10000);
TblPipXxxBatseqCtrlInsert.setSeqNo(Long.valueOf(10000));
tblPipXxxBatseqCtrlMapper.insertSelective(TblXxxBatseqCtrlInsert);
seqDefine.setSeqCurrent(0+1);//下一個從1開始
seqDefine.setSeqFragmentMax(0+10000);//最大值爲10000
}
logger.debug("獲取流水號段 事務結束");
}
class SeqFragmentDefine
{
//當前段的最大值(包含)
private long seqFragmentMax;
private long seqCurrent;
public long getSeqFragmentMax() {
return seqFragmentMax;
}
public void setSeqFragmentMax(long seqFragmentMax) {
this.seqFragmentMax = seqFragmentMax;
}
public long getSeqCurrent() {
return seqCurrent;
}
public void setSeqCurrent(long seqCurrent) {
this.seqCurrent = seqCurrent;
}
}
}
實體類
public class TblXxxBatseqCtrl extends BaseEntity {
private String seqName;
private Long seqNo;
private Integer stepLength;
private Date recCrtTs;
private Date recUpdTs;
public String getSeqName() {
return seqName;
}
public void setSeqName(String seqName) {
this.seqName = seqName == null ? null : seqName.trim();
}
public Long getSeqNo() {
return seqNo;
}
public void setSeqNo(Long seqNo) {
this.seqNo = seqNo;
}
public Integer getStepLength() {
return stepLength;
}
public void setStepLength(Integer stepLength) {
this.stepLength = stepLength;
}
public Date getRecCrtTs() {
return recCrtTs;
}
public void setRecCrtTs(Date recCrtTs) {
this.recCrtTs = recCrtTs;
}
public Date getRecUpdTs() {
return recUpdTs;
}
public void setRecUpdTs(Date recUpdTs) {
this.recUpdTs = recUpdTs;
}
}
表結構
drop table if exists tbl_Xxx_batseq_ctrl;
CREATE TABLE tbl_Xxx_batseq_ctrl (seq_name varchar(20) NOT NULL,
seq_no bigint NOT NULL comment '已經使用到的最大值(包含)',
step_length int,
rec_crt_ts timestamp DEFAULT CURRENT_TIMESTAMP,
rec_upd_ts timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (seq_name)) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8_general_ci;
核心邏輯解釋
- format器
String.format(String.format("%%0%dd",length),seqMap.get(seqName).getSeqCurrent())
等價於String.format("%0"+length+“d”,seqMap.get(seqName).getSeqCurrent())
例如: String.format("%08d",1)
輸出: 00000001
例如: String.format("%08d",-1)
輸出:-0000001
例如: String.format("%08d",200000001)
輸出:200000001 (超出長度,輸出原值)
- 設計邏輯
a.每次啓動,分配步長爲10000的序列號給服務器節點使用
b.以損耗序列號資源來換取數據庫服務器的高性能,每個節點的序列號值,不更新到數據庫
c.由於synchronized關鍵字,解決了併發問題
- 內存圖解
學習Java的同學注意了!!!
學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流羣,羣號碼:543120397 我們一起學Java!