论坛首页 Java企业应用论坛

一个循环流水号实现,求评

浏览 13200 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (4)
作者 正文
   发表时间:2011-09-19  
weiqiang.yang 写道
调用参数里面还得传一个dao,这有点怪异吧
getNextSerialNumber不一定非得是static的

可以每次取下一个序列的时候都检查一下有没有初始化过,如果没有初始化,那么先从数据库读取数据进行初始化
如果已经初始化了,那么直接返回(内存中保存的数据+1)%LIMIT


 private static volatile boolean INITED = false;
    private static volatile int SN = 0;
    
    private void init(){
        SN = xxxDao.xx(); // 从数据库加载最后一次持久化的值
        INITED = true;
    }
    
    public synchronized String getNextSerialNumber(){
       if(!INITED) init(); 
       SN = (SN + 1) % LIMIT;
       // TODO: 更新数据库
       return String.format("%06d", SN);
    }


这个思路比楼主的思路好。但个人觉得还有改进的地方。
每次获取就更新一次数据库可改进:
一次读取时,设置一个区间,如20,更新数据库的序号就是当前值+区间的值,而每次获取下一个序列号值则多一个校验,是否已经达到(当前值+区间)值,如达到,则重新往数据库读即可。
0 请登录后投票
   发表时间:2011-09-19  
weiqiang.yang 写道
调用参数里面还得传一个dao,这有点怪异吧
getNextSerialNumber不一定非得是static的

可以每次取下一个序列的时候都检查一下有没有初始化过,如果没有初始化,那么先从数据库读取数据进行初始化
如果已经初始化了,那么直接返回(内存中保存的数据+1)%LIMIT


 private static volatile boolean INITED = false;
    private static volatile int SN = 0;
    
    private void init(){
        SN = xxxDao.xx(); // 从数据库加载最后一次持久化的值
        INITED = true;
    }
    
    public synchronized String getNextSerialNumber(){
       if(!INITED) init(); 
       SN = (SN + 1) % LIMIT;
       // TODO: 更新数据库
       return String.format("%06d", SN);
    }


可以预见的是大并发下,getNextSerialNumber 会成为系统的一个瓶颈.
0 请登录后投票
   发表时间:2011-09-19  
直接用sequence好了,这么麻烦
0 请登录后投票
   发表时间:2011-09-19  
我在想能不能缩小同步的范围,像这样:
public String getNextSerialNumber(){ 
      int n = nextSerialNumber();
      updateDatabase(n);
}

public synchronized String nextSerialNumber(){}
public String updateDatabase(int n){}
0 请登录后投票
   发表时间:2011-09-19  
chunquedong 写道
我在想能不能缩小同步的范围,像这样:
public String getNextSerialNumber(){ 
      int n = nextSerialNumber();
      updateDatabase(n);
}

public synchronized String nextSerialNumber(){}
public String updateDatabase(int n){}


会好些,但是换汤不换药.synchronized 的问题在于无论是不是并发都要上锁.所以应该用 AtomicLong 这些工具,这些工具实际调用CPU指令基于CAS,效率会更好.

这个问题有种情况,很让人挠头,就是要求系统重启后必须继续分配编号.这等于要求 nextSerialNumber 和 updateDatabase必须是原子性的.这种要求下,上面所有的代码都要完蛋.也就只能用数据库的sequence了.可是性能情况未知.
0 请登录后投票
   发表时间:2011-09-20  
util.concurrent 中的原子计数器
0 请登录后投票
   发表时间:2011-09-20  
之前做过这样的设计。。还是需要持久化一下的哈。。
0 请登录后投票
   发表时间:2011-09-20  
JE帐号 写道
weiqiang.yang 写道
调用参数里面还得传一个dao,这有点怪异吧
getNextSerialNumber不一定非得是static的

可以每次取下一个序列的时候都检查一下有没有初始化过,如果没有初始化,那么先从数据库读取数据进行初始化
如果已经初始化了,那么直接返回(内存中保存的数据+1)%LIMIT


 private static volatile boolean INITED = false;
    private static volatile int SN = 0;
    
    private void init(){
        SN = xxxDao.xx(); // 从数据库加载最后一次持久化的值
        INITED = true;
    }
    
    public synchronized String getNextSerialNumber(){
       if(!INITED) init(); 
       SN = (SN + 1) % LIMIT;
       // TODO: 更新数据库
       return String.format("%06d", SN);
    }


可以预见的是大并发下,getNextSerialNumber 会成为系统的一个瓶颈.


不了解lz的并发是多大的并发,如果并发并不是很大,那我觉得getNext...方法加同步是没有问题的,并不是在方法上加同步就一定有瓶颈么 ~

在方法上面加同步,是因为需要恢复重启前的最后一个序列
所以“计算下一个序列”和“持久化到数据库”需要保持原子性

如果能接受流水号不连续的话,那可以考虑楼上有人说到的,先将数据库的值更新为current + N,然后在接下来的N次调用都不更新数据库,用完N个序列之后再将数据库更新为current+N ...
0 请登录后投票
   发表时间:2011-09-21  
不赞同吧方法同步,太消耗性能,你可以试试那些并发的数据结构 ConcurrentHashMap,详见java.util.concurrent。 
0 请登录后投票
   发表时间:2011-09-21  
oracle里面有序列号一说。。。感觉实现这个问题就易如反掌了
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics