获取id 的一种策略

标签: id 策略 | 发表时间:2015-07-07 17:48 | 作者:uule
出处:http://www.iteye.com

从数据库中批量(step个)拿出Id,然后使用,待消耗完后再批量拿出Id

 

key1 = genKey+"##"+subKey;

 

三个并发Map:

mapLock:判断是否存在锁

mapGenId:保存每次的currNo值

mapMaxId:保存每次currNo+step后的值

三个ConcurrentHashMap的key都为 key1

 

大体思路:

一、由genKey、subKey构建一个锁,获取Id值时先判断是否存在锁,当不存在锁时,先初始化

 

初始化:

从数据库中取出currNo

1、若currNo为0(第一次)

将当前currNo设为0并放入mapGenId中

表中插入一条currNo为step的记录,返回MaxId的值为step

  

2、若currNo不为0

将当前currNo设为数据库中查出的currNo,并放入mapGenId中

将数据库中查出的currNo,加上step后更新,返回MaxId的值为currNo+step

 

 

将MaxId置入保存最大值的并发map mapMaxId中

再生成key为key1的锁

 

二、初始化完成后,取得锁(若已存在锁,则取出并锁住),从mapGenId、mapMaxId中分别取得当前currNo值和当前最大MaxId值

 

若 mapGenId中取出的值(当前currNo值)<= mapMaxId中的值(每次的最大currNo值)时,说明上次取得 step个Id还没有消耗完,所以直接当前currNo+1即可,否则说明

已消耗完,需要重新从数据库中取数据

 

三、若需要重新取数据,则跟初始化时currNo不为0的情况相同

 加上 step后更新数据库,然后分别设置mapGenId、mapMaxId即可

 

 DB:



  

 

使用:

long lPointId = HttpUtil.getId("testSQL","userPoint");

public static Long getId(String genKey,String subKey){
		Long lId = 0l;		
		lId = GetTableId.getCurrentId(genKey, subKey);
		return lId;
	}

 

public class GetTableId {
	static Logger logger = Logger.getLogger(GetTableId.class);
	
	
	/**
	 * 获取ID
	 * @param genKey
	 * @param subKey
	 * @return
	 */
	public static Long getCurrentId(String genKey,String subKey){
		GenId genIdVo = new GenId();
		genIdVo.setGenKey(genKey);
		genIdVo.setSubKey(subKey);
		
		Long currNo = getGenId(genIdVo);
		return currNo;
	}
	
	
	public static Long getGenId(GenId genIdVo){
		String subKey = genIdVo.getSubKey();

		String genKey = genIdVo.getGenKey();
		
		if (!GenIdHelper.getInstance().containsKey(genKey, subKey)) {
			initGenIdByDB(genIdVo);  //初始化ID hash
		}
		synchronized (GenIdHelper.getInstance().getLockObject(genKey, subKey)) {
			
			Long currNo = getGenIdByCache(genKey, subKey);
			
			logger.info("return currNo = " + currNo);
			
			return currNo;

		}

	}

	/**
	 * 第一次加载
	 */
	private static void initGenIdByDB(GenId genIdVo) {
		String subKey = genIdVo.getSubKey();

		String genKey = genIdVo.getGenKey();

		logger.info("genKey = " + genKey + ": subKey = " + subKey);

		GenId vo = getGenIdLock(genKey, subKey);

		if (GenIdHelper.getInstance().containsKey(genKey, subKey)) {

			logger.info("other thread is return" + genKey + ":" + subKey);

			return;
		}

		Long step = GenIdConstant.GenIdStep;

		Long currNo = 0L;

		if (vo.getCurrNo() == 0) {

			currNo = insertGenId(genKey, subKey, step);

			logger.info("no live frist is currNo =" + currNo);

			GenIdHelper.getInstance().setGenIDValue(genKey, subKey, new Long(0));

		} else {

			updateGenIdVO(genKey, subKey);
			
			currNo = vo.getCurrNo() + GenIdHelper.getInstance().getGenIdSetp(genKey, subKey);

			GenIdHelper.getInstance().setGenIDValue(genKey, subKey, vo.getCurrNo());

			logger.info("live frist is currNo =" + currNo);

		}

		setGenIdCacheMap(genKey, subKey, currNo);

		GenIdHelper.getInstance().setLockObject(genIdVo.getGenKey(),genIdVo.getSubKey());

	}
	
