50万数据生成6位数不重复字符串_JAVA技术分享:单号的生成
一.訂單號生成的原則:
1.全局的唯一性
2.自增長
3.長度的要求
4.具有一定的可讀性
5.保密,不可推測性
6.效率性
二.實現方案
常見的ID生成策略。 1. 數據庫自增長序列或字段 2. UUID 3. UUID的變種*【UUID to Int64;NHibernate在其主鍵生成方式中提供了Comb算法(combined guid/timestamp)】 4. Redis生成ID 5. Twitter的snowflake算法 6. 利用zookeeper的znode生成唯一ID 7. MongoDB的ObjectId
三.高并發下怎樣生成唯一的訂單號?
如果沒有并發,訂單號只在一個線程內產生,那么由于程序是順序執行的,不同訂單的生成時間一定不同,因此用時間就可以區分各個訂單。
如果存在并發,且訂單號是由一個進程中的多個線程產生的,那么只要把線程ID添加到序列號中就可以保證訂單號唯一。
如果存在并發,且訂單號是由同一臺主機中的多個進程產生的,那么只要把進程ID添加到序列號中就可以保證訂單號唯一。
如果存在并發,且訂單號是由不同臺主機產生的,那么MAC地址、IP地址或CPU序列號等能夠區分主機的號碼添加到序列號中就可以保證訂單號唯一。
1. 機器碼(3位, 分布式節點),年月日分時秒(12位),遞增的序列(4位),當并發遞增序列超過4位時,秒數+1,序列從0開始計時,這樣每秒支持9999個訂單號生成,隔天序列清為0.
2.后臺統一生成的訂單號后,推入redis,一次性推個幾十W個,檢查剩余多少后,再推,也可以保證高并發的場景。
四.Twitter開源分布式自增ID算法snowflake
1.snowflake簡介
互聯網快速發展的今天,分布式應用系統已經見怪不怪,在分布式系統中,我們需要各種各樣的ID,既然是ID那么必然是要保證全局唯一,除此之外,不同當業務還需要不同的特性,比如像并發巨大的業務要求ID生成效率高,吞吐大;比如某些銀行類業務,需要按每日日期制定交易流水號;又比如我們希望用戶的ID是隨機的,無序的,純數字的,且位數長度是小于10位的。等等,不同的業務場景需要的ID特性各不一樣,于是,衍生了各種ID生成器,但大多數利用數據庫控制ID的生成,性能受數據庫并發能力限制,那么有沒有一款不需要依賴任何中間件(如數據庫,分布式緩存服務等)的ID生成器呢?本著取之于開源,用之于開源的原則,今天,特此介紹Twitter開源的一款分布式自增ID算法snowflake,并附上算法原理推導和演算過程!
snowflake算法是一款本地生成的(ID生成過程不依賴任何中間件,無網絡通信),保證ID全局唯一,并且ID總體有序遞增,性能每秒生成300w+。
2.snowflake算法原理
snowflake生產的ID是一個18位的long型數字,二進制結構表示如下(每部分用-分開):
0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000 - 00000 - 00000000 0000
第一位未使用,接下來的41位為毫秒級時間(41位的長度可以使用69年,從1970-01-01 08:00:00),然后是5位datacenterId(最大支持2^5=32個,二進制表示從00000-11111,也即是十進制0-31),和5位workerId(最大支持2^5=32個,原理同datacenterId),所以datacenterId*workerId最多支持部署1024個節點,最后12位是毫秒內的計數(12位的計數順序號支持每個節點每毫秒產生2^12=4096個ID序號).
所有位數加起來共64位,恰好是一個Long型(轉換為字符串長度為18).
單臺機器實例,通過時間戳保證前41位是唯一的,分布式系統多臺機器實例下,通過對每個機器實例分配不同的datacenterId和workerId避免中間的10位碰撞。最后12位每毫秒從0遞增生產ID,再提一次:每毫秒最多生成4096個ID,每秒可達4096000個。理論上,只要CPU計算能力足夠,單機每秒可生產400多萬個,實測300w+,效率之高由此可見。
(該節改編自:http://www.cnblogs.com/relucent/p/4955340.html)
3.snowflake算法源碼(java版)
@ToString@Slf4jpublic class SnowflakeIdFactory { private final long twepoch = 1288834974657L; private final long workerIdBits = 5L; private final long datacenterIdBits = 5L; private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); private final long sequenceBits = 12L; private final long workerIdShift = sequenceBits; private final long datacenterIdShift = sequenceBits + workerIdBits; private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final long sequenceMask = -1L ^ (-1L << sequenceBits); private long workerId; private long datacenterId; private long sequence = 0L; private long lastTimestamp = -1L; public SnowflakeIdFactory(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { //服務器時鐘被調整了,ID生成器停止服務. throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; } protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } protected long timeGen() { return System.currentTimeMillis(); } public static void testProductIdByMoreThread(int dataCenterId, int workerId, int n) throws InterruptedException { List tlist = new ArrayList<>(); Set setAll = new HashSet<>(); CountDownLatch cdLatch = new CountDownLatch(10); long start = System.currentTimeMillis(); int threadNo = dataCenterId; Map idFactories = new HashMap<>(); for(int i=0;i<10;i++){ //用線程名稱做map key. idFactories.put("snowflake"+i,new SnowflakeIdFactory(workerId, threadNo++)); } for(int i=0;i<10;i++){ Thread temp =new Thread(new Runnable() { @Override public void run() { Set setId = new HashSet<>(); SnowflakeIdFactory idWorker = idFactories.get(Thread.currentThread().getName()); for(int j=0;j setOne = new HashSet<>(); Set setTow = new HashSet<>(); long start = System.currentTimeMillis(); for (int i = 0; i < n; i++) { setOne.add(idWorker.nextId());//加入set } long end1 = System.currentTimeMillis() - start; log.info("第一批ID預計生成{}個,實際生成{}個<<<>>>共耗時:{}",n,setOne.size(),end1); for (int i = 0; i < n; i++) { setTow.add(idWorker2.nextId());//加入set } long end2 = System.currentTimeMillis() - start; log.info("第二批ID預計生成{}個,實際生成{}個<<<>>>共耗時:{}",n,setTow.size(),end2); setOne.addAll(setTow); log.info("合并總計生成ID個數:{}",setOne.size()); } public static void testPerSecondProductIdNums(){ SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2); long start = System.currentTimeMillis(); int count = 0; for (int i = 0; System.currentTimeMillis()-start<1000; i++,count=i) { /** 測試方法一: 此用法純粹的生產ID,每秒生產ID個數為300w+ */ idWorker.nextId(); /** 測試方法二: 在log中打印,同時獲取ID,此用法生產ID的能力受限于log.error()的吞吐能力. * 每秒徘徊在10萬左右. */ //log.error("{}",idWorker.nextId()); } long end = System.currentTimeMillis()-start; System.out.println(end); System.out.println(count); } public static void main(String[] args) { /** case1: 測試每秒生產id個數? * 結論: 每秒生產id個數300w+ */ //testPerSecondProductIdNums(); /** case2: 單線程-測試多個生產者同時生產N個id,驗證id是否有重復? * 結論: 驗證通過,沒有重復. */ //testProductId(1,2,10000);//驗證通過! //testProductId(1,2,20000);//驗證通過! /** case3: 多線程-測試多個生產者同時生產N個id, 全部id在全局范圍內是否會重復? * 結論: 驗證通過,沒有重復. */ try { testProductIdByMoreThread(1,2,100000);//單機測試此場景,性能損失至少折半! } catch (InterruptedException e) { e.printStackTrace(); } }}測試用例
/** case1: 測試每秒生產id個數? * 結論: 每秒生產id個數300w+ *///testPerSecondProductIdNums(); /** case2: 單線程-測試多個生產者同時生產N個id,驗證id是否有重復? * 結論: 驗證通過,沒有重復. *///testProductId(1,2,10000);//驗證通過!//testProductId(1,2,20000);//驗證通過! /** case3: 多線程-測試多個生產者同時生產N個id, 全部id在全局范圍內是否會重復? * 結論: 驗證通過,沒有重復. */try { testProductIdByMoreThread(1,2,100000);//單機測試此場景,性能損失至少折半!} catch (InterruptedException e) { e.printStackTrace();}4.snowflake算法推導和演算過程
說明:
演算使用的對象實例:SnowflakeIdFactory idWorker = new SnowflakeIdFactory(1, 2);
運行時數據workerId=1,datacenterId=2,分別表示機器實例的生產者編號,數據中心編號;
sequence=0表示每毫秒生產ID從0開始計數遞增;
以下演算基于時間戳=1482394743339時刻進行推導。
一句話描述:以下演算模擬了1482394743339這一毫秒時刻,workerId=1,datacenterId=2的id生成器,生產第一個id的過程。
總結
以上是生活随笔為你收集整理的50万数据生成6位数不重复字符串_JAVA技术分享:单号的生成的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【MongoDB 工具篇】MongoDB
- 下一篇: 封装html ui 控件,聊聊前端 UI