• 0
  • 0
分享
  • 根据关键字生成唯一顺序号的两种方式——软件测试圈
  • 曼倩诙谐 2021-08-05 10:34:50 字数 3523 阅读 674 收藏 0

  在项目开发中,经常遇到根据给定关键字生成系统唯一顺序号的场景,本文整理了两种不同的实现方式。

  1. 通过数据库加锁方式生成顺序号

  该方案主要通过对数据库中表记录的加锁读写来实现的,该表中的记录对应不同关键字的顺序号生成信息,并且,为了提高生成顺序号的效率,可以一次生成指定步长个数的顺序号并存入本地缓存中。

  该方案首先需要在数据库建立用于生成顺序号的表SEQUENCE_NUMBER,表结构如表1所示:

1.png


表1 表结构

  相应的,定义该表对应的Domain:

public class SequenceNumberDomain{ 
private keyName;//关键字名 
private currentKey; //当前值 
private maxKey; //最大值 
private step; //步长 
//省略get和set方法
}

  其次,需要定义用于存储顺序号生成信息的本地缓存,本地缓存可以通过定义CouncurrentHashMap实现。

  至此,所有的准备工作都已完成,可以进行顺序号的生成了,生成顺序号的主要伪代码如下:

ConcurrentHashMap<String, SequenceNumberDomain> localCache = new ConcurrentHashMap();

  该方法的优点是可以一次生成step个顺序号存入缓存,减少了对数据库的访问次数,而且缓存中只存储了step个顺序号,即使缓存数据丢失,系统最多损失掉step个顺序号,不会导致生成重复的顺序号或者丢失太多顺序号。但是,该方案也具有缺点,首先步长难以确定,步长的设置和系统的并发量有关,若步长太短,且会频繁的访问数据库,降低生成顺序号的效率,若步长过长,则缓存丢失会损失较多顺序号;其次,如果系统是分布式部署,不同服务只能访问自己的数据库,若要生成指定关键字的全系统唯一顺序号,则可能需要通过rpc接口、分布式事务等方式等从表中分别取一定数量且不交叉的顺序号存入各自的本地缓存,实现较为复杂。


  2. 通过redis生成顺序号

  若系统部署了redis服务,则顺序号也可以借助redis的incr命令实现,incr命令是将key中储存的数字值加一,如果key不存在,key的值会先被初始化为0,然后再执行incr操作,而且incr命令为原子操作,不会产生并发问题。

  该方案的伪代码如下:

//key 待生成顺序号的关键字,step 步长
pubilic long getSequenceNumber(Stirng key, int step){ 
//开启事务(代码省略) 
//从本地缓存中获取该key对应的SequenceNumberDomain,若本地缓存没有,则创建新的SequenceNumberDomain放入本地缓存。
SequenceNumberDomain sequenceNumberDomain= localCache.get(key); 
if(sequenceNumberDomain == null){ 
//selectForUpdate 通过 select * from SEQUENCE_NUMBER for update 从数据库查询表中当前key的信息; 
sequenceNumberDomain = SequenceNumberDAO.selectForUpdate(key);   //sequenceNumberDomain为空,说明数据库中SEQUENCE_NUMBER没有这个关键字的记录,需要把该记录插入进去 
if(sequenceNumberDomain==null){ 
sequenceNumberDomain = new SequenceNumberDomain(); 
sequenceNumberDomain.setKeyName(key);     sequenceNumberDomain.setCurrentKey(step + 1);      sequenceNumberDomain.setStep(step);     SequenceNumberDAO.SequenceNumberDAO.insert(sequenceNumberDomain); //将sequenceNumberDomain设置好相关信息存入缓存,由于数据库中没有该关键字的记录,所以缓存中当前值为1,且最大值为step,相当于缓存中存储了从1到step的顺序号。
sequenceNumberDomain.setCurrentKey(1);      sequenceNumberDomain.setMaxKey(step);     sequenceNumberDomain.setStep(step);
localCache.push(key, sequenceNumberDomain); 
//将生成的顺序号返回 
return 1; 
}else { 
//如果数据库中有该关键字对应的记录,则根据该记录生成顺序号,同时更新该记录信息 
long sequenceNumber = sequenceNumberDomain.getCurrentKey();   sequenceNumberDomain.setCurrentKey(sequenceNumber + step);   sequenceNumberDomain.setStep(step);   SequenceNumberDAO.SequenceNumberDAO.update(sequenceNumberDomain); 
//设置缓存中能获取到的顺序号的最大值   sequenceNumberDomain.setMaxKey(sequenceNumber + step - 1);   localCache.push(key, sequenceNumberDomain); 
return sequenceNumber;
 } 
} else {
 //如果缓存中可以获取到该key对应的sequenceNumberDomain且需要的顺序号不超过缓存中的最大顺序号,则直接通过缓存生成顺序号,若需要的顺序号超过缓存中的最大顺序号,则需要从数据库获取该key对应的记录,根据数据库中的信息生成顺序号 
if(sequenceNumberDomain.getCurrentKey()+1<=sequenceNumberDomain.getMaxKey()){ 
long sequenceNumber = sequenceNumberDomain.getCurrentKey()+1 ;   sequenceNumberDomain.setCurrentKey(
sequenceNumberDomain.getCurrentKey()+1);
localCache.push(key,sequenceNumberDomain); 
}else{ 
sequenceNumberDomain = SequenceNumberDAO.selectForUpdate(key);
long sequenceNumber = sequenceNumberDomain.getCurrentKey();   sequenceNumberDomain.setCurrentKey(sequenceNumber + step);   sequenceNumberDomain.setStep(step);   SequenceNumberDAO.SequenceNumberDAO.update(sequenceNumberDomain); 
//设置缓存中能获取到的顺序号的最大值   sequenceNumberDomain.setMaxKey(sequenceNumber + step - 1);     localCache.push(key, sequenceNumberDomain); 
return sequenceNumber;
} 
} 
//结束事务 代码省略 
}

  该方案实现简单,且多个微服务可以访问同一个redis服务,不需要通过rpc接口即可根据关键字生成全系统唯一的顺序号,而且由于redis存取速度非常快,所以即使每次只生成一个顺序号,该方案的效率也会非常高。但是该方案的缺点在于如果redis宕机,则数据可能由于没有及时备份而与磁盘上的数据不一致,导致redis重启后生成的顺序号重复。



