一、实现MyBatis ID构建接口
1 2 3 4 5 6 7 8 9 10 11 12 |
@Slf4j @Component public class CustomIdGenerator implements IdentifierGenerator {
@Override public Long nextId(Object entity) { //生成ID long id = SnowFlakeUtils.nextId(); log.info( "生成ID: " + id); return id; } } |
二、雪花ID生成工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
@Slf4j public class SnowFlakeUtils {
/** 初始偏移时间戳 */ private static final long OFFSET = 1546300800L;
/** 机器id (0~15 保留 16~31作为备份机器) */ private static final long WORKER_ID; /** 机器id所占位数 (5bit, 支持最大机器数 2^5 = 32)*/ private static final long WORKER_ID_BITS = 5L; /** 自增序列所占位数 (16bit, 支持最大每秒生成 2^16 = 65536) */ private static final long SEQUENCE_ID_BITS = 16L; /** 机器id偏移位数 */ private static final long WORKER_SHIFT_BITS = SEQUENCE_ID_BITS; /** 自增序列偏移位数 */ private static final long OFFSET_SHIFT_BITS = SEQUENCE_ID_BITS + WORKER_ID_BITS; /** 机器标识最大值 (2^5 / 2 - 1 = 15) */ private static final long WORKER_ID_MAX = (( 1 << WORKER_ID_BITS) - 1 ) >> 1 ; /** 备份机器ID开始位置 (2^5 / 2 = 16) */ private static final long BACK_WORKER_ID_BEGIN = ( 1 << WORKER_ID_BITS) >> 1 ; /** 自增序列最大值 (2^16 - 1 = 65535) */ private static final long SEQUENCE_MAX = ( 1 << SEQUENCE_ID_BITS) - 1 ; /** 发生时间回拨时容忍的最大回拨时间 (秒) */ private static final long BACK_TIME_MAX = 1000L;
/** 上次生成ID的时间戳 (秒) */ private static long lastTimestamp = 0L; /** 当前秒内序列 (2^16)*/ private static long sequence = 0L; /** 备份机器上次生成ID的时间戳 (秒) */ private static long lastTimestampBak = 0L; /** 备份机器当前秒内序列 (2^16)*/ private static long sequenceBak = 0L;
static { // 初始化机器ID long workerId = getWorkId(); if (workerId < 0 || workerId > WORKER_ID_MAX) { throw new IllegalArgumentException(String.format( "cmallshop.workerId范围: 0 ~ %d 目前: %d" , WORKER_ID_MAX, workerId)); } WORKER_ID = workerId; }
private static Long getWorkId(){ try { String hostAddress = Inet4Address.getLocalHost().getHostAddress(); int [] ints = StringUtils.toCodePoints(hostAddress); int sums = 0 ; for ( int b : ints){ sums += b; } return ( long )(sums % WORKER_ID_MAX); } catch (UnknownHostException e) { // 如果获取失败,则使用随机数备用 return RandomUtils.nextLong( 0 ,WORKER_ID_MAX- 1 ); } }
/** 私有构造函数禁止外部访问 */ private SnowFlakeUtils() {}
/** * 获取自增序列 * @return long */ public static long nextId() { return nextId(SystemClock.now() / 1000 ); }
/** * 主机器自增序列 * @param timestamp 当前Unix时间戳 * @return long */ private static synchronized long nextId( long timestamp) { // 时钟回拨检查 if (timestamp < lastTimestamp) { // 发生时钟回拨 log.warn( "时钟回拨, 启用备份机器ID: now: [{}] last: [{}]" , timestamp, lastTimestamp); return nextIdBackup(timestamp); }
// 开始下一秒 if (timestamp != lastTimestamp) { lastTimestamp = timestamp; sequence = 0L; } if (0L == (++sequence & SEQUENCE_MAX)) { // 秒内序列用尽 // log.warn("秒内[{}]序列用尽, 启用备份机器ID序列", timestamp); sequence--; return nextIdBackup(timestamp); }
return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | (WORKER_ID << WORKER_SHIFT_BITS) | sequence; }
/** * 备份机器自增序列 * @param timestamp timestamp 当前Unix时间戳 * @return long */ private static long nextIdBackup( long timestamp) { if (timestamp < lastTimestampBak) { if (lastTimestampBak - SystemClock.now() / 1000 <= BACK_TIME_MAX) { timestamp = lastTimestampBak; } else { throw new RuntimeException(String.format( "时钟回拨: now: [%d] last: [%d]" , timestamp, lastTimestampBak)); } }
if (timestamp != lastTimestampBak) { lastTimestampBak = timestamp; sequenceBak = 0L; }
if (0L == (++sequenceBak & SEQUENCE_MAX)) { // 秒内序列用尽 // logger.warn("秒内[{}]序列用尽, 备份机器ID借取下一秒序列", timestamp); return nextIdBackup(timestamp + 1 ); }
return ((timestamp - OFFSET) << OFFSET_SHIFT_BITS) | ((WORKER_ID ^ BACK_WORKER_ID_BEGIN) << WORKER_SHIFT_BITS) | sequenceBak; }
/** * 并发数 */ private static final int THREAD_NUM = 30000 ; private static volatile CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
public static void main(String[] args) { ConcurrentHashMap<Long,Long> map = new ConcurrentHashMap<>(THREAD_NUM); List<Long> list = Collections.synchronizedList( new LinkedList<>());
for ( int i = 0 ; i < THREAD_NUM; i++) { Thread thread = new Thread(() -> { // 所有的线程在这里等待 try { countDownLatch.await();
Long id = SnowFlakeUtils.nextId(); list.add(id); map.put(id,1L); } catch (InterruptedException e) { e.printStackTrace(); } });
thread.start(); // 启动后,倒计时计数器减一,代表有一个线程准备就绪了 countDownLatch.countDown(); }
try { Thread.sleep( 50000 ); } catch (Exception e){ e.printStackTrace(); }
System.out.println( "listSize:" +list.size()); System.out.println( "mapSize:" +map.size()); System.out.println(map.size() == THREAD_NUM); } } |
到此这篇关于MyBatis使用雪花ID的实现的文章就介绍到这了,更多相关MyBatis 雪花ID内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
原文链接:https://www.jianshu.com/p/a17dd394501b
查看更多关于MyBatis使用雪花ID的实现的详细内容...