泰州市建设工程招标网seo网址大全
前言
有一个数据迁移的小活:从备库提取需要的数据,历史数据量较大,别人的库还不方便加索引,好在主键都是 MyBatis-Plus 雪花算法生成的,因此我有一个 Big 胆的想法,查询一定时间范围内的数据,我只需要起始ID和结束 ID 之间
的数据即可。
根据时间范围反推 ID 范围,高效不迷路!!!
MyBatis-Plus 的雪花算法
废话不多说,贴上生产雪花 ID 的妈妈,如下:
/** Copyright (c) 2011-2022, baomidou (jobob@qq.com).** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.baomidou.mybatisplus.core.toolkit;import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.concurrent.ThreadLocalRandom;/*** 分布式高效有序 ID 生产黑科技(sequence)** <p>优化开源项目:https://gitee.com/yu120/sequence</p>** @author hubin* @since 2016-08-18*/
public class Sequence {private static final Log logger = LogFactory.getLog(Sequence.class);/*** 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)*/private static 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 final long workerId;/*** 数据标识 ID 部分*/private final long datacenterId;/*** 并发控制*/private long sequence = 0L;/*** 上次生产 ID 时间戳*/private long lastTimestamp = -1L;/*** IP 地址*/private InetAddress inetAddress;public Sequence(InetAddress inetAddress) {this.inetAddress = inetAddress;this.datacenterId = getDatacenterId(maxDatacenterId);this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);}/*** 有参构造器** @param workerId 工作机器 ID* @param datacenterId 序列号*/public Sequence(long workerId, long datacenterId) {Assert.isFalse(workerId > maxWorkerId || workerId < 0,String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));Assert.isFalse(datacenterId > maxDatacenterId || datacenterId < 0,String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获取 maxWorkerId*/protected long getMaxWorkerId(long datacenterId, long maxWorkerId) {StringBuilder mpid = new StringBuilder();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (StringUtils.isNotBlank(name)) {/** GET jvmPid*/mpid.append(name.split(StringPool.AT)[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);}/*** 数据标识id部分*/protected long getDatacenterId(long maxDatacenterId) {long id = 0L;try {if (null == this.inetAddress) {this.inetAddress = InetAddress.getLocalHost();}NetworkInterface network = NetworkInterface.getByInetAddress(this.inetAddress);if (null == network) {id = 1L;} else {byte[] mac = network.getHardwareAddress();if (null != mac) {id = ((0x000000FF & (long) mac[mac.length - 2]) | (0x0000FF00 & (((long) mac[mac.length - 1]) << 8))) >> 6;id = id % (maxDatacenterId + 1);}}} catch (Exception e) {logger.warn(" getDatacenterId: " + e.getMessage());}return id;}/*** 获取下一个 ID** @return 下一个 ID*/public synchronized long nextId() {long timestamp = timeGen();//闰秒if (timestamp < lastTimestamp) {long offset = lastTimestamp - timestamp;if (offset <= 5) {try {wait(offset << 1);timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));}} catch (Exception e) {throw new RuntimeException(e);}} else {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));}}if (lastTimestamp == timestamp) {// 相同毫秒内,序列号自增sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 同一毫秒的序列数已经达到最大timestamp = tilNextMillis(lastTimestamp);}} else {// 不同毫秒内,序列号置为 1 - 2 随机数sequence = ThreadLocalRandom.current().nextLong(1, 3);}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 SystemClock.now();}/*** 反解id的时间戳部分*/public static long parseIdTimestamp(long id) {return (id>>22)+twepoch;}
}
时间范围生成最大ID和最小ID工具类
下面的参数,参考上面的雪花算法中的参数,必须一致
import com.baomidou.mybatisplus.core.toolkit.Sequence;import java.time.LocalDateTime;
import java.time.ZoneOffset;/*** 雪花算法id的时间范围工具类* @author monkeyhi*/
public class SequenceIdRangeUtil {// 以下常量需与 Sequence 类定义一致private static final long TWEPOCH = 1288834974657L; // 固定起始时间戳private static final long WORKER_ID_BITS = 5L; // 机器ID位数private static final long DATACENTER_ID_BITS = 5L; // 数据中心ID位数private static final long SEQUENCE_BITS = 12L; // 序列号位数private static final long TIMESTAMP_LEFT_SHIFT = 22; // 时间戳左移位数(12+5+5)private static final long DATACENTER_ID_SHIFT = 17; // 数据中心ID左移位数(12+5)private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; // 机器ID左移位数(12)private static final long MAX_DATACENTER_ID = -1L ^ (-1L << WORKER_ID_BITS); // 5位最大值private static final long MAX_WORKER_ID = -1L ^ (-1L << DATACENTER_ID_BITS); // 5位最大值private static final long MAX_SEQUENCE = -1L ^ (-1L << WORKER_ID_SHIFT); // 12位最大值(0b111111111111)/*** 生成起始时间对应的最小ID(时间部分最小值,其他位全0)*/public static long genMinIdByStartDateTime(LocalDateTime startDateTime) {long timestamp = startDateTime.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();long timestampDelta = timestamp - TWEPOCH;return timestampDelta << TIMESTAMP_LEFT_SHIFT;}/*** 生成结束时间对应的最大ID(时间部分最大值,其他位全1)*/public static long genMaxIdByEndDateTime(LocalDateTime endDateTime) {long timestamp = endDateTime.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();long timestampDelta = timestamp - TWEPOCH;// 时间戳部分 | 最大数据中心ID | 最大机器ID | 最大序列号return (timestampDelta << TIMESTAMP_LEFT_SHIFT)| (MAX_DATACENTER_ID << DATACENTER_ID_SHIFT)| (MAX_WORKER_ID << WORKER_ID_SHIFT)| MAX_SEQUENCE;}public static void main(String[] args) {//2025-03-24 18:20:14 2025-03-24 18:20:13LocalDateTime start = LocalDateTime.of(2025, 3, 24, 18, 20, 13);LocalDateTime end = LocalDateTime.of(2025, 3, 24, 18, 20, LocalDateTime.MAX.getSecond());// 生成ID范围long minId = SequenceIdRangeUtil.genMinIdByStartDateTime(start);long maxId = SequenceIdRangeUtil.genMaxIdByEndDateTime(end);System.out.println("最小ID:" + minId);System.out.println("最大ID:" + maxId);System.out.println("参考ID:1904116032358764546L");// 参考的idlong exampleId = Sequence.parseIdTimestamp(1904116032358764546L);System.out.println("其时间戳:" + exampleId);}
}
最终输出
最小ID:1904116030108598272
最大ID:1904116223050776575
参考ID:1904116032358764546
其时间戳:1742811613536 = 2025-03-24 18:20:13
最后,祝大家玩的愉快~~