作者:王欢   

来源:51Testing软件测试网原创

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   其实,仔细想一想不管是现在的互联网时代还是将来的人工智能时代的一些产品都需要保证它的稳定性和健康度,这就离不开我们测试人员,而且,需求会越来越多。  从boos直聘、猎聘等招聘平台你也可以看出来现在互联网公司只要有自己的产品的都会招聘测试人员,目前高级软件测试工程师的工资基本和开发人员持平。不说废话,直接上图:  (这张图是我刚查的boos直聘上的测试开发岗位的需求量,有好几百条这样的岗位需求。)  (这张图是猎聘网上的招聘需求,又是好几百条,都翻不过来,你还说没有前景吗?)  目前测试开发人员缺口在三十万左右,是急需测试人员的。所以,第一个担忧完全没必要,肯定是有好的前景。  与其说前景...
            0 0 574
            分享
          •   前言  什么是自动化测试  把人对软件的测试行为转化为由机器执行测试行为的一种实践。  例如GUI自动化测试,模拟人去操作软件界面,把人从简单重复的劳动中解放出来。  本质是用代码去测试另一段代码,属于一种软件开发工作,已经开发完成的用例还必须随着被测试对象的改变而更新,因此,还有额外的维护成本。  自动化测试有哪些分类  按测试目的分类:  · 功能自动化测试  · 性能自动化测试  按测试对象分类:  · Web应用测试  · APP测试  · 接口测试  · 单元测试  为什么需要自动化测试  可以替代大量的手工机械重复性操作,测试工...
            0 0 633
            分享
          • 1.HTTP协议与TCP/IP协议的关系HTTP的长连接和短连接本质上是TCP长连接和短连接。HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议。IP协议主要解决网络路由和寻址问题,TCP协议主要解决如何在IP层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP有可靠,面向连接的特点。2.如何理解HTTP协议是无状态的HTTP协议是无状态的,指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。HTTP是一个无状态的面向连接的协议,无状态不代表...
            0 0 1316
            分享
          • 北京时间9月9日早间消息,据报道,索尼指控微软在关于《使命召唤》可以继续支持PlayStation游戏机的问题上对游戏行业和监管者形成误导。在微软宣布斥资750亿美元收购动视暴雪后,这家软件巨头曾经承诺,动视暴雪开发的《使命召唤》系列游戏将会继续支持索尼的PlayStation游戏机。但索尼互动娱乐CEO吉姆·瑞恩(JimRyan)表示,虽然微软“承诺”将同时在PlayStation和微软自家的Xbox游戏机上发布未来版本的《使命召唤》游戏,但实际上,微软只会让这款游戏在PlayStation上保留有限的几年。英国竞争和市场管理局(CMA)上周威胁称,他们将对微软展开深入调查。而其他地区的监管...
            0 0 832
            分享
          •   软件缺陷管理  软件缺陷-概念  软件缺陷-基本概念主要分为:缺陷、故障、失效。  ·缺陷(Defect):以静态形式存在于软件内部,相当于BUG;  · 故障(Fault):软件运行中出现的状态,不处理可能会失效,以动态形式存在;  · 失效(Failure):软件运行时产生的外部异常行为结果,与用户需求不一致。  缺陷不一定导致故障,故障也不一定会失效;缺陷导致故障,有可能导致失效,也有可能不会导致失效。  软件缺陷-定义  软件未达到产品说明书中已标明的功能软件未达到产品说明书中虽未指出但应达到的目标软件出现产品说明书中指明不会出现的错误软件功能超出了产品说明书中...
            0 0 813
            分享
      • 51testing软件测试圈微信