高并发环境下生成唯一流水号

高并发环境下生成唯一流水号的主要思路有两种:

  第一种是有一个控制全局的变量确保每个流水号的唯一性;

  第二种是每台机器根据算法自己生成在系统中无冲突的流水号;

假设流水号的长度是128位(16字节);

第一种实现方法:(1)采用数据库的自增主键确保唯一性;

Database.java

package mine;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Database {
    static String serialNumber;
    static String username="root";
    static String pwd = "123";
    static String url = "jdbc:mysql://192.168.1.6:3306/serialnumber";
    static String driver = "org.gjt.mm.mysql.Driver";
    private Connection con;
    private Statement statement;
    public static void main(String[] args){
        serialNumber =new Database().getSerialNumber();
        System.out.println(serialNumber);
    }
    private void start(){
        try {
            Class.forName( driver );
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            System.out.println("error in loading driver.");
        }

        long time=10000;
        while((con)==null&&time>0){//设置超时时间10s
            try {
                Thread.sleep(100);
                time-=100;
                con = DriverManager.getConnection(url,username,pwd);
            }catch(Exception e){}
        }
        time=1000;
        while((statement)==null&&time>0){
            try{
                Thread.sleep(100);
                time-=100;
                statement = con.createStatement();
            }catch(Exception e){}
        }
    }
    private void close(){
        try {
            if(statement!=null)
                statement.close();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            System.out.println("error in close statement.");
        }
        try {
            if(con!=null)
                con.close();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            System.out.println("error in close connection.");
        }
    }
    public String getSerialNumber(){
        start();
        String str="";
        long time =System.currentTimeMillis();
        try{
            statement.execute("insert serialnumber(time) values("+time+")");
            ResultSet re = statement.executeQuery("select NO from serialnumber where time="+time+";");
            if(re.next()){
                str=re.getString(1);
            }
            re.close();
        }catch(Exception e){}
        finally{
            close();
        }
        return ""+time+str;
    }
}

ThreadTest.java,线程池容量为100(mysql的默认连接数是100);每个线程的连接超时时间为10秒,启动10000个线程;

运行时间:34s

package mine;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Client extends Thread{
    private String serialNumber="";
    private String ID="";
    static int connect=0;
    Client(String id){
        this.ID=id;
    }
    public void run(){
        //connect++;
        new Database().getSerialNumber();
        //System.out.println("thread "+ID+" run ……connect:"+connect);
        //connect--;
    }
    public String getSerialNumber(){
        return serialNumber;
    }
    public String getID(){
        return ID;
    }
}

public class ThreadTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        long time0=System.currentTimeMillis();
        ExecutorService executor = Executors.newFixedThreadPool(100);//creating a pool of 5 threads
        for (int i = 0; i < 10000; i++) {
            Thread client = new Client("" + i);
            executor.execute(client);//calling execute method of ExecutorService
        }
        executor.shutdown();
        while (!executor.isTerminated()) {}
        int time =((int)(System.currentTimeMillis()-time0))/1000;
        System.out.println("Finished all threads time:"+time+"s");
    }

}

……Finished all threads time:34s

(2)采用全局变量加锁的方式确保唯一性;流水号为20位十进制数:13位表示时间的数+7位(全局变量);

package mine;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class GSerialNumber extends Thread{
    private static Object lock=new Object();
    private static long golbal=0;
    private String SerialNumber="";
    public String getSerialNumber(){
        long temp;
        synchronized(lock){
            temp=golbal++;
        }
        SerialNumber=String.format("%013d%07d",System.currentTimeMillis(),temp);
        return SerialNumber;
    }
    public void run(){
        String str=getSerialNumber();
    }
}
public class GlobalLock {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        long time0=System.currentTimeMillis();
        int n =10000;
        ExecutorService executor =Executors.newFixedThreadPool(n);
        for(int i=0;i<100*n;i++){
            Thread thread = new GSerialNumber();
            executor.execute(thread);
        }
        executor.shutdown();
        while(!executor.isTerminated()){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {}
        }
        long time =(int)((System.currentTimeMillis()-time0));
        System.out.println("Time:"+time+"ms");
    }

}

线程池容量10000;线程数:1000000;

运行时间:

Time:5107ms

采用字典树测试是否有冲突;

package mine;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import util.Trie;

