使用 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 中

image

具体代码细节如下:

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 这个方法。为了确保生成的数字具有不连续、不重复的特点,实现步骤如下:

  1. 采用雪花算法生成一批数字

  2. 将每个数字转换为字符串,将字符串转换为字符列表

  3. 使用 Jdk 自带的 Collections.shuffle 方法,将字符列表随机打乱

  4. 将打乱后的字符列表,再拼接组成最终的数字字符串

理论上来讲,经过这一波操作,生成的字符串重复概率极低,从全局角度上来看,基本上可以认为不重复。

因为对于同一个 19 位的字符串来说,根据高中的数学排列公式,可以计算出其随机排列后的结果可能性数量如下:

image

A(19 , 19)= 19 的阶乘 / 0 的阶乘 = 121,645,100,408,832,000

备注:0 的阶乘等于 1

由此可见,对于同一个 19 位字符串,随机排列后就有 十亿个亿 种结果,重复概率可以忽略不计,更何况雪花算法生成的每个数字都不一样,每个都需要进行打散随机再排列,所以从理论上来看,数字重复的概率极低,可以基本上认为不重复。


本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/XueHuaNumber.zip

posted @ 2023-04-02 14:36  乔京飞  阅读(10650)  评论(0编辑  收藏  举报