	/**
	 * 设置CACHEMAP里的值
	 * 
	 * @param genKey
	 * @param subKey
	 * @param currNo
	 */
	private static void setGenIdCacheMap(String genKey, String subKey, Long currNo) {

		logger.info("setGenIdCacheMap genKey = " + genKey + ": subKey = " + subKey + ": currNo = " + currNo);

		GenIdHelper.getInstance().setMaxID(genKey, subKey, currNo);
	}
	
	/**
	 * 如果数据库不存在就插入
	 * 
	 * @param genKey
	 * @param subKey
	 * @param step
	 * insert into genId (currNo, genKey, subKey, updateTime) values
				({currNo}, {genKey}, {subKey}, now());
	 */
	private static Long insertGenId(String genKey, String subKey, Long step) {		
		CDO cdoRequest = new CDO();
		CDO cdoResponse = new CDO();
		cdoRequest.setStringValue(ITransService.SERVICENAME_KEY,"CommonService");
		cdoRequest.setStringValue(ITransService.TRANSNAME_KEY, "insertGenId");
		cdoRequest.setStringValue("subKey", subKey);
		cdoRequest.setStringValue("genKey", genKey);
		cdoRequest.setLongValue("currNo", step);
		Return ret = getServiceBus().handleTrans(cdoRequest, cdoResponse);
		if (ret.getCode() != 0) {
			logger.error("insertGenId error.");
		}

		return step;
	}
	
	/**
	 * 从数据库里取的时候先锁住这条记录
	 * 
	 * @param genKey
	 * @param subKey
	 * @return
	 */
	private static GenId getGenIdLock(String genKey, String subKey) {
		long lId = 0;
		logger.info("islock genKey = " + genKey + ": subKey = " + subKey);

		GenId genIdVo = new GenId();

		genIdVo.setSubKey(subKey);

		genIdVo.setGenKey(genKey);
		
		CDO cdoRequest = new CDO();
		CDO cdoResponse = new CDO();
		
		cdoRequest.setStringValue(ITransService.SERVICENAME_KEY,"CommonService");
		cdoRequest.setStringValue(ITransService.TRANSNAME_KEY, "getGenId");
		cdoRequest.setStringValue("subKey", subKey);
		cdoRequest.setStringValue("genKey", genKey);
		Return ret = getServiceBus().handleTrans(cdoRequest, cdoResponse);
		if (ret.getCode() == 0) {
			if(cdoResponse.exists("cdoGenId")){
				lId = cdoResponse.getLongValue("cdoGenId");
			}
		}
		genIdVo.setCurrNo(lId);
		return genIdVo;
	}
	
	/**
	 * 更新数据的操作
	 * 
	 * @param subKey
	 * @param genKey
	 * @param currNo
	 * update genId  set currNo = currNo + {currNo},updateTime=now() 
				where genKey = {genKey} and subKey = {subKey}
	 */
	private static void updateGenIdVO(String genKey, String subKey) {
		logger.info("updateGenIdVO genKey = " + genKey + ": subKey = " + subKey);

		long currNo = GenIdConstant.GenIdStep;

		CDO cdoRequest = new CDO();
		CDO cdoResponse = new CDO();
		cdoRequest.setStringValue(ITransService.SERVICENAME_KEY,"CommonService");
		cdoRequest.setStringValue(ITransService.TRANSNAME_KEY, "updateGenIdByPk");
		cdoRequest.setStringValue("subKey", subKey);
		cdoRequest.setStringValue("genKey", genKey);
		cdoRequest.setLongValue("currNo", currNo);
		Return ret = getServiceBus().handleTrans(cdoRequest, cdoResponse);
		if (ret.getCode() != 0) {
			logger.error("updateGenIdByPk error.");
		}

	}
	
