MyCAT——MyCAT(下载和安装、配置 Mycat 相关文件、出错时解决方法、mycat 分片规则【仅列举五种】)、分布式环境下的主键生成(Java 代码处理—— LEAF的两种模式))

8 篇文章 1 订阅
2 篇文章 2 订阅

MyCAT——MyCAT(下载和安装、配置 Mycat 相关文件、出错时解决方法、mycat 分片规则【仅列举五种】)、分布式环境下的主键生成(Java 代码处理—— LEAF的两种模式))

一、MyCAT

当数据库是主从结构的时候,Java 怎么去操控呢?
两种方法,一种是配置多数据源,但是麻烦不好操控。另外一种是使用数据库中间件。

这个数据库中间件搭在 java 和数据库之间,以后要操作数据库,java 代码不需要同时操作这么多数据库,只需要操作数据库中间件即可。通过数据库中间件去操作多数据库。

虽然前面搭了主从架构的 mysql,但是工作中更多用到的是双 M(master) 结构。就是两个 mysql ,互为对方主机。为什么这么搭建?因为主从切换方便;像前面搭建的主从结构,主机写入,从机读取,当然从机也可以写数据,但是数据会乱套,且主机不能同步到从机的数据;现在这种双 M 结构的搭建,你往哪台机子上面写入数据,另外一台机子都可以同步数据;而且两台机子中有机子挂了,可以迅速的切换到另外一台机子上面去,非常的方便;且将来挂掉的机子重启了又可以重新同步数据。

1、引言和介绍

a、什么是 MyCAT

什么是MyCAT:

简单的说,MyCAT就是:

  • 一个彻底开源的,面向企业应用开发的“大数据库集群”
  • 支持事务、ACID、可以替代Mysql的加强版数据库中间件
  • 一个可以视为“Mysql”集群的企业级数据库,用来替代昂贵的Oracle集群
  • 一个融合内存缓存技术、Nosql技术、HDFS大数据的新型SQL Server
  • 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品
  • 一个新颖的数据库中间件产品

MyCAT的目标是:低成本的将现有的单机数据库和应用平滑迁移到“云”端,解决数据存储和业务规模迅速增长情况下的数据瓶颈问题。

MyCAT的关键特性:

  • 支持 SQL 92标准
  • 支持Mysql集群,可以作为Proxy使用
  • 支持JDBC连接ORACLE、DB2、SQL Server,将其模拟为MySQL Server使用
  • 支持galera for mysql集群,percona-cluster或者mariadb cluster,提供高可用性数据分片集群
  • 自动故障切换,高可用性
  • 支持读写分离,支持Mysql双主多从,以及一主多从的模式
  • 支持全局表,数据自动分片到多个节点,用于高效表关联查询
  • 支持独有的基于E-R 关系的分片策略,实现了高效的表关联查询
  • 多平台支持,部署和实施简单

b、MyCAT 架构

在这里插入图片描述

c、Mycat解决的问题

在这里插入图片描述

d、Mycat对多数据库的支持

在这里插入图片描述

e、分片策略

MyCAT支持水平分片与垂直分片:

  • 水平分片:一个表格的数据分割到多个节点上,按照行分隔。
  • 垂直分片:一个数据库中多个表格A,B,C,A存储到节点1上,B存储到节点2上,C存储到节点3上。

在这里插入图片描述

MyCAT通过定义表的分片规则来实现分片,每个表格可以捆绑一个分片规则,每个分片规则指定一个分片字段并绑定一个函数,来实现动态分片算法。

  • Schema:逻辑库,与MySQL中的Database(数据库)对应,一个逻辑库中定义了所包括的Table。
  • Table:表,即物理数据库中存储的某一张表,与传统数据库不同,这里的表格需要声明其所存储的逻辑数据节点DataNode。在此可以指定表的分片规则。
  • DataNode:MyCAT的逻辑数据节点,是存放table的具体物理节点,也称之为分片节点,通过DataSource来关联到后端某个具体数据库上
  • DataSource:定义某个物理库的访问地址,用于捆绑到Datanode上

mycat 是不保存数据的。

2、Mycat的下载及安装

官方网站:
http://www.mycat.org.cn/

