java如何根據(jù)時間戳生成有序ID
引言
我們常用的主鍵有這么幾種
1. 數(shù)據(jù)庫自增主鍵,比如mysql的autoincrement,這種插入快,但是識別度不高
2. uuid 這個號稱是全球唯一的,但是無序,沒有實際意義,只能保證唯一
3. 時間戳,這種在分布式的場景下就需要考慮更多種情況
4. 雪花算法 snow flake ,分布式全局唯一主鍵,很牛,但是我覺得用起來也挺麻煩哈哈哈
所以在并發(fā)情況沒那么大的時候用一個工具類搞定,我就是這么懶
工具類
@Slf4j public class NumUtil { private static long tmpID = 0; private static final long LOCK_TIME = 1; private static final long INCREASE_STEP = 1; private static SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS"); private static final Lock LOCK = new ReentrantLock(); public static long nextPkId() throws InterruptedException { //當(dāng)前:(年、月、日、時、分、秒、毫秒) long timeCount; if (LOCK.tryLock(LOCK_TIME, TimeUnit.SECONDS)) { timeCount = Long.parseLong(sdf.format(new Date())); try { if (tmpID < timeCount) { tmpID = timeCount; } else { tmpID += INCREASE_STEP; timeCount = tmpID; } return timeCount; } finally { LOCK.unlock(); } } else { log.error("lock failed"); return nextPkId(); } } }
貼上代碼,這里用了當(dāng)前時間,精確到毫秒級,如果有需要的話可以在實例化timeCount的時候乘以10或者100 1000之類的,這個看大家,然后加上鎖,防止線程不安全的情況,加鎖失敗的時候遞歸,再來一次。
也可以使用synchronized做成同步方法,當(dāng)中的區(qū)別下次再討論。
有評論說宕機會導(dǎo)致tmpID歸0導(dǎo)致已經(jīng)使用過超出當(dāng)前時間的ID,所以持久化這個tmpID也是可以的。
但這也就是在并發(fā)沒那么高的情況下才使用這種方法,一般并發(fā)場景下還是分布式鎖+推特的雪花算法解決。
測試
public static void numTest() { ExecutorService executorService = Executors.newCachedThreadPool(); int n = 10000; List<Long> list = new ArrayList<>(); CountDownLatch latch = new CountDownLatch(n); for (int i = 0; i < n; i++) { executorService.execute(() -> { //執(zhí)行業(yè)務(wù)請求 try { list.add(NumUtil.nextPkId()); } catch (InterruptedException e) { e.printStackTrace(); } latch.countDown(); }); } try { // 一定記得加上timeout時間,防止阻塞主線程 latch.await(3000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { log.error(e.getMessage()); } //4.等待所有子任務(wù)完成,組裝內(nèi)容 while (list.size() < n) { log.info("集合長度 >>> {}",list.size()); } //5.關(guān)閉線程池 executorService.shutdown(); for (Long aLong : list) { System.out.println(aLong); } }
然后噼里啪啦打印了一萬個ID,沒有重復(fù)的,一秒以內(nèi)生成
結(jié)論
當(dāng)然這種只是為了單體或者是并發(fā)沒有高到那么離譜的場景下使用,效率我覺得還不錯,分布式的場景下可能需要用到redis的自增來計數(shù)以達(dá)到數(shù)據(jù)安全的效果
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家
相關(guān)文章
SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程
由于最近開發(fā)的項目需要用到打印單據(jù),就在網(wǎng)上找了一下方案,反反復(fù)復(fù),都沒有找到合適的,借鑒了網(wǎng)上資源,使用itext5、itext7的工具包,所以本文介紹了SpringBoot使用itext填充pdf表單及導(dǎo)出pdf的流程,需要的朋友可以參考下2024-09-09Java多線程案例實戰(zhàn)之定時器的實現(xiàn)
在Java中可以使用多線程和定時器來實現(xiàn)定時任務(wù),下面這篇文章主要給大家介紹了關(guān)于Java多線程案例之定時器實現(xiàn)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01將InputStream轉(zhuǎn)化為base64的實例
這篇文章主要介紹了將InputStream轉(zhuǎn)化為base64的實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12Java設(shè)計模式之觀察者模式(Observer模式)介紹
這篇文章主要介紹了Java設(shè)計模式之觀察者模式(Observer模式)介紹,Java深入到一定程度,就不可避免的碰到設(shè)計模式(design pattern)這一概念,了解設(shè)計模式,將使自己對java中的接口或抽象類應(yīng)用有更深的理解,需要的朋友可以參考下2015-03-03Springboot+redis+Interceptor+自定義annotation實現(xiàn)接口自動冪等
本篇文章給大家介紹了使用springboot和攔截器、redis來優(yōu)雅的實現(xiàn)接口冪等,對于冪等在實際的開發(fā)過程中是十分重要的,因為一個接口可能會被無數(shù)的客戶端調(diào)用,如何保證其不影響后臺的業(yè)務(wù)處理,如何保證其只影響數(shù)據(jù)一次是非常重要的,感興趣的朋友跟隨小編一起看看吧2019-07-07