001/**
002 * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package com.mybatisflex.core.keygen.impl;
017
018import com.mybatisflex.core.keygen.IKeyGenerator;
019
020import java.util.concurrent.ThreadLocalRandom;
021
022/**
023 * 独创的 FlexID 算法(简单、好用):
024 * <p>
025 * 特点:
026 * 1、保证 id 生成的顺序为时间顺序,越往后生成的 ID 值越大;
027 * 2、运行时,单台机器并发量在每秒钟 10w 以内;
028 * 3、运行时,无视时间回拨;
029 * 4、最大支持 99 台机器;
030 * 5、够用大概 300 年左右的时间;
031 * <p>
032 * 缺点:
033 * 1、每台机器允许最大的并发量为 10w/s。
034 * 2、出现时间回拨,重启机器时,在时间回拨未恢复的情况下,可能出现 id 重复。
035 * <p>
036 * ID组成:时间(7+)| 毫秒内的时间自增 (00~99:2)| 机器ID(01 ~ 99:2)| 随机数(000~999:3)用于分库分表时,通过 id 取模,保证分布均衡。
037 */
038public class FlexIDKeyGenerator implements IKeyGenerator {
039
040    private static final long INITIAL_TIMESTAMP = 1680411660000L;
041    private static final long MAX_CLOCK_SEQ = 99;
042
043    private long lastTimeMillis = 0;//最后一次生成 ID 的时间
044    private long clockSeq = 0;      //时间序列
045    private long workId = 1;        //机器 ID
046
047    public FlexIDKeyGenerator() {
048    }
049
050    public FlexIDKeyGenerator(long workId) {
051        this.workId = workId;
052    }
053
054    @Override
055    public Object generate(Object entity, String keyColumn) {
056        return nextId();
057    }
058
059    private synchronized long nextId() {
060
061        //当前时间
062        long currentTimeMillis = System.currentTimeMillis();
063
064        if (currentTimeMillis == lastTimeMillis) {
065            clockSeq++;
066            if (clockSeq > MAX_CLOCK_SEQ) {
067                clockSeq = 0;
068                currentTimeMillis++;
069            }
070        }
071
072        //出现时间回拨
073        else if (currentTimeMillis < lastTimeMillis) {
074            currentTimeMillis = lastTimeMillis;
075            clockSeq++;
076
077            if (clockSeq > MAX_CLOCK_SEQ) {
078                clockSeq = 0;
079                currentTimeMillis++;
080            }
081        } else {
082            clockSeq = 0;
083        }
084
085        lastTimeMillis = currentTimeMillis;
086
087        long diffTimeMillis = currentTimeMillis - INITIAL_TIMESTAMP;
088        return diffTimeMillis * 1000000 + clockSeq * 10000 + workId * 1000 + getRandomInt();
089    }
090
091
092    private int getRandomInt() {
093        return ThreadLocalRandom.current().nextInt(1000);
094    }
095
096
097}