当前访客身份:游客 [ 登录 | 加入 OSCHINA ]

代码分享

当前位置:
代码分享 » Java  » 数据库编程
歇蹩虎子

数据库唯一主键生成策略

歇蹩虎子 发布于 2014年03月22日 14时, 5评/2220阅
分享到: 
收藏 +0
2
预先生成数据库主键, 方便数据库扩展.
标签: <无>

代码片段(1) [全屏查看所有代码]

1. [文件] IdWorker.java ~ 4KB     下载(13)     跳至 [1] [全屏预览]



import java.net.InetAddress;

/**
 * 在分布式系统中,需要生成全局UID的场合还是比较多的,twitter的snowflake解决了这种需求,
 * 实现也还是很简单的,除去配置信息,核心代码就是毫秒级时间41位+机器ID 10位+毫秒内序列12位。
 * 该项目地址为:https://github.com/twitter/snowflake是用Scala实现的。
 * python版详见开源项目https://github.com/erans/pysnowflake。
 * @author   xiqiao
 * @Date	 2013-12-19 	 
 */
public class IdWorker {
	//根据具体机器环境提供
	private final long workerId;
	//滤波器,使时间变小,生成的总位数变小,一旦确定不能变动
	private final static long twepoch = 1361753741828L;
	private long sequence = 0L;
	private final static long workerIdBits = 10L;
	private final static long maxWorkerId = -1L ^ -1L << workerIdBits;
	private final static long sequenceBits = 12L;

	private final static long workerIdShift = sequenceBits;
	private final static long timestampLeftShift = sequenceBits + workerIdBits;
	private final static long sequenceMask = -1L ^ -1L << sequenceBits;

	private long lastTimestamp = -1L;
	//根据主机id获取机器码
	private static IdWorker worker = new IdWorker();

	/**
	 * 创建 IdWorker对象.
	 * @Deprecated 请调用静态方法getId()
	 * @param workerId
	 */
	@Deprecated
	public IdWorker(final long workerId) {
		if (workerId > IdWorker.maxWorkerId || workerId < 0) {
			throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",
					IdWorker.maxWorkerId));
		}
		this.workerId = workerId;
	}

	public IdWorker() {
		this.workerId = getAddress() % (IdWorker.maxWorkerId + 1);
	}

	public static long getId() {
		return worker.nextId();
	}

	public synchronized long nextId() {
		long timestamp = this.timeGen();
		if (this.lastTimestamp == timestamp) {
			this.sequence = (this.sequence + 1) & IdWorker.sequenceMask;
			if (this.sequence == 0) {
				//System.out.println("###########" + sequenceMask);//等待下一毫秒
				timestamp = this.tilNextMillis(this.lastTimestamp);
			}
		} else {
			this.sequence = 0;
		}
		if (timestamp < this.lastTimestamp) {
			try {
				throw new Exception(String.format(
						"Clock moved backwards.  Refusing to generate id for %d milliseconds", this.lastTimestamp
								- timestamp));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		this.lastTimestamp = timestamp;
		long nextId = ((timestamp - twepoch << timestampLeftShift)) | (this.workerId << IdWorker.workerIdShift)
				| (this.sequence);
		//  System.out.println("timestamp:" + timestamp + ",timestampLeftShift:"
		//	    + timestampLeftShift + ",nextId:" + nextId + ",workerId:"
		//	    + workerId + ",sequence:" + sequence);
		return nextId;
	}

	private long tilNextMillis(final long lastTimestamp1) {
		long timestamp = this.timeGen();
		while (timestamp <= lastTimestamp1) {
			timestamp = this.timeGen();
		}
		return timestamp;
	}

	private static long getAddress() {
		try {
			String currentIpAddress = InetAddress.getLocalHost().getHostAddress();
			String[] str = currentIpAddress.split("\\.");
			StringBuilder hardware = new StringBuilder();
			for (int i = 0; i < str.length; i++) {
				hardware.append(str[i]);
			}
			return Long.parseLong(hardware.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}

		return 2L;
	}

	private long timeGen() {
		return System.currentTimeMillis();
	}

	public static void main(final String[] args) {
		//IdWorker worker2 = new IdWorker(0);
		//System.out.println(worker2.nextId());
		//long ll = getAddress() % 16;
		//System.out.println(ll);
		long start = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
			getId();
		}
		long end = System.currentTimeMillis();
		System.out.println((100000 / (end - start)) + "个/ms");
		System.out.println(getId());
	}

}


开源中国-程序员在线工具:Git代码托管 API文档大全(120+) JS在线编辑演示 二维码 更多»

发表评论 回到顶部 网友评论(5)

  • 1楼:V 发表于 2014-03-22 17:46 回复此评论
    哥们,你这想得也太想当然了吧,一百个并发同时生成时,你不完蛋了
  • 2楼:歇蹩虎子 发表于 2014-03-23 16:03 回复此评论

    引用来自“V”的评论

    哥们,你这想得也太想当然了吧,一百个并发同时生成时,你不完蛋了
    最大支持10000个并发同时生成,100个应该没问题. 前提是一万个并发获取的time是相同的,后面的四位数必须完成一圈,才会出现冲突.
  • 3楼:lovedreamland 发表于 2014-03-25 11:26 回复此评论
    UUID.randomUUID()
  • 4楼:歇蹩虎子 发表于 2014-03-26 12:56 回复此评论

    引用来自“lovedreamland”的评论

    UUID.randomUUID()
    UUID是不能根据插入时间的先后进行排序的, 除非自己写一个uuid的算法
  • 5楼:V 发表于 2014-04-19 09:24 回复此评论

    引用来自“歇蹩虎子”的评论

    引用来自“lovedreamland”的评论

    UUID.randomUUID()
    UUID是不能根据插入时间的先后进行排序的, 除非自己写一个uuid的算法

    那你就UUID串跟下时间串不就结了,其它不用多想,费事还不讨好

开源从代码分享开始 分享代码
歇蹩虎子的其它代码 全部(4)...