背景:雪花算法在分布式集群情况下,会生成重复的数值
雪花算法介绍:
雪花算法由 64bit 组成,刚好凑上 long 的 8 个字节 然后中间有 10bit 是标识符作用,叫 dataCenterId 和 workerId,分别占 5bit
mybatisplus 取 mac 地址作为 dataCenterId,取出 5bit,通过 mac 地址来减少碰撞,然后又将 dataCenterId 和进程号拼接,取hashCode 低 16位,再次减少碰撞
问题出就出在进程号上面, K8s 的进程号都是 1。所以说,如果从 mac 地址上取出的 5位 bit 冲突了,那么相同的多个进程在同一毫秒同一序列号获取的雪花算法就是错的
解决方案:(节点最好低于32个)
雪花算法的标识位使用Redis 统一分配
核心代码:
@Resource private RedissonClient redissonClient; private static String key = "sysNo"; //使用redis原子自增,实现分布节点不同机器码 RAtomicLong counter = redissonClient.getAtomicLong(key); long workId =counter % 32; if(workId % 32 == 0){ workId = 1; } String macAddr = getMacAddr(); long dataCenterId = macAddr.hashCode(); if(dataCenterId % 32 == 0){ dataCenterId = 1; } Snowflake snowflake = new Snowflake(workId , dataCenterId); //生成唯一ID long uniKey= snowflake.nextId();
//获取节点mac地址
public String getMacAddr (){ try { // 获取所有网络接口 Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface networkInterface = interfaces.nextElement(); if (networkInterface.isLoopback() || networkInterface.isVirtual() || !networkInterface.isUp()) { continue; // 跳过回环接口、虚拟接口和非活动接口 } byte[] mac = networkInterface.getHardwareAddress(); if (mac != null) { Formatter formatter = new Formatter(); for (byte b : mac) { formatter.format("%02X:", b); } String macAddress = formatter.toString().substring(0, formatter.toString().length() - 1); // 移除最后一个冒号 formatter.close(); return macAddress; } else { System.out.println("MAC Address not found for: " + networkInterface.getName()); return null; } } } catch (Exception e) { e.printStackTrace(); } }