MyBatis使用雪花ID怎么实现
发表于:2025-11-15 作者:千家信息网编辑
千家信息网最后更新 2025年11月15日,这篇文章主要介绍了MyBatis使用雪花ID怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇MyBatis使用雪花ID怎么实现文章都会有所收获,下面我们一起来看看吧
千家信息网最后更新 2025年11月15日MyBatis使用雪花ID怎么实现
这篇文章主要介绍了MyBatis使用雪花ID怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇MyBatis使用雪花ID怎么实现文章都会有所收获,下面我们一起来看看吧。
一、实现MyBatis ID构建接口
@Slf4j@Componentpublic class CustomIdGenerator implements IdentifierGenerator { @Override public Long nextId(Object entity) { //生成ID long id = SnowFlakeUtils.nextId(); log.info("生成ID: " + id); return id; }}二、雪花ID生成工具类
@Slf4jpublic 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 map = new ConcurrentHashMap<>(THREAD_NUM); List 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怎么实现"知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。
序列
机器
备份
时间
生成
雪花
最大
时钟
知识
偏移
位数
内容
最大值
篇文章
线程
支持
倒计时
主机
代表
价值
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
树莓派串口采集数据库
苹果服务器是什么
大型游戏服务器坏了其他各地有吗
top网络安全风险
蚂蚁金服云服务器安全吗
人间地狱服务器国服
数据通信与网络安全知识
宝塔服务器管理软件怎么使用
数据库开发培训班
网络安全英文缩写是什么意思
下列不是数据库管理系统
工业网络技术课程设计
软件开发需要些什么工器具
上海嵌入式软件开发报价
枣庄和徐州服务器
app软件开发业务流程图
强网杯人工智能网络安全挑战赛
数据库就像生活中的自来水
烟台有软件开发公司吗
服务器的访问管理有哪些
校园网采用的是什么网络技术
数据库实验报告一结论及体会
东莞东塔网络安全特训营哪家好
dxl数据库
中国工业软件开发现况
广电网络技术部门
梦幻西游开新区服务器升级
服务器漏洞安全
博兴快消品管理软件开发
ros 缓存服务器