class GSerialNumber extends Thread{
    private static Object lock=new Object();
    private static long golbal=0;
    private String SerialNumber="";
    public String getSerialNumber(){
        long temp;
        synchronized(lock){
            temp=golbal++;
        }
        SerialNumber=String.format("%013d%07d",System.currentTimeMillis(),temp);
        return SerialNumber;
    }
    static Trie trie=new Trie();
    static Object tlock = new Object();
    public void run(){
        String str=getSerialNumber();
        synchronized(tlock){
            trie.insert(str);
        }
    }
}
public class GlobalLock {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        long time0=System.currentTimeMillis();
        int n =10000;
        ExecutorService executor =Executors.newFixedThreadPool(n);
        for(int i=0;i<100*n;i++){
            Thread thread = new GSerialNumber();
            executor.execute(thread);
        }
        executor.shutdown();
        while(!executor.isTerminated()){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {}
        }
        long time =(int)((System.currentTimeMillis()-time0));
        System.out.println("Time:"+time+"ms");
        System.out.println("冲突数:"+new GSerialNumber().trie.count);
    }

}

create trie
Time:5755ms
冲突数:0

第二种方案(独自生成流水号):

(1)采用MAC地址+System.currentTimeMillis()+System.nanoTime();测试线程池容量为100,线程数为10000

    public static String method2(){
        NetworkInterface netInterface;
        long mac=0;
        try {
            netInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
            mac =new BigInteger(netInterface.getHardwareAddress()).longValue();//.toString(10);
        }catch (SocketException | UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        String str = String.format("%039d%013d%013d",mac,System.currentTimeMillis(),System.nanoTime());
        return encryp(str);
    }

create trie
冲突数:10

(2)采用MAC地址+System.currentTimeMillis()+Math.Random()*1000;测试线程池容量为100,线程数为10000

    public static String method3(){
        NetworkInterface netInterface;
        long mac=0;
        try {
            netInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
            mac =new BigInteger(netInterface.getHardwareAddress()).longValue();
        }catch (SocketException | UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        String str = String.format("%039d%013d%04d",mac,System.currentTimeMillis(),(int)(Math.random()*1000));
        return str;
    }

create trie
冲突数:1

(3)采用MAC地址+PID+System.currentTimeMillis();Thread(10);

    public static String method4(){
        NetworkInterface netInterface;
        long mac=0;
        try {
            netInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost());
            mac =new BigInteger(netInterface.getHardwareAddress()).longValue();
        }catch (SocketException | UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        String name = ManagementFactory.getRuntimeMXBean().getName();
        // get pid
        String pid = name.split("@")[0];
        String str = String.format("%039d%s%013d",mac,pid,System.currentTimeMillis());
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return encryp(str);
    }

这里采用多线程测试的时候有一个问题,不同的线程之间是公用一个pid,这样就会产生冲突;为此测试时我们应该把线程id也附加上;测试线程池容量为100,线程数为10000

测试代码:

package mine;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import util.Trie;

class SerialCreate extends Thread{
    static int ID=0;
    int id=ID++;
    static Trie tries = new Trie();
    static Object lock = new Object();
    public void run(){
        String str=new SerialNumber().method4()+String.format("%06d", id);//加入线程id
        synchronized (lock) {
            tries.insert(str);
        }
    }
}

public class SerialNumberTest {
    public static void main(String[] args){
        int n =100;
        ExecutorService execupool = Executors.newFixedThreadPool(n);
        for(int i=0;i<100*n;i++){
            Thread serial = new SerialCreate();
            execupool.execute(serial);
        }
        execupool.shutdown();
        while(!execupool.isTerminated()){}
        System.out.println("冲突数:"+new SerialCreate().tries.count);
    }
}

测试结果:

create trie
冲突数:0

由于账单号是128位的,而上面的生成算法有的产生字符串表示的数字会超过128位,这样就需要用MD5算法加密散列成128位的数字;

转换函数:encrpy()

    public static String encryp(String pwd){
        byte[] message=null;
        message = pwd.getBytes();
        MessageDigest md=null;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] encrypwd =md.digest(message);
        BigInteger bigInteger = new BigInteger(1, encrypwd);
        return bigInteger.toString(10);
    }

时间: 2024-12-06 21:46:26

高并发环境下生成唯一流水号的相关文章

数据库 之 高并发环境下的规则

原文:数据库 之 高并发环境下的规则 本文大部分转至沈剑老师,加上自己的一些见解. 本文前提 高并发环境 规则要点 1) 数据库字符集使用utf8mb4 无乱码风险.万国码 2)禁止使用存储过程.视图.触发器.Event 高并发大数据的互联网业务,架构设计思路是"解放数据库CPU,将计算转移到服务层",并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现"增机器就加性能".数据库擅长存储与索引,CPU计算还是上移吧 3)禁止