	/**
	 * 比较当前值和最大值,返回自增ID
	 * @param genKey
	 * @param subKey
	 * @return
	 */
	private static Long getGenIdByCache(String genKey, String subKey) {
		
		Long maxId = GenIdHelper.getInstance().getMaxID(genKey, subKey)	;
		
		logger.info("getGenIdByCache maxId = " + maxId);
		
		Long currNo = GenIdHelper.getInstance().getGenIDValue(genKey, subKey);
		
		logger.info("currNo maxId = " + currNo);
		
		if(currNo >= maxId){
			
			GenId genIdVo = new GenId();
			
			genIdVo.setGenKey(genKey);
			
			genIdVo.setSubKey(subKey);
			
			setGenIdByDB(genIdVo);
		
		}
		
		GenIdHelper.getInstance().setGenIDValue(genKey, subKey, currNo + 1);

		return currNo + 1;
	}
	
	
	/**
	 * 如果大于最大值就从数据库里取
	 */
	private static void setGenIdByDB(GenId genIdVo) {

		String subKey = genIdVo.getSubKey();

		String genKey = genIdVo.getGenKey();

		logger.info("genKey = " + genKey + ": subKey = " + subKey);

		GenId vo = getGenIdLock(genKey, subKey);
		
		updateGenIdVO(genKey, subKey);

		Long currNo = vo.getCurrNo() + GenIdConstant.GenIdStep;

		logger.info("setGenIdByDB currNo = " + currNo);

		setGenIdCacheMap(genKey,subKey, currNo);

	}

}

 

public class GenId {
	
	private Long genId;
	
	private Long currNo;
	
	private String genKey;
	
	private String subKey;
        ...
}

 

public class GenIdConstant {

	public final static String SPLITFLAG = "##";
	
	public final static Long GenIdStep  = new Long(100);
	
	public final static String RESULTTYPEKEYNAME  = "resultCode";
	
	public final static String RESULTCURRNONAME  = "currNo";

	
}

 

处理并发工具类:

public class GenIdHelper {
	
	//每条记录的线程锁
	private static ConcurrentHashMap<String, Object> mapLock = new ConcurrentHashMap<String, Object>();
	
	//每个记录的步长
	private static ConcurrentHashMap<String, Long> mapStep = new ConcurrentHashMap<String, Long>();
	
	//每个记录的ID值
	private static ConcurrentHashMap<String, Long> mapGenId = new ConcurrentHashMap<String, Long>();
	
	//每个记录的MAXID值
	private static ConcurrentHashMap<String, Long> mapMaxId = new ConcurrentHashMap<String, Long>();
	
	/*
	 * 懒汉,线程安全
	 */
	private static GenIdHelper cc = null;
	
	private GenIdHelper(){}
	
	public static synchronized GenIdHelper getInstance() {
		if (cc == null){		
			cc = new GenIdHelper();
		}
		return cc;
	}
	
	/**
	 * 设置每个自增ID的线程锁对象
	 * 
	 */	
	protected  void setLockObject(String genKey, String subKey){
		
		mapLock.put(buildLockKey(genKey, subKey), new Object());
	}
	
	/**
	 * 设置每个自增ID的线程锁对象
	 * 
	 */	
	protected Object getLockObject(String genKey, String subKey){
		
		return mapLock.get(buildLockKey(genKey, subKey));
	}
	
	/**
	 * 设置每个自增ID的最大值
	 * 
	 */	
	protected  void setMaxID(String genKey, String subKey, Long maxId){
		
		mapMaxId.put(buildLockKey(genKey, subKey), maxId);
	}
	
	/**
	 * 获取自增ID的最大值
	 * 
	 */	
	protected  Long getMaxID(String genKey, String subKey){
		
		return mapMaxId.get(buildLockKey(genKey, subKey));
	}
	
	/**
	 * 获取自增ID的值
	 * 
	 */	
	protected  Long getGenIDValue(String genKey, String subKey){
		
		return mapGenId.get(buildLockKey(genKey, subKey));
	}
	
	/**
	 * 设置获取自增ID的值
	 * 
	 */	
	protected void setGenIDValue(String genKey, String subKey, Long currNo){
		
		mapGenId.put(buildLockKey(genKey, subKey), currNo);
	}
	
	/**
	 * 判断是否存在线程锁对象
	 * @param key
	 */	
	protected  boolean containsKey(String genKey, String subKey){
		
		return mapLock.containsKey(buildLockKey(genKey, subKey));
	}
	
	private  String buildLockKey(String genKey, String subKey){
		
		return genKey + GenIdConstant.SPLITFLAG + subKey;
	}
	
	protected Long getGenIdSetp(String genKey, String subKey){
		
		Long step = mapStep.get(buildLockKey(genKey, subKey));
		
		if(step == null){
			
			step = GenIdConstant.GenIdStep;
		}
		
		return step;
		
	}

}

 。。。

 

 

 

 

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [id 策略] 推荐:

获取id 的一种策略