github地址
https://github.com/MyCATApache

因为 mycat 是用 java 开发的,所以需要 jdk,jdk 的下载和环境变量配置参考前面博客。

  • 第一步:把MyCat的压缩包上传到linux服务器
  • 第二步:解压缩,得到mycat目录
  • 第三步:进入mycat/bin,启动MyCat
  • 启动命令:./mycat start
  • 停止命令:./mycat stop
  • 重启命令:./mycat restart
  • 注意:可以使用mysql的客户端直接连接mycat服务。默认服务端口为8066

3、配置 Mycat 相关文件

a、配置 schema.xml

先介绍 Schema.xml

Schema.xml作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、表、分片规则、DataNode以及DataSource。弄懂这些配置,是正确使用MyCat的前提。这里就一层层对该文件进行解析:

  • schema: 标签用于定义MyCat实例中的逻辑库。
  • Table: 标签定义了MyCat中的逻辑表。
  • dataNode: 标签定义了MyCat中的数据节点,也就是我们通常说所的数据分片。
  • dataHost: 标签在mycat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。

注意:若是LINUX版本的MYSQL,则需要设置为Mysql大小写不敏感,否则可能会发生表找不到的问题。在MySQL的配置文件中my.cnf 位置在etc目录下[mysqld] 中增加一行:
  lower_case_table_names = 1

先提醒一句:如果要配置主从结构,至少要 6 个 mysql 才能组成,因为这里指定最少需要 3 组 mysql 主从,也就是 6 个 mysql。

所以这里为了省事,就不搭主从了,就使用三个库来模拟效果,虽然不那么形象,但效果是一样的。

首先在创建三个数据库(模拟主从效果):
在这里插入图片描述

接着:
在这里插入图片描述
一个 datahost 可以代表主机和从机。

上图中有个地方写错了,配置 readHost 不是在 writeHost 的下面,而是里面:
在这里插入图片描述
到此就配置好了。

如果是三个 mysql 主从,估计 datanode 就要 6 个,datahost 就要三个。还要配置 readHost。

a、配置 server.xml

先介绍这个配置文件: server.xml几乎保存了所有mycat需要的系统配置信息。最常用的是在此配置用户名、密码及权限。
在这里插入图片描述
然后就使用链接 mysql 的工具就能够链接 mycat,用户名密码看上图设置。

4、出错时查看日志

如果有问题,一般主要是两种。

第一步是如果测试链接有问题,那就是配置 mycat 的文件没配置好。

第二种是如果能链接,但是在里面打开数据库有问题,那就是 mycat 连 mysql 有问题。

有问题这里只能去查看日志,没有别的办法。

在 mycat / logs / 目录下有个 wrapper.log 文件,里面记载了错误信息。查看即可。

5、简单使用 MyCAT

在 mycat 里面,执行创建数据表的语句,这时就会发现,三个数据库同时都有数据表:
在这里插入图片描述
然后看效果:
在这里插入图片描述

这时为三个库分别加上不同的数据,然后回到 mycat 查询数据,会把三个库的数据集合到一起:
在这里插入图片描述

在这里插入图片描述

诸如其他的 where 也是没有问题的。这一块的查询没啥好说的。

6、mycat 分片规则

mycat 的分片规则有 17 种,但是并不难,只要会用一个,其他的都会用,这里只列举五种。

分片规则讨论的就是,比如执行一条插入 sql,这个数据应该被插入到哪个库里面去。分片就是定义这些规则。

a、global

有一些表,数据量不大,也不怎么修改,主要是查询操作,例如系统配置表,这一类表我们可以使用 global 这种分片规则。且系统配置表没多少数据,一般也很少修改。
global 的特点是,该表会在所有的库中都创建,而且每一个库中都保存了该表的完整数据。
具体配置方式,就是在 schema.xml 的 table 节点中添加一个 type 属性,值为 global:

增删改都是同时对几个表一起操作,但是查询并不会结果合并,只会查出一个表的结果。好处是查询只需要查一个库即可,效率比较高;但是缺点是增删改效率低,比较慢,但是一开始也说了这个适用于数据量少且很少修改的表,所以对这些设置 global 没毛病。

