订单号生成逻辑,C#和JAVA双版
五年没写过博客了,倒是天天在看
转来转去,又转回技术
原来一直在使用微软爸爸的东西,最近一两年开始玩android,玩java,还有PostgreSQL
都有些应用了,倒是可以整理些随笔出来,这就是其中一篇吧
c#是java的优雅版本,java的linq就是一坨那啥,嗯!觉得不爽就别看了
订单号这个玩意我想有几点得保证
1,有顺序但不连续
2,不能太长,最好15位以内
3,最好全数字
目前来说谷歌和百度出来的感觉都达不到我要的这些要求,不是太长超过20位,就是会加上字母,再就是会连续,否则就没顺序,没有办法只有自己想
本来没想自己干的,这玩意绝对是个老问题,估计大家都有好办法,藏着了吧
开干,思路如下:
得有时间在里面,这样有顺序
不连续好办,加上随机尾数
不能太长也好办,取一部分时间戳,或者是当前时间减去过去一个固定时间的差值
OK,上代码,先JAVA
import java.io.*;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong; /**
* Created by 黑曜石 on 2017/5/19.
*/
public class TradeNoGenerator {
//基准时间截 (2017-01-01)
private static final long initStamp=1483200000000L;
//每秒基准容量1000个
private static final int secondsSpace=1000;
//基准容量扩容倍数
private static final int secondsSpaceMulti=10;
//多少秒不用,重新设定原子初始值
private static final int secondsRefresh=60;
//原子初始值
private AtomicLong atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti); private TradeNoGenerator() {
} private static class TradeNoGeneratorHolder{
private static TradeNoGenerator instance = new TradeNoGenerator();
} public static TradeNoGenerator getInstance() {
return TradeNoGeneratorHolder.instance;
} //线程安全
/**
* 用这个,用这个
* @return
* 生成结果例:11983017246298
* 由三段组成,11983017,2462,98
* 第一段11983017,位数会随着时间增长增加,计算方式为与initStamp相加,结果为到秒的时间戳
* 第二段,2462,看容量决定位数,容量=secondsSpace*secondsSpaceMulti,原子增长字段
* 第三段98,固定2位,随机数
*/
public synchronized String getNo(){
long no=atomicLong.getAndIncrement();
//当前秒时间戳与基准时间戳秒级数量
long currentSecond = (System.currentTimeMillis()-initStamp) / 1000;
//计数超过60秒未使用过,则重新给定初始值
if (currentSecond-no/(secondsSpace*secondsSpaceMulti)>=secondsRefresh){
//重置原子计数原始值
atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
no=atomicLong.getAndIncrement();
}
//计算容量是否超出
if (no-currentSecond*secondsSpace*secondsSpaceMulti>=secondsSpace*secondsSpaceMulti){
//超出则等待下一秒的到来,这里计算到下一整数秒的微秒差
long millisWithinSecond = System.currentTimeMillis() % 1000;
try {
Thread.sleep(1000 - millisWithinSecond);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println(">>> new second . "+no +" "+Thread.currentThread().getName()); //重置原子计数原始值
atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
no=atomicLong.getAndIncrement();
} int hashCode= UUID.randomUUID().hashCode();
hashCode=hashCode<0?-hashCode:hashCode;
String randomStr=String.valueOf(hashCode).substring(0,2);
return no+randomStr;
} /**
* 获取时间戳
* @param tradeNo
* @return
*/
public long getTimestamp(String tradeNo){
int spaceLen = String.valueOf(secondsSpace*secondsSpaceMulti).length()-1;
int stampLen=tradeNo.length()-spaceLen-2;
String stampStr=tradeNo.substring(0,stampLen);
//右侧补齐微秒
long stamp=Long.parseLong(stampStr+String.format("%1$03d",0));
stamp+=initStamp;
return stamp;
} public static void main(String[] args) { File file=new File("e:/tradeno1.txt");
if (file.exists()){
file.delete();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
file=new File("e:/tradeno2.txt");
if (file.exists()){
file.delete();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
} new Thread(new Runnable() {
@Override
public void run() { try {
Writer w=new FileWriter("e:/tradeno1.txt");
BufferedWriter buffWriter=new BufferedWriter(w); TimeMark timeMark=new TimeMark();
for (int i = 0; i < 12000; i++) {
String x=getInstance().getNo();
//System.out.println(x);
buffWriter.write(x+"\t\r");
}
System.out.println("]]]");
timeMark.simplePrint(); buffWriter.close();
w.close();
System.out.println("写入成功!"); } catch (FileNotFoundException e) {
System.out.println("要读取的文件不存在:"+e.getMessage());
} catch (IOException e) {
System.out.println("文件读取错误:"+e.getMessage());
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
try {
Writer w=new FileWriter("e:/tradeno2.txt");
BufferedWriter buffWriter=new BufferedWriter(w); TimeMark timeMark=new TimeMark();
for (int i = 0; i < 24000; i++) {
String x=getInstance().getNo();
//System.out.println(x);
buffWriter.write(x+"\t\r");
}
System.out.println("]]]");
timeMark.simplePrint(); buffWriter.close();
w.close();
System.out.println("写入成功!"); } catch (FileNotFoundException e) {
System.out.println("要读取的文件不存在:"+e.getMessage());
} catch (IOException e) {
System.out.println("文件读取错误:"+e.getMessage());
}
}
}).start();
}
}
接下来是C#
public class TradeNoGenerator
{
//基准时间截 (2017-01-01)
private static DateTime initDateTime = new DateTime(2017, 1, 1);
//每秒基准容量1000个
private static int secondsSpace = 1000;
//基准容量扩容倍数
private static int secondsSpaceMulti = 10;
//多少秒不用,重新设定原子初始值
private static int secondsRefresh = 60;
//原子初始值
private static long initStamp = (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti; static TradeNoGenerator __TradeNoGenerator;
private TradeNoGenerator()
{
} public static TradeNoGenerator GetInstance()
{
if (__TradeNoGenerator == null)
{
__TradeNoGenerator = new TradeNoGenerator();
}
return __TradeNoGenerator;
} //线程安全
/**
* 用这个,用这个
* @return
* 生成结果例:11983017246298
* 由三段组成,11983017,2462,98
* 第一段11983017,位数会随着时间增长增加,计算方式为与initStamp相加,结果为到秒的时间戳
* 第二段,2462,看容量决定位数,容量=secondsSpace*secondsSpaceMulti,原子增长字段
* 第三段98,固定2位,随机数
*/
public String GetNo()
{
long no = Interlocked.Increment(ref initStamp);
//当前秒时间戳与基准时间戳秒级数量
double currentSecond = DateTime.UtcNow.Subtract(initDateTime).TotalSeconds;
//计数超过60秒未使用过,则重新给定初始值
if (currentSecond - no / (secondsSpace * secondsSpaceMulti) >= secondsRefresh)
{
//重置原子计数原始值
initStamp= (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;
no = Interlocked.Increment(ref initStamp);
}
//计算容量是否超出
if (no - currentSecond * secondsSpace * secondsSpaceMulti >= secondsSpace * secondsSpaceMulti)
{
//超出则等待下一秒的到来,这里计算到下一整数秒的微秒差
int millisWithinSecond = DateTime.UtcNow.Millisecond % 1000;
try
{
Thread.Sleep(1000 - millisWithinSecond);
}
catch (Exception ex)
{ } //重置原子计数原始值
initStamp = (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;
no = Interlocked.Increment(ref initStamp);
} int hashCode = Guid.NewGuid().GetHashCode();
hashCode = hashCode < 0 ? -hashCode : hashCode;
String randomStr = hashCode.ToString().Substring(0, 2);
return no + randomStr;
}
}
注释写得很清楚啦
1秒基准容量是1000个,可以以10的倍数来扩大(只能是10的倍数),一般扩大10倍,1秒容量到10000个,可以解决很多问题啦
速度你们可以自己测试,一秒内的速度是毫秒级别的
超出秒容量的话,那就得等待下一秒的到来
还有个问题就是当前不支持分布式
无论是JAVA还是C#基本是利用原子自增方式解决并发和效率问题
订单号生成逻辑,C#和JAVA双版的更多相关文章
- Java订单号生成,唯一订单号(日均千万级别不重复)
Java订单号生成,唯一订单号 相信大家都可以搜索到很多的订单的生成方式,不懂的直接百度.. 1.订单号需要具备以下几个特点. 1.1 全站唯一性. 1.2 最好可读性. 1.3 随机性,不能重复,同 ...
- 偶尔在网上看到的,相对比较好的c#端订单号生成规则
偶尔在网上看到的,相对比较好的c#端订单号生成规则 public class BillNumberBuilder{ private static object locker = new obj ...
- 全局唯一订单号生成方法(参考snowflake)
backgroud Snowflake is a network service for generating unique ID numbers at high scale with some si ...
- 基于redis的订单号生成方案
目前,比较火的nosql数据库,如MongoDB,Redis,Riak都提供了类似incr原子行操作. 下面是PHP版的一种实现方式: <?php /** * 基于Redis的全局订单号id * ...
- 业务订单号生成算法,每秒50W左右,不同机器保证不重复,包含日期可读性好
参考snowflace算法,基本思路: 序列12位(更格式化的输出后,性能损耗导致每毫秒生成不了这么多,所以可以考虑减少这里的位,不过留着也并无影响) 机器位10位 毫秒为左移 22位 上述几个做或运 ...
- C#端一个不错的订单号生成规则
/// <summary> /// 订单助手 /// </summary> public class OrderHelper { /// <summary> /// ...
- C#:lock锁与订单号(或交易号)的生成
在弄电商类网站的时候,往往是根据年月日时分秒的格式生成订单号(yyyyMMddHHmmss),为了解决并发性,就直接在生成订单号的区域块加上lock. 下面,我们来简单测试一下. 1.新建项目(控制台 ...
- PHP生成唯一的订单号
记:之前面试的时候被面试官问过简历项目中的订单号我是什么规则生成的,我牛逼吹过头了,乱说了一通,靠!今天在公司的项目中订单号生成,好奇,看了下,就是网上的这种而已. * * uniqid - 官方是这 ...
- MSSQL高并发下生成连续不重复的订单号
一.确定需求 只要做过开发的基本上都有做过订单,只要做过订单的基本上都要涉及生成订单号,可能项目订单号生成规则都不一样,但是大多数规则都是连续增长. 所以假如给你一个这样的需求,在高并发下,以天为单位 ...
随机推荐
- 【jQuery】todolist
1 2 3 用npm命令下载依赖,优点:不用去网上找链接,代码都一样 4.jQuery自动下载进node_modules文件下 npm install jquery --save 这句命令的意思是保 ...
- PAT——1049. 数列的片段和
给定一个正数数列,我们可以从中截取任意的连续的几个数,称为片段.例如,给定数列{0.1, 0.2, 0.3, 0.4},我们有(0.1) (0.1, 0.2) (0.1, 0.2, 0.3) (0.1 ...
- loadrunner脚本中参数化和返回值输出log到外部文件
loadrunner脚本中参数化和返回值输出log到外部文件 很多时候,我们在做性能测试之前,需要造数据,但是使用的这些参数化数据和生成的返回数据在后面的测试都会用的,所以我们需要在造数据过程中,将参 ...
- c#中的结构
1.在c#中,结构是值类型的数据结构,它可以使用一个单一的变量存储各种数据类型的相关数据,使用Struct关键字进行声明. 2.C#中结构的特点: (1)结构中可以有字段,属性,方法,运算表达式,事件 ...
- 将硬件规定的通信协议用Lua实现(涉及到很多Lua通信的数据转换)
1:这次处理的是大唐的gps通信协议,先简单介绍一下他规定的通信规则: 信息结构: 传输说明: 信息结构中的各个字节书写时都是以十六进制标识,两位数组成.传输时,SOI和EOI(SOI=7EH,EOI ...
- Powerdesigner设置表字段注释与name相同
Powerdesigner设置当表字段注释为空时与name相同 1.在Database-->edit Current DBMS-->script-->objects-->col ...
- Java中的集合框架-Collection(二)
上一篇<Java中的集合框架-Collection(一)>把Java集合框架中的Collection与List及其常用实现类的功能大致记录了一下,本篇接着记录Collection的另一个子 ...
- layDay日期格式不合法报错解决
报错内容如下: Uncaught TypeError: Cannot read property 'appendChild' of undefined 相关报错内容的行代码如下 即使日期格式拼接正确也 ...
- yyy loves Easter_Egg I(恶心的字符串模拟)
题目背景 Soha的出题效率着实让人大吃一惊.OI,数学,化学的题目都出好了,物理的题还没有一道.于是,Huntfire,absi2011,redbag对soha进行轮番炸,准备炸到soha出来,不料 ...
- 事务与MVCC
前言 关于事务,是一个很重要的知识点,大家在面试中也会被经常问到这个问题: 数据库事务有不同的隔离级别,不同的隔离级别对锁的使用是不同的,**锁的应用最终导致不同事务的隔离级别 **:在上一篇文章中我 ...