介绍:
相对于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 org.apache.commons.lang.StringUtils; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; /** * 时间戳序列器<br> * * 支持同一毫秒最多生成9999笔序列号<br> * @author sanfeng * * 想象力就是生产力 */ public class TSS { // 默认1个大小 private static HashMap<String, AtomicInteger> tssCache = new HashMap<String, AtomicInteger>(1); private static final ReentrantLock lock = new ReentrantLock(); // 因为有锁,所以是变成了线程安全的,省去每次 new 的消耗,耗时降低约一半 private static final SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS"); public static String getTimeStampSequence() { String timestamp = null; String inc = null; lock.lock(); try { timestamp = sdf.format(new Date()); AtomicInteger value = tssCache.get(timestamp); if(value == null) { tssCache.clear(); int defaultStartValue = 0; tssCache.put(timestamp, new AtomicInteger(defaultStartValue)); inc = String.valueOf(defaultStartValue); } else { inc = String.valueOf(value.addAndGet(1)); } } finally { lock.unlock(); } return timestamp + StringUtils.leftPad(inc, 4, '0'); } public static void main(String[] args) throws Exception { // for (int i = 0; i < 1000; i++) { // new Thread(new Runnable() { // // @Override // public void run() { // for (int j = 0; j < 10; j++) { // System.out.println(TSS.getTimeStampSequence()); // } // } // }).start(); // } // 统计重复 HashSet<String> set = new HashSet<String>(); BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream("C:/Users/Administrator/Desktop/1.txt"))); String str = br.readLine(); while(str != null) { if(set.contains(str)) { System.out.println(str); } else { set.add(str); } str = br.readLine(); } br.close(); } }