在 schema.xml 的 table 节点中添加一个 type 属性,值为 global:
注意:这里因为 table 节点里面有个正常的分片规则,先要暂时去掉:

在这里插入图片描述
去掉绿色的,增加红色的即可。

保存退出后,再重启:
在这里插入图片描述

因为修改了定义,所以要删除且重新创建一张表:
在这里插入图片描述
然后随便执行一个插入语句,查看结果即可。只要所有表都有相同的数据,就是成功。

这时执行查询语句,会发现只会返回一张表的数据。

b、sharding-by-intfile

sharding-by-intfile 这个是枚举分片,就是在数据表中专门设计一个字段,以后根据这个字段的值来决定数据插入到哪个 dataNode 上。

这种分片就很像之前博客中的分区,男和女的例子。

注意,在配置 sharding-by-intfile 规则时,一定要删除 type=“global” ,否则配置不会生效。具体配置如下:
在这里插入图片描述

在这里插入图片描述

还需要改动这里:mycat 目录下的 conf / rule.xml 。 这里是各种分片的规则:
在这里插入图片描述
在 rule.xml 文件中,首先找到 tableRule 的名字为 sharding-by-intfile 的节点,这个节点中定义了两个属性,一个是 columns 表示一会在数据表中定义的枚举列的名字(数据表中一会需要创建一个名为 sharding_id 的列,这个列的值决定了该条数据保存在哪个数据库实例中),这个名字可以自定义;另外一个属性叫做 algorithm ,这是指 sharding-by-intfile 所对应的算法名称。根据这个名称,可以找到具体的算法:

可见上面的算法是 hash-int,根据这个,还是在当前文件下,去查找:
在这里插入图片描述
如果下载 mycat 的源码就能看到这个类,这里就不去看了。接着会看到会读取一个 partition-hash-int.txt 的配置文件,里面定义分片规则,和各种东西之间的配置关系。

接着退出去,继续改 partition-hash-int.txt 文件的内容:
在这里插入图片描述

可以看到:
在这里插入图片描述
这里的意思就是,如果 sharing_id 为 10000 的话,就保存到 0号库里面,后面的同理。当然这里是可以改的:
在这里插入图片描述

保存退出重启后,根据这个规则,需要在数据库里面加个字段:
在这里插入图片描述
同理,前面使用的表要删掉,不然不生效。

接着测试:
在这里插入图片描述
接着就可以在三个库里面中的一个找到一个是 zhangsan 的数据。

现在查询的话,各个数据库的数据并不会合并,而是都会展示出来。

当然查询的时候可以使用分片:
在这里插入图片描述
那么这种查询不会查另外两个库的节点,只会去查 sharing_id 为 2 的节点。

c、auto-sharding-long

auto-sharding-long 表示按照既定的范围去存储数据。就是提前规划好某个字段的值在某个范围时,相应的记录存到某个 dataNode 中。

更改方式是一样的。去配置文件里面把 rule 改为 auto-sharding-long 即可。

接着同样是根据上面的步骤去找 rule.xml 文件,然后查看算法,再去查找算法对应的文件,再去查看这个文件:
在这里插入图片描述
这里就是这个范围。

同样的,这里意思是 id 为 0-500万的,会进到 0 号库里面。后面同理。这里可以自行更改测试:
在这里插入图片描述

后续就是根据上图自行插入不同 id 去测试是否进到对应的库中。这里就不展示了。

提示:这里的范围是闭区间,0-5 都会进到 0 号库里面。

d、mod-long

取模:根据表中的某一个字段,做取模操作。根据取模的结果将记录存放在不同的 dataNode 上。这种方式不需要再添加额外字段。

就是取余数的意思。

默认是 id 去取模。

在这里插入图片描述

跟 3 取模,无非就是 0 1 2;如果给到其他数字,比如有 3,4 这些,那么有的数据会存不进去。如果是查询,查询出来的结果会为 null。

e、sharding-by-murmur

前面介绍的几种方式,都存在一个问题,如果数据库要扩容,之前配置会失效,可能会出现数据库查询紊乱。因此我们要引入一致性 hash 这样一种分片规则,可以解决这个问题。

意思是说如果一开始没设计好,后面想再扩容,就扩不了。所以这里就提供了这个,可以让我们去扩容。

