关于UUID的标准参考->http://en.wikipedia.org/wiki/Universally_unique_identifier,JAVA1.7 JDK支持其中version3(基于名称)和version4(随机)的UUID生成方式。
Version3
Version 3 UUIDs use a scheme deriving a UUID via MD5 from a URL, a fully qualified domain name, an object identifier, a distinguished name (DN as used in Lightweight Directory Access Protocol), or on names in unspecified namespaces. Version 3 UUIDs have the form xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B.
Version4
Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the version number (4 bits) as well as two reserved bits. All other bits (the remaining 122 bits) are set using a random or pseudorandom data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
where x
is any hexadecimal digit and y
is one of 8
, 9
, A
, or B
(e.g., f47ac10b-58cc-4372-a567-0e02b2c3d479
).
Version4是基于随机数字生成的UUID,因为没有那么大的数据量无法测试生成UUID是否是绝对唯一的,因此考虑使用Version3生成UUID。以下代码也适用于不同机器集群中生成UUID。为了减少UUID生成的时间,可以一次生成多个UUID,并存放到一个UUID缓存中。
package com.company.projects.gis.md5; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.UUID; /** * 采用MD5加密解密 * @author tfq * @datetime 2011-10-13 */ public class UUIDUtil { public static long seed = 0 ; private static String mac = "" ; static{ //获取mac地址 try { NetworkInterface networkInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()); byte[] macByte = networkInterface.getHardwareAddress() ; StringBuffer sb = new StringBuffer(""); for(int i=0; i<macByte.length; i++) { if(i!=0) { sb.append("-"); } //字节转换为整数 String str = Integer.toHexString(macByte[i]&0xff); if(str.length()==1) { sb.append("0"+str); }else { sb.append(str); } } //设置mac地址 mac = sb.toString() ; } catch (Exception e) { e.printStackTrace(); } } /** * 根据mac地址、时间和seed生成UUID,生成UUID是经过MD5加密过的,能够避免生成id连续的UUID * @return */ public synchronized static String getUUID(){ if(seed > Long.MAX_VALUE){ seed = Long.MIN_VALUE ; } ++seed ; UUID uuid = UUID.nameUUIDFromBytes((mac+(System.currentTimeMillis())+seed).getBytes()) ; return uuid.toString() ; } // 测试主函数 public static void main(String args[]) throws UnknownHostException, SocketException { for (int i = 0; i < 10; i++) { System.out.println(getUUID()); } } }
hibernate生成uuid
hibernate提供了多种生成唯一主键标识的方式,其中包括uuid,uuid是由IP+JVM启动时间+当前时间+计数标识来生成的。可以在多机分布式集群和单机集群中生成uuid,Hibernate中生成uuid的类为org.hibernate.id.UUIDHexGenerator。
另参考一种生成有意义的uuid方式。参考地址:http://825635381.iteye.com/blog/2295479
相对于DB自增序列的全局主键生成器,性能更高,同时保留业务需求的业务含义,
对于有分库分表需求的业务同时可以存储分库和分表的信息,对于高并发的互联网企业分库分表生成主键来说是一种很好的方法
- package com.tongbanjie.trade.test.base;
- import java.net.InetAddress;
- import org.apache.commons.lang.StringUtils;
- import com.tongbanjie.commons.util.TSS;
- public class TestGenId {
- public static void main(String[] args) throws Exception {
- /**
- * 项目:交易单分表
- *
- * 需求
- * 查询需求: 1. userId维度
- * 2. 产品维度
- * 3. 商户维度
- * 4. 时间区间维度
- *
- * 预计订单量:
- * 一单平均10000, 一年交易额5000亿, 需要成功订单量 = 500000000000 / 10000 = 50000000 5000万订单
- * 购买加回款应该是1亿订单量, 所以, 单表2000万, 一年需要5张表
- *
- * 最后扩展64库 + 64表, 共64*64 = 4096表, 4096 * 2000万 = 819亿订单够用了, 819亿 * 10000 = 8190000亿 819万亿,够用了
- *
- * 全局唯一主键:
- * 15位时间戳 + 自增序号四位 + 机器后两段IP,6位 + 备用1位 + 分库信息两位 + 分表信息两位 共30位, 回款改造前
- * 15位时间戳 + 自增序号四位 + 机器后两段IP,6位 + 备用3位 + 分库信息两位 + 分表信息两位 共32位, 回款改造后
- *
- * 单JVM支持最多1s 1000 * 9999 = 9999000, 999万9千笔订单,后续还可以扩展。
- *
- * 分库规则:
- * 寻找到数据库 (userId/100) % 64 + 1 找到数据库 订单最多64个库 目前一个库 二分法裂变扩容
- * 分表规则:
- * 寻找到表信息 userId % 64 + 1 找到表信息 一个库最多64个表 目前分8张表 以后二分法裂变扩容
- *
- * 迁移规则:
- * 迁移方案同步写, 目前用动态表名, 以后分表中间件稳定后, 迁移过去
- *
- * 查询改造:
- * 原接口不变,对用户无感知, 底层钩子遍历
- */
- // 只获取本地局域网IP即可
- String ip = InetAddress.getLocalHost().getHostAddress();
- String[] ipArray = ip.split("\\.");
- final String lastTwoPhaseIp = StringUtils.rightPad(ipArray[2], 3, '0')
- + StringUtils.leftPad(ipArray[3], 3, '0');
- for (int i = 0; i < 100000; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- // TSS commons工具类
- String tss = TSS.getTimeStampSequence();
- String id = tss + lastTwoPhaseIp + "000" + "01" + "08";
- System.out.println(id);
- }
- }).start();
- }
- }
- }
- package com.tongbanjie.commons.util;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.apache.commons.lang.StringUtils;
- /**
- * 时间戳序列器<br>
- *
- * 支持同一毫秒最多生成9999笔序列号<br>
- * @author sanfeng
- *
- * 想象力就是生产力
- */
- public class TSS {
- // 默认1个大小
- private static ConcurrentHashMap<String, AtomicInteger> cache = new ConcurrentHashMap<String, AtomicInteger>(1);
- public static String getTimeStampSequence() {
- String timestamp = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date());
- String inc = null;
- AtomicInteger value = cache.get(timestamp);
- if(value == null) {
- cache.clear();
- int defaultStartValue = 0;
- cache.put(timestamp, new AtomicInteger(defaultStartValue));
- inc = String.valueOf(defaultStartValue);
- } else {
- inc = String.valueOf(value.addAndGet(1));
- }
- return timestamp + StringUtils.leftPad(inc, 4, '0');
- }
- }
关于UUID的标准参考->http://en.wikipedia.org/wiki/Universally_unique_identifier,JAVA1.7 JDK支持其中version3(基于名称)和version4(随机)的UUID生成方式。
Version3
Version 3 UUIDs use a scheme deriving a UUID via MD5 from a URL, a fully qualified domain name, an object identifier, a distinguished name (DN as used in Lightweight Directory Access Protocol), or on names in unspecified namespaces. Version 3 UUIDs have the form xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B.
Version4
Version 4 UUIDs use a scheme relying only on random numbers. This algorithm sets the version number (4 bits) as well as two reserved bits. All other bits (the remaining 122 bits) are set using a random or pseudorandom data source. Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
where x
is any hexadecimal digit and y
is one of 8
, 9
, A
, or B
(e.g., f47ac10b-58cc-4372-a567-0e02b2c3d479
).
Version4是基于随机数字生成的UUID,因为没有那么大的数据量无法测试生成UUID是否是绝对唯一的,因此考虑使用Version3生成UUID。以下代码也适用于不同机器集群中生成UUID。为了减少UUID生成的时间,可以一次生成多个UUID,并存放到一个UUID缓存中。
package com.company.projects.gis.md5; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.UUID; /** * 采用MD5加密解密 * @author tfq * @datetime 2011-10-13 */ public class UUIDUtil { public static long seed = 0 ; private static String mac = "" ; static{ //获取mac地址 try { NetworkInterface networkInterface = NetworkInterface.getByInetAddress(InetAddress.getLocalHost()); byte[] macByte = networkInterface.getHardwareAddress() ; StringBuffer sb = new StringBuffer(""); for(int i=0; i<macByte.length; i++) { if(i!=0) { sb.append("-"); } //字节转换为整数 String str = Integer.toHexString(macByte[i]&0xff); if(str.length()==1) { sb.append("0"+str); }else { sb.append(str); } } //设置mac地址 mac = sb.toString() ; } catch (Exception e) { e.printStackTrace(); } } /** * 根据mac地址、时间和seed生成UUID,生成UUID是经过MD5加密过的,能够避免生成id连续的UUID * @return */ public synchronized static String getUUID(){ if(seed > Long.MAX_VALUE){ seed = Long.MIN_VALUE ; } ++seed ; UUID uuid = UUID.nameUUIDFromBytes((mac+(System.currentTimeMillis())+seed).getBytes()) ; return uuid.toString() ; } // 测试主函数 public static void main(String args[]) throws UnknownHostException, SocketException { for (int i = 0; i < 10; i++) { System.out.println(getUUID()); } } }
hibernate生成uuid
hibernate提供了多种生成唯一主键标识的方式,其中包括uuid,uuid是由IP+JVM启动时间+当前时间+计数标识来生成的。可以在多机分布式集群和单机集群中生成uuid,Hibernate中生成uuid的类为org.hibernate.id.UUIDHexGenerator。
另参考一种生成有意义的uuid方式。参考地址:http://825635381.iteye.com/blog/2295479
相对于DB自增序列的全局主键生成器,性能更高,同时保留业务需求的业务含义,
对于有分库分表需求的业务同时可以存储分库和分表的信息,对于高并发的互联网企业分库分表生成主键来说是一种很好的方法
- package com.tongbanjie.trade.test.base;
- import java.net.InetAddress;
- import org.apache.commons.lang.StringUtils;
- import com.tongbanjie.commons.util.TSS;
- public class TestGenId {
- public static void main(String[] args) throws Exception {
- /**
- * 项目:交易单分表
- *
- * 需求
- * 查询需求: 1. userId维度
- * 2. 产品维度
- * 3. 商户维度
- * 4. 时间区间维度
- *
- * 预计订单量:
- * 一单平均10000, 一年交易额5000亿, 需要成功订单量 = 500000000000 / 10000 = 50000000 5000万订单
- * 购买加回款应该是1亿订单量, 所以, 单表2000万, 一年需要5张表
- *
- * 最后扩展64库 + 64表, 共64*64 = 4096表, 4096 * 2000万 = 819亿订单够用了, 819亿 * 10000 = 8190000亿 819万亿,够用了
- *
- * 全局唯一主键:
- * 15位时间戳 + 自增序号四位 + 机器后两段IP,6位 + 备用1位 + 分库信息两位 + 分表信息两位 共30位, 回款改造前
- * 15位时间戳 + 自增序号四位 + 机器后两段IP,6位 + 备用3位 + 分库信息两位 + 分表信息两位 共32位, 回款改造后
- *
- * 单JVM支持最多1s 1000 * 9999 = 9999000, 999万9千笔订单,后续还可以扩展。
- *
- * 分库规则:
- * 寻找到数据库 (userId/100) % 64 + 1 找到数据库 订单最多64个库 目前一个库 二分法裂变扩容
- * 分表规则:
- * 寻找到表信息 userId % 64 + 1 找到表信息 一个库最多64个表 目前分8张表 以后二分法裂变扩容
- *
- * 迁移规则:
- * 迁移方案同步写, 目前用动态表名, 以后分表中间件稳定后, 迁移过去
- *
- * 查询改造:
- * 原接口不变,对用户无感知, 底层钩子遍历
- */
- // 只获取本地局域网IP即可
- String ip = InetAddress.getLocalHost().getHostAddress();
- String[] ipArray = ip.split("\\.");
- final String lastTwoPhaseIp = StringUtils.rightPad(ipArray[2], 3, '0')
- + StringUtils.leftPad(ipArray[3], 3, '0');
- for (int i = 0; i < 100000; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- // TSS commons工具类
- String tss = TSS.getTimeStampSequence();
- String id = tss + lastTwoPhaseIp + "000" + "01" + "08";
- System.out.println(id);
- }
- }).start();
- }
- }
- }
- package com.tongbanjie.commons.util;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.apache.commons.lang.StringUtils;
- /**
- * 时间戳序列器<br>
- *
- * 支持同一毫秒最多生成9999笔序列号<br>
- * @author sanfeng
- *
- * 想象力就是生产力
- */
- public class TSS {
- // 默认1个大小
- private static ConcurrentHashMap<String, AtomicInteger> cache = new ConcurrentHashMap<String, AtomicInteger>(1);
- public static String getTimeStampSequence() {
- String timestamp = new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date());
- String inc = null;
- AtomicInteger value = cache.get(timestamp);
- if(value == null) {
- cache.clear();
- int defaultStartValue = 0;
- cache.put(timestamp, new AtomicInteger(defaultStartValue));
- inc = String.valueOf(defaultStartValue);
- } else {
- inc = String.valueOf(value.addAndGet(1));
- }
- return timestamp + StringUtils.leftPad(inc, 4, '0');
- }
- }