MQ在高并发环境下,如果队列满了,如何防止消息丢失?

1.为什么MQ能解决高并发环境下的消息堆积问题? MQ消息如果堆积,消费者不会立马消费所有的消息,不具有实时性,所以可以解决高并发的问题. 性能比较好的消息中间件:Kafka.RabbitMQ,RocketMQ. 2.什么情况下会产生消息丢失的现象? 消息队列满了的情况下. 3.如何解决消息丢失的问题? (1)生产者可以采用重试机制.因为消费者会不停的消费消息,可以重试将消息放入队列. 如果还是不行,可以将消息记录到数据库,后期做补偿.(不太推荐,不方便) (2)死信队列,可以理解为备胎.(推荐

如何在高并发环境下设计出无锁的数据库操作(Java版本) 转载

一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Java的Disruptor就是一个很好的例子.如果用java的concurrentCollection类去做,原理就是启动一个线程,跑一个Queue,并发的时候,任务压入Queue,线程轮训读取这个Queue,然后一个个顺序执行. 在这个设计模式下,任何并发都会变成了单线程操作,而且速度非常快.现在的n

高并发环境下低级死循环bug

业务背景 在内存中,对mq消息进行分类计数. 问题描述 生产环境,运行一段时间后,发现消息队列有大量堆积.如果把计数逻辑注释掉,只接收用户访问消息而不进行处理,则mq队列无堆积.mq栈dump信息如下: ConsumeMessageThread_75    TID: 214 STATE: WAITING ConsumeMessageThread_75    sun.misc.Unsafe.park(Native Method) ConsumeMessageThread_75    java.ut

高并发环境下,Redisson实现redis分布式锁

原文:http://tlzl0526-gmail-com.iteye.com/blog/2378853 在一些高并发的场景中,比如秒杀,抢票,抢购这些场景,都存在对核心资源,商品库存的争夺,控制不好,库存数量可能被减少到负数,出现超卖的情况,或者 产生唯一的一个递增ID,由于web应用部署在多个机器上,简单的同步加锁是无法实现的,给数据库加锁的话,对于高并发,1000/s的并发,数据库可能由行锁变成表锁,性能下降会厉害.那相对而言,redis的分布式锁,相对而言,是个很好的选择,redis官方推

【高并发】高并发环境下诡异的加锁问题(你加的锁未必安全)

声明 特此声明:文中有关支付宝账户的说明,只是用来举例,实际支付宝账户要比文中描述的复杂的多.也与文中描述的完全不同. 前言 很多网友留言说:在编写多线程并发程序时,我明明对共享资源加锁了啊?为什么还是出问题呢?问题到底出在哪里呢?其实,我想说的是:你的加锁姿势正确吗?你真的会使用锁吗?错误的加锁方式不但不能解决并发问题,而且还会带来各种诡异的Bug问题,有时难以复现! 在上一篇<[高并发]如何使用互斥锁解决多线程的原子性问题?这次终于明白了!>一文中,我们知道在并发编程中,不能使用多把锁保护

并发情况下取唯一流水号的写法

readonly object _Syn = new object(); int Seed = 0; public int TreadValueGet() { // 5 Critical Section // Enter 和 Exit 方法提供的功能与 C# lock 语句提供的功能相同, // 区别在于 lock 将 Enter(Object, Boolean) 方法重载和 Exit 方法包装在 try…finally 块中以确保释放监视器 //int _retValue = 0; //whi

如何在高并发分布式系统中生成全局唯一Id(转)

http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了个MSSQL参数化语法生成器,会在9月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也

高并发分布式系统中生成全局唯一Id汇总

高并发分布式系统中生成全局唯一Id汇总 (转自:http://www.cnblogs.com/baiwa/p/5318432.html) 数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求:   1 不能有单点故障.   2 以时间为序,或者ID里包含时间.这样一是可以少一个索引,二是冷热数据容易分离.   3 可以控制ShardingId.比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易.