具体配置和前面一样:

另外需要注意,在 rule.xml 中修改默认 dataNode 的数量:

修改完后,重启 MyCat ,进行测试。

二、分布式环境下的主键生成

两种思路:
整体上来说,这个问题有两种不同的思路:

  • 让数据库自己搞定
  • Java 代码来处理主键,然后直接插入数据库中即可。

这两种思路又对应了不同的方案,我们一个一个来看。

1、数据库自己搞定(了解即可)

数据库自己搞定,就是说我在数据插入的时候,依然不考虑主键的问题,希望继续使用数据库的主键自增,但是很明显,原本默认的主键自增现在没法用了,我们必须有新的方案。

数据库分库分表之后的结构如下图(假设数据库中间件用的 MyCat):
在这里插入图片描述
此时如果原本的 db1、db2、db3 继续各自主键自增,那么对于 MyCat 而言,主键就不是自增了,主键就会重复,用户从 MyCat 中查询到的数据主键就有问题。

我们可以直接修改 MySQL 数据库主键自增的起始值和步长。

首先我们可以通过如下 SQL 查看与此相关的两个变量的取值:
SHOW VARIABLES LIKE 'auto_increment%
在这里插入图片描述

可以看到,主键自增的起始值和步长都是 1。

我们可以修改起始值跟步长,比如起始值是10,步长是2,那么每次插入之后的主键就是 10、12、14 等等,以此类推。

起始值在定义表的时候就可以设置(在创建表语句的最后加):
在这里插入图片描述

这样,起始 id 就是从 100 开始。

起始值好改,在定义表的时候就可以设置,步长我们可以通过修改这个配置实现:
set @@auto_increment_increment=x;

通过设置上面这些,我们可以在创建不同的表的时候,分别设置起始值和步长,这样合并后的 id 就不会起冲突:
比如:
第一张表:1 4 7
第二张表:2 5 8
第三张表:3 6 9

但这么设置很多弊端,也很麻烦。

2、Java 代码处理

a、UUID(用的很少)

在这里插入图片描述

b、SNOWFLAKE(雪花算法)

在这里插入图片描述在这里插入图片描述
下面给出一个雪花算法的工具类,仅供参考:

// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
    private final static long twepoch = 1288834974657L;
    // 机器标识位数
    private final static long workerIdBits = 5L;
    // 数据中心标识位数
    private final static long datacenterIdBits = 5L;
    // 机器ID最大值
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    // 数据中心ID最大值
    private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    // 毫秒内自增位
    private final static long sequenceBits = 12L;
    // 机器ID偏左移12位
    private final static long workerIdShift = sequenceBits;
    // 数据中心ID左移17位
    private final static long datacenterIdShift = sequenceBits + workerIdBits;
    // 时间毫秒左移22位
    private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /* 上次生产id时间戳 */
    private static long lastTimestamp = -1L;
    // 0,并发控制
    private long sequence = 0L;

    private final long workerId;
    // 数据标识id部分
    private final long datacenterId;

    public IdWorker(){
        this.datacenterId = getDatacenterId(maxDatacenterId);
        this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
    }

    /**
     * @param workerId
     *            工作机器ID
     * @param datacenterId
     *            序列号
     */
    public IdWorker(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
     *
     * @return
     */
    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) {
            // 当前毫秒内,则+1
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                // 当前毫秒内计数满了,则等待下一秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // ID偏移组合生成最终的ID,并返回ID
        long nextId = ((timestamp - twepoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;

        return nextId;
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * <p>
     * 获取 maxWorkerId
     * </p>
     */
    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
        StringBuffer mpid = new StringBuffer();
        mpid.append(datacenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            /*
             * GET jvmPid
             */
            mpid.append(name.split("@")[0]);
        }
        /*
         * MAC + PID 的 hashcode 获取16个低位
         */
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * <p>
     * 数据标识id部分
     * </p>
     */
    protected static long getDatacenterId(long maxDatacenterId) {
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                        | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (maxDatacenterId + 1);
            }
        } catch (Exception e) {
            System.out.println(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }

用法如下:
在这里插入图片描述

这种方式其实用的也不多。

更为常见的是下面这种

c、LEAF(重点)

Leaf 是美团开源的分布式 ID 生成系统,最早期需求是各个业务线的订单 ID 生成需求。在美团早期,有的业务直接通过 DB 自增的方式生成 ID,有的业务通过 Redis 缓存来生成 ID,也有的业务直接用 UUID这种方式来生成 ID。以上的方式各自有各自的问题,因此美团决定实现一套分布式 ID 生成服务来满足需求目前 Leaf 覆盖了美团点评公司内部金融、餐饮、外卖、酒店旅游、猫眼电影等众多业务线。在4C8G VM 基础上,通过公司 RPC 方式调用,QPS 压测结果近 5w/s,TP999 1ms(TP=TopPercentile,Top 百分数,是一个统计学里的术语,与平均数、中位数都是一类。TP50、TP90 和 TP99等指标常用于系统性能监控场景,指高于 50%、90%、99% 等百分线的情况)。目前 LEAF 的使用有两种不同的思路,号段模式和 SNOWFLAKE 模式,你可以同时开启两种方式,也可
以指定开启某种方式(默认两种方式为关闭状态)。

我们从 GitHub 上 Clone LEAF 之后,它的配置文件在 leafserver/src/main/resources/leaf.properties 中,各项配置的含义如下:
在这里插入图片描述
可以看到,如果使用号段模式,需要数据库支持;如果使用 SNOWFLAKE 模式,需要 Zookeeper 支持。

这个项目是开源项目(也是个 SpringBoot 项目),可以直接去 github 上搜索 leaf 下载。

这里先使用第一种模式——号段模式

(1)号段模式

号段模式还是基于数据库,但是思路有些变化,如下:

  1. 利用 proxy server 从数据库中批量获取 id,每次获取一个 segment (step 决定其大小) 号段的
    值,用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力。
  2. 各个业务不同的发号需求用 biz_tag 字段来区分,每个 biz-tag 的 ID 获取相互隔离,互不影响。
  3. 如果有新的业务需要扩区 ID,只需要增加表记录即可。

如果使用号段模式,我们首先需要创建一张数据表,脚本如下:

CREATE DATABASE leaf
CREATE TABLE `leaf_alloc` (
	`biz_tag` varchar(128) NOT NULL DEFAULT '',
	`max_id` bigint(20) NOT NULL DEFAULT '1',
	`step` int(11) NOT NULL,
	`description` varchar(256) DEFAULT NULL,
	`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;
insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segmenttest', 1, 2000, 'Test leaf Segment Mode Get Id')

这张表中各项字段的含义如下:

  • biz_tag:业务标记(不同业务可以有不同的号段序列)
  • max_id:当前号段下的最大 id
  • step:每次取号段的步长
  • description:描述信息
  • update_time:更新时间

配置完成后,启动项目,访问 http://localhost:8080/api/segment/get/leaf-segment-test 路径(路径最后面的 leaf-segment-test 是业务标记),即可拿到 ID。

可以通过如下地址访问到号段模式的监控页面 http://localhost:8080/cache 。

这边先介绍一下用法:
在这里插入图片描述
上图中的 biz_tag 相当于业务标记。 前面也说了这套 id 自增的方案可以用到很多不同的业务中,你打开手机美团主页那么多不同的业功能,里面那么多不同的业务都可以使用这一套 id 自增方案。

上面的 max_id 意思是当前的 id 最大值到哪里。但并不是指目前的 id 。
step 就是步长。每到 id 数量不够用时,就会扩容,这个就是扩容的数量。

混合起来的意思是,

那么怎么获取自增长的 id 呢?
请求这个接口就能获得http://localhost:8080/api/segment/get/leaf-segment-test。每调用一次,就能获取一个自增长的 id 。
这个自增长的 id 就是从数据库里面获取的。但并不是每次要获取 id 都去请求数据库获取一次。是一口气就从数据库获取一批 id 过来放到缓存中去。 比如上面的 step 是10,那么每次都会获取 10 个 id 放到 SpringBoot 的内存中; 接下来如果要调用就直接从内存中获取就行。当发现这 10 个不太够用,会再一次获取 10 个 id 过来。注意,这里并不是全部用完了才去数据库中获取;而是消耗了缓存中的三分之一个 id 时,就会向数据库申请拿下一批的 id 了。以此类推。

这边用人话翻译一下优点:
使用简单、稳定、还支持分布式项目,且是真正的自增且连续的 id
相信很多用过 mysql 的小伙伴都遇到过这么种情况,现在数据库有 1 - 10 的 id,如果删除 8 - 10,这时候再插入数据,id 就是从 11 开始了。mysql 的主键增长只能保证是自增的,但不能保证连续。但是这个 leaf 是可以实现真正的自增且连续的 id。

号段模式优缺点:

优点:

  • Leaf 服务可以很方便的线性扩展,性能完全能够支撑大多数业务场景。
  • ID 号码是趋势递增的 8byte 的 64 位数字,满足上述数据库存储的主键要求。
  • 容灾性高:Leaf 服务内部有号段缓存,即使 DB 宕机,短时间内 Leaf 仍能正常对外提供服务。
  • 可以自定义 max_id 的大小,非常方便业务从原有的 ID 方式上迁移过来。

缺点:

  • ID 号码不够随机,能够泄露发号数量的信息,不太安全。
  • DB 宕机会造成整个系统不可用。

使用截图:
在这里插入图片描述
在这里插入图片描述
然后修改一下依赖(在最外面那层的 pom.xml 中修改):

修改数据库版本(对应自己要连接的数据库版本即可):
在这里插入图片描述

修改 druid:
在这里插入图片描述

然后运行没问题就可测试:
在这里插入图片描述
这就是 自增长的 id 了。

多刷新几次 id 就会增长。

(2)雪花模式

雪花模式也需要有个公共的地方去统筹,id 增长的信息。这个统筹就是 Zookeeper 。
SNOWFLAKE 模式需要配合 Zookeeper 一起,不过 SNOWFLAKE 对 Zookeeper 的依赖是弱依赖,把Zookeeper 启动之后,我们可以在 SNOWFLAKE 中配置 Zookeeper 信息,如下:

Zookeeper 需要 在 docker 上面安装集群。可以用 docker-compose.yaml 安装:

要的自取:

version: '3.7'

# 给zk集群配置一个网络,网络名为zk-net
networks:
  zk-net:
    name: zk-net

# 配置zk集群的
# container services下的每一个子配置都对应一个zk节点的docker container
services:
  zk1:
    # docker container所使用的docker image
    image: zookeeper
    hostname: zk1
    container_name: zk1
    # 配置docker container和宿主机的端口映射
    ports:
      - 2181:2181
      - 8081:8080
    # 配置docker container的环境变量
    environment:
      # 当前zk实例的id
      ZOO_MY_ID: 1
      # 整个zk集群的机器、端口列表
      ZOO_SERVERS: server.1=0.0.0.0:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888;2181
    # 将docker container上的路径挂载到宿主机上 实现宿主机和docker container的数据共享
    volumes:
      - ./zk1/data:/data
      - ./zk1/datalog:/datalog
    # 当前docker container加入名为zk-net的隔离网络
    networks:
      - zk-net

  zk2:
    image: zookeeper
    hostname: zk2
    container_name: zk2
    ports:
      - 2182:2181
      - 8082:8080
    environment:
      ZOO_MY_ID: 2
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=0.0.0.0:2888:3888;2181 server.3=zk3:2888:3888;2181
    volumes:
      - ./zk2/data:/data
      - ./zk2/datalog:/datalog
    networks:
      - zk-net

  zk3:
    image: zookeeper
    hostname: zk3
    container_name: zk3
    ports:
      - 2183:2181
      - 8083:8080
    environment:
      ZOO_MY_ID: 3
      ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888;2181 server.3=0.0.0.0:2888:3888;2181
    volumes:
      - ./zk3/data:/data
      - ./zk3/datalog:/datalog
    networks:
      - zk-net

然后使用 docker-compose 安装的方式安装即可。

然后直接访问下面的地址,有出现画面就是成功:
在这里插入图片描述

接着在 SpringBoot 里面配置对应的信息即可:
在这里插入图片描述

这么配置就可以了。

然后重新启动项目,访问下面地址就可以获取 id:
http://localhost:8080/api/snowflake/get/test

效果:
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值