关于订单号的生成,有一些简单的方案
无需编码
大表不能做水平分表,否则插入删除时容易出现问题
高并发下插入数据需要加入失误机制
在业务操作父,子表(关联表)插入时,先要插入父表,再插入子表
编程简单
随机数存在重复问题,即使在相同的时间戳下。每次插入数据库前需要校验下是否已经存在相同的数值。
同一时间,一个用户不会存在2张订单
会员ID也会透露出运营数据,鸡生蛋蛋生鸡的问题
简单
用户不友好,索引关联效率较低
snowflake是Twitter开源的分布式ID 生成算法,结果是一个long型的ID
这种方案大致来说是一种以划分明明空间(UUID也算,由于比较常见,所以单独分析)来生成的ID 的一种算法,这种方案把64-bit分贝划分成多段,分开来提示机器,时间等;
其核心思想是: 使用41bit作为毫秒数,10bit作为机器的ID(5ge bit是数据中心,5个bit的机器ID),12bit 作为毫秒数内的流水号,最后一个符号位,永远是0.
比如在snowlake中的64-bit分别表示如下图(图片来自网络)表示
整个结构是64位,所以我们在Java中可以使用long来进行存储。该算法实现基本就是二进制操作,单机每秒内理论上最多可以生成1024*(2^12),也就是409.6万个ID(1024*4096=4194304)
整体上按照时间自增排序,并且排序整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID做区分),并且效率高,轻测试,SnowFlaje每秒能产生26万ID左右。
强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务处于不可用状态,
针对此,美团做出了改进:https://github.com/Meituan-Dianping/Leaf
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>5.0.6</version>
</dependency>
public class IdGenerator {
private long workerId = 0;
@PostConstruct
void init() {
try {
workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
log.info("当前机器 workerId: {}", workerId);
} catch (Exception e) {
log.warn("获取机器 ID 失败", e);
workerId = NetUtil.getLocalhost().hashCode();
log.info("当前机器 workerId: {}", workerId);
}
}
/**
* 获取一个批次号,形如 2019071015301361000101237
* <p>
* 数据库使用 char(25) 存储
*
* @param tenantId 租户ID,5 位
* @param module 业务模块ID,2 位
* @return 返回批次号
*/
public synchronized String batchId(int tenantId, int module) {
String prefix = DateTime.now().toString(DatePattern.PURE_DATETIME_MS_PATTERN);
return prefix + tenantId + module + RandomUtil.randomNumbers(3);
}
@Deprecated
public synchronized String getBatchId(int tenantId, int module) {
return batchId(tenantId, module);
}
/**
* 生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
*
* @return
*/
public String simpleUUID() {
return IdUtil.simpleUUID();
}
/**
* 生成的UUID是带-的字符串,类似于:a5c8a5e8-df2b-4706-bea4-08d0939410e3
*
* @return
*/
public String randomUUID() {
return IdUtil.randomUUID();
}
private Snowflake snowflake = IdUtil.createSnowflake(workerId, 1);
public synchronized long snowflakeId() {
return snowflake.nextId();
}
public synchronized long snowflakeId(long workerId, long dataCenterId) {
Snowflake snowflake = IdUtil.createSnowflake(workerId, dataCenterId);
return snowflake.nextId();
}
/**
* 生成类似:5b9e306a4df4f8c54a39fb0c
* <p>
* ObjectId 是 MongoDB 数据库的一种唯一 ID 生成策略,
* 是 UUID version1 的变种,详细介绍可见:服务化框架-分布式 Unique ID 的生成方法一览。
*
* @return
*/
public String objectId() {
return ObjectId.next();
}
}
}
Hutool官方给出的分布式IdUtil类的说明:
https://hutool.cn/docs/#/core/工具类/唯一ID工具-IdUtil?id=%e4%bb%8b%e7%bb%8d
参考自:https://www.jianshu.com/p/5c028b4b41f2?utm_campaign=haruki