一、实现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