发布时间:2022-09-16 06:00
1. 订单编号,物流编号,身份证号 等需要展现给用户
2. 数据库主键,索引,以此来分分区,分表,分库
3. 消息ID
4. 动态帖子ID
1. 业务编号需要有一定的意义,人看起来不那么难受
N00001 √
反例:3af516cd74ec41788cea2c700c62ed60
2. 数据库主键和索引,如果是Mysql需要:容易比较大小(数字), insert的ID需要自增(减少调整页的性能损耗,如果不理解可去看看Mysql索引的数据结构)
3. 分区,分表,分库:一般情况都是冷热数据分离,所以需要ID有时间元素。如果ID用于路由就跟业务有关联了,根据实际情况分析
4. 消息ID,只需要唯一标识就可以,但查重就需要在缓存中记录,如果有时间元素,就可以判断出消息是否重复发送,比如:当前消息处理到今天了,但突然发现一个昨天的消息,那么直接丢弃就行了。
5. 帖子列表获取是没法按照普通分页来操作的:
原因:新增速度远超过你刷新的速度,获取的第二页可能比第一页都早
客户端帖子列表,只需要记录一个StartID和EndID即可,那么查询就是:> StartID ,limit 10,order **,这个字段最好是ID字段,否则排序数据库就受不了。
如果只有一个服务,多线程并发访问就可以用一个锁实现
分布式多实例服务,就有抢占的问题,锁必须独立在进程之外
如果分表是按照ID平均保存,那么就需要ID根据某种路由规则后值 应该是比较平均的,这类要求就需要按照实际情况看了
ID分为业务NUM 和 系统标识用的ID,业务ID一般都是产品给业务规则,在拼接递增序列即可,下面不在讨论
ID要求:全局唯一,时间增序
必须考虑分布式问题
/**
* 依据Twitter的分布式id生成算法而实现的唯一id生成器
*
* SnowFlake的结构如下(每部分用-分开):
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L *
* 60 * 60 * 24 * 365) = 69
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
* 加起来刚好64位,为一个Long型。
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*
* @date: 2018-10-23
* @version: 1.0
*/
public class IdUtil {
private final long twepoch = 1288834974657L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
private static WktiesIdUtil instance = null;
static {
getInstance();
}
private synchronized static WktiesIdUtil getInstance() {
if (instance == null) {
instance = new WktiesIdUtil(21, 20);
}
return instance;
}
/**
* 静态方法获取唯一id编号, 长度为19位
*
* @return 唯一id
*/
public static long getUniqueId() {
return instance.nextId();
}
public WktiesIdUtil(long workerId, long dataCenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (dataCenterId > maxDatacenterId || dataCenterId < 0) {
throw new IllegalArgumentException(
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = dataCenterId;
}
/**
* 获取唯一id编号, 长度为19位
*
* @return 唯一id
*/
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
long shangyige = WktiesIdUtil.getUniqueId();
for(int i = 0 ;i<50000000;i++){
long zhege = WktiesIdUtil.getUniqueId();
System.out.println(i + " " +zhege + " =>" + (zhege-shangyige));
if(shangyige >= zhege){
System.out.println(zhege);
break;
}
shangyige = zhege;
}
}
}
END