- - 企业架构 - ITeye博客
从数据库中批量(step个)拿出Id,然后使用,待消耗完后再批量拿出Id. mapLock:判断是否存在锁. mapGenId:保存每次的currNo值. mapMaxId:保存每次currNo+step后的值. 三个ConcurrentHashMap的key都为 key1. 一、由genKey、subKey构建一个锁,获取Id值时先判断是否存在锁,当不存在锁时,先初始化.

产生Id

- - 研发管理 - ITeye博客
// worker编号最大值,决定支持的部署节点数量. // 毫秒内自增位数,每毫秒最大序号支持65535. // worker编号偏移量. // 毫秒基线:2015-01-01 00:00:00. * 从环境变量中获取worker编号,每个部署环境编号不能重复. * 每个部署环境编号不能重复. * @param workerId Worker编号.

id Software发布《狂怒(Rage)》

- ArmadilloCommander - Solidot
id Software发布了容量为21GB的第一人称射击游戏《狂怒(Rage)》. 游戏基于id Tech 5引擎,背景是世界末日后的未来. 目前对它的评价好坏参半,媒体综合评分80左右,玩家评分相似或更低. 在游戏中,玩家将扮演一位小行星Apophis撞击地球后的幸存者. 在灾难发生前,全世界展开合作将包括科学家在内的精英冰冻在地下,以在灾难后重建地球.

小米手机ID简介

- miyizs - Billwang 工业设计
      小米手机是小米公司(全称北京小米科技有限责任公司)专为发烧友级手机控打造的 一款高品质智能手机. 下面我们将对其做一个简单的介绍.       小米手机的外观设计走的是简约内敛路线,直板加圆润的边角让其显得简单清爽. 小米手机配置了,1.5GHz双核处理器、1G RAM、4英寸夏普屏、800万像素摄像头以及大容量电池.

标签?ID?还是CLASS?

- - 前端观察
想谈一下几个基本的HTML问题,都是围绕着应该怎样使用HTML. 多用有语义的标签,少用div和span,避免使用没有class的div和span. 设想一下HTML的世界最初只有div和span这两个标签,其实网页依然可以写得出来. 更多标签的出现,其实是为了替代利用率高但不好书写的 
 和  来的.

设备id那些事

- - 算法之道
随着用户隐私关注度越来越高,搜广推以imei收集用户数据的方式开始被要求整改,首先是客户端不会上传imei,那么追踪用户的唯一标识没有了,那后续该怎么做. 其实除了这个唯一标识符问题,还有一些应用设置项:出现了允许关闭推荐的选项,致命一击. 先了解一下现在移动终端可以收集哪些 id. 不可逆、唯一性、不可篡改、一致性.

Linuxer:制作自己的Linux ID Card吧

- rex - Wow! Ubuntu
Super Boot Manager的作者Alessandro Lanave,又为Linuxer带来了一个web程序,制作Linux ID Card ,Card效果如图. 可以把ID Card做为论坛签名,博客签名,任何你需要的地方. 当然,如果觉得没有自己喜欢的发行版的模板,可以向Alessandro Lanave提交哦,.

两个 Apple ID 是很有必要的

- 闷闲居士 - Page to Page
刚买iPad的时候注册的是中国区的Apple ID,绑定了自己的信用卡. 可当搜索下载Kindle for iPad的时候才觉得,注册一个美国区的Apple ID还是很有必要的,否则很多中国区没有的App无法下载. 大致学习了2篇注册美国区ID的帖子,赶紧动手,可是还是因为疏忽产生了问题,提示我说“我的邮箱地址已经注册了”.

全局唯一ID生成方案

- - 标点符
在实现大型分布式程序时,通常会有全局唯一ID生成的需求,用来对每一个对象标识一个代号. 另外,业务层对于全局唯一ID生成也有要求:. 全局唯一性:不能出现重复的ID号. 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能.

用户体系搭建之ID-Mapping

- - 标点符
在推进用户画像和风险控制时,遇到的最大的问题是用户身份信息的混乱:. 相同用户,不同渠道下账号不相同,如微信小程序和APP. 同个用户,在不同的设备商登录. ID-Mapping是大数据分析中非常基本但又关键的环节,ID-Mapping通俗的说就是把几份不同来源的数据,通过各种技术手段识别为同一个对象或主题,例如同一台设备(直接),同一个用户(间接),同一家企业(间接)等等,可以形象地理解为用户画像的“拼图”过程.