使用 MybatisPlusCore 自带的雪花算法生成不重复数字
这里不介绍雪花算法的实现原理,可以自行搜索查阅网上的资料。这里主要介绍雪花算法的使用场景,如何调用第三方类库 Mybatis Plus Core 自带的方法来使用雪花算法。
雪花算法的主要使用场景,就是生成不重复的数字,作为数据库表的主键使用。你可能会使用 uuid 作为主键,但是其占用 16 个字节长度,具有无序性,在表中数据量大的情况下,会引起数据位置频繁变动,不利于索引的维护,对于批量数据的增删改具有严重性能影响,Mysql 官方也不推荐使用 uuid 作为表的主键。
使用数字作为数据库表的主键,是最佳实践。其优点在于占用空间小(最多8个字节),查询性能和索引维护效率高,目前雪花算法是业界公认的生成不重复数字非常高效的算法,其算法简单,分布式高并发环境下,能够实现每秒生成百万个不重复数字,被国内外互联网厂商普遍使用。
雪花算法唯一的缺点就是依赖于服务器的时钟,如果时钟回拨,可能会生成重复的数字。该缺点也是可以配合其它方案进行解决的。
代码实现
新建一个 SpringBoot 项目,在 pom 文件中只需要引用 mybatis-plus-core 的 jar 包即可,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
</parent>
<groupId>com.jobs</groupId>
<artifactId>XueHuaNumber</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>XueHuaNumber</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--只需要引用 Mybatis plus core 的组件即可使用其内置的雪花算法-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
项目的结构如下图所示,代码的执行在测试类 XueHuaNumberApplicationTests 中
具体代码细节如下:
package com.jobs.xuehua;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.*;
@SpringBootTest
class XueHuaNumberApplicationTests {
//使用雪花算法生成单个 19 位数字,每次生成的数字都不同
@Test
void SingleXueHuaNumber() {
//Mybatis plus core 内置的类,自带雪花算法函数。
DefaultIdentifierGenerator dig = new DefaultIdentifierGenerator();
long result = dig.nextId(new Object());
System.out.println(result);
}
//使用雪花算法批量生成若干个 19 位数字,从生成结果来看,是连续的数字
@Test
void BatchXueHuaNumber() {
//Mybatis plus core 内置的类,自带雪花算法函数。
DefaultIdentifierGenerator dig = new DefaultIdentifierGenerator();
long result;
for (int i = 0; i < 100000; i++) {
//使用雪花算法生成一个 19 位的数字
result = dig.nextId(new Object());
//为了节省控制台空间,只打印前 1000 个数字
if (i <= 1000) {
System.out.println(result);
}
}
}
//使用雪花算法,生成 19 位数字,转换为字符串,打散字符串的顺序,从而生成不连续的数字
@Test
void BatchRandomNumber() {
//使用 map 存储生成的数字,最后用于判断在一次批量生成的过程中,是否存在重复的数字
HashMap<String, String> hm = new HashMap<>();
//Mybatis plus core 内置的类,自带雪花算法函数。
DefaultIdentifierGenerator dig = new DefaultIdentifierGenerator();
//程序执行开始时间戳
long start = System.currentTimeMillis();
String temp, result;
List<String> slist;
for (int i = 0; i < 100000; i++) {
//雪花算法获取一个 19 位数字字符串
temp = dig.nextId(new Object()).toString();
//将字符串转换为 list
slist = Arrays.asList(temp.split(""));
//利用 Collections 自带的方法,打乱 list 中字符串的顺序
Collections.shuffle(slist);
//合并打乱后的字符串
result = String.join("", slist);
//添加到 hashmap 中,看看是否有重复
hm.put(result, "");
//将打乱后的字符串,并打印出来(为了节省控制台空间,只打印前 1000 个数字)
if (i <= 1000) {
System.out.println(result);
}
}
long end = System.currentTimeMillis();
long dural = end - start;
System.out.println("随机 100000 个数字,耗费时间:" + dural + " 毫秒");
System.out.println("hashmap 中的数据数量为:" + hm.size());
}
//Mybatis plus core 内置的类,也封装了有生成 uuid 的函数,其生成的 uuid 去掉了横线
@Test
void getUuidCompare() {
String uidjdk = UUID.randomUUID().toString();
System.out.println("Jdk 自带方法生成的 uuid 为:" + uidjdk);
System.out.println("-----------------------------------");
DefaultIdentifierGenerator dig = new DefaultIdentifierGenerator();
String uid = dig.nextUUID(new Object());
//从打印的结果来看,已经去掉了 uuid 种的横杠
System.out.println("mybatis core 自带方法生成的 uuid 为:" + uid);
}
}
这里说一下 BatchRandomNumber 这个方法。为了确保生成的数字具有不连续、不重复的特点,实现步骤如下:
-
采用雪花算法生成一批数字
-
将每个数字转换为字符串,将字符串转换为字符列表
-
使用 Jdk 自带的 Collections.shuffle 方法,将字符列表随机打乱
-
将打乱后的字符列表,再拼接组成最终的数字字符串
理论上来讲,经过这一波操作,生成的字符串重复概率极低,从全局角度上来看,基本上可以认为不重复。
因为对于同一个 19 位的字符串来说,根据高中的数学排列公式,可以计算出其随机排列后的结果可能性数量如下:
A(19 , 19)= 19 的阶乘 / 0 的阶乘 = 121,645,100,408,832,000
备注:0 的阶乘等于 1
由此可见,对于同一个 19 位字符串,随机排列后就有 十亿个亿 种结果,重复概率可以忽略不计,更何况雪花算法生成的每个数字都不一样,每个都需要进行打散随机再排列,所以从理论上来看,数字重复的概率极低,可以基本上认为不重复。
本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/XueHuaNumber.zip