好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

MyBatis使用雪花ID的实现

一、实现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的实现的详细内容...

  阅读:25次