分布式系统进阶八之手写分布式ID生成组件

文章详细描述了一个基于MySQL的ID生成系统,通过leaf_alloc表管理和序列类(Sequence)来控制每个表的ID分配,同时介绍了IdGenerate类的初始化和使用方法。
摘要由CSDN通过智能技术生成

架构图

这种模式需要依赖MySQL,表字段table_name代表表名,max_id代表该表目前已分配的最大ID值,step代表每次Leaf往数据库请求时,一次性分配的ID数量。

ID申请表leaf_alloc

CREATE TABLE `leaf_alloc` (
  `table_name` varchar(32) CHARACTER SET latin1 NOT NULL COMMENT '表名称',
  `max_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '当前号段下的最大 id',
  `step` int(11) unsigned NOT NULL DEFAULT '1' COMMENT '每次取号段的步长',
  `desc` varchar(64) NOT NULL COMMENT '描述信息',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`table_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='叶子申请表';

引入依赖

		<dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

序列类定义

/**
 * 序列类定义
 *
 * @author yangyanping
 * @date 2024-04-01
 */
@Data
@Slf4j
public class Sequence {

    /**
     * 容量大小
     */
    private int blockSize = 100;

    /**
     * 初始值大小
     */
    private long startValue = 0;

    /**
     * 数据源
     */
    private DataSource dataSource;

    private Map<String, Step> stepMap = new HashMap<>();

    public Sequence() {
    }

    public synchronized long get(String sequenceName) {
        Step step = this.stepMap.get(sequenceName);
        if (step == null) {
            step = new Step(this.startValue, this.startValue + this.blockSize);
            this.stepMap.put(sequenceName, step);
        } else if (step.currentValue < step.endValue) {
            return step.incrementAndGet();
        }

        for (int i = 0; i < this.blockSize; ++i) {
            if (this.getNextBlock(sequenceName, step)) {
                return step.incrementAndGet();
            }
        }

        throw new RuntimeException("get error.");
    }

    private boolean getNextBlock(String sequenceName, Step step) {
        Long value = this.getPersistenceValue(sequenceName);
        if (value == null) {
            try {
                value = this.newPersistenceValue(sequenceName);
            } catch (Exception var5) {
                log.error("newPersistenceValue error!");
                value = this.getPersistenceValue(sequenceName);
            }
        }

        boolean b = this.saveValue(value, sequenceName) == 1;
        if (b) {
            step.setCurrentValue(value);
            step.setEndValue(value + this.blockSize);
        }

        return b;
    }

    private int saveValue(long value, String sequenceName) {
        Connection connection = null;
        PreparedStatement statement = null;

        int var8;
        try {
            connection = this.dataSource.getConnection();
            statement = connection.prepareStatement("update leaf_alloc set max_id = ?  where table_name = ? and max_id = ?");
            statement.setLong(1, value + this.blockSize);
            statement.setString(2, sequenceName);
            statement.setLong(3, value);
            var8 = statement.executeUpdate();
        } catch (Exception var18) {
            log.error("newPersistenceValue error!", var18);
            throw new RuntimeException("newPersistenceValue error!", var18);
        } finally {
            this.close(null, statement, connection);
        }

        return var8;
    }

    private Long getPersistenceValue(String sequenceName) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = this.dataSource.getConnection();
            statement = connection.prepareStatement("select max_id from leaf_alloc where table_name = ?");
            statement.setString(1, sequenceName);
            resultSet = statement.executeQuery();
            if (resultSet.next()) {
                return resultSet.getLong("max_id");
            }
        } catch (Exception var23) {
            log.error("getPersistenceValue error!", var23);
            throw new RuntimeException("getPersistenceValue error!", var23);
        } finally {
            this.close(resultSet, statement, connection);
        }

        return null;
    }

    private Long newPersistenceValue(String sequenceName) {
        Connection connection = null;
        PreparedStatement statement = null;

        try {
            connection = this.dataSource.getConnection();
            statement = connection.prepareStatement("insert into leaf_alloc (max_id,table_name) values (?,?)");
            statement.setLong(1, this.startValue);
            statement.setString(2, sequenceName);
            statement.executeUpdate();
        } catch (Exception var15) {
            log.error("newPersistenceValue error!", var15);
            throw new RuntimeException("newPersistenceValue error!", var15);
        } finally {
            this.close(null, statement, connection);
        }

        return this.startValue;
    }

    private void close(ResultSet resultSet, PreparedStatement statement, Connection connection) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException var22) {
                log.error("close resultset error!", var22);
            }
        }

        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException var14) {
                log.error("close statement error!", var14);
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException var13) {
                log.error("close connection error!", var13);
            }
        }
    }

    /**
     * 步长定义
     */
    @Data
    static class Step {
        /**
         * 初始值
         */
        private long currentValue;

        /**
         * 截止值
         */
        private long endValue;

        Step(long currentValue, long endValue) {
            this.currentValue = currentValue;
            this.endValue = endValue;
        }

        public long incrementAndGet() {
            return ++this.currentValue;
        }
    }
}

ID生成类定义

/**
 * ID生成类定义
 *
 * @author yangyanping
 * @date 2024-04-01
 */
@Slf4j
public class IdGenerate  {
    private static Map<String, Sequence> sequenceMap;
    private static Sequence defaultSequence;

    private static DataSource dataSource;

    public IdGenerate(DataSource ds) {
        dataSource = ds;
    }

    public static long getNetId(String tableName) {
        Sequence sequence = null;

        if (sequenceMap != null) {
            sequence = sequenceMap.get(tableName);
        }

        if (sequence == null) {
            if (defaultSequence != null) {
                return defaultSequence.get(tableName);
            } else {
                throw new RuntimeException("sequence " + tableName + " undefined!");
            }
        } else {
            return sequence.get(tableName);
        }
    }

    public void init() throws Exception {
        sequenceMap = new HashMap<>();

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = dataSource.getConnection();
            statement = connection.prepareStatement("select table_name,max_id,step from leaf_alloc");
            resultSet = statement.executeQuery();

            while (resultSet.next()) {
                Integer step = resultSet.getInt("step");
                String name = resultSet.getString("table_name");

                Sequence sequence = new Sequence();
                sequence.setStartValue(0);
                sequence.setBlockSize(step);
                sequence.setDataSource(dataSource);

                if ("default".equalsIgnoreCase(name)) {
                    defaultSequence = sequence;
                } else {
                    sequenceMap.put(name, sequence);
                }

                log.warn("leaf-Id-tableName={},step={}", name, step);
            }
        } catch (Exception var23) {
            log.error("afterPropertiesSet error!", var23);
            throw new RuntimeException("afterPropertiesSet error!", var23);
        } finally {
            this.close(resultSet, statement, connection);
        }
    }

    private void close(ResultSet resultSet, PreparedStatement statement, Connection connection) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException var22) {
                log.error("close resultset error!", var22);
            }
        }

        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException var14) {
                log.error("close statement error!", var14);
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException var13) {
                log.error("close connection error!", var13);
            }
        }
    }
}

配置IdGenerate Bean

@Configuration
public class BeanConfig {

    @Bean(name = "idGenerate", initMethod = "init")
    public IdGenerate idGenerate(@Autowired @Qualifier("baseDataSource") DataSource dataSource) {
        return new IdGenerate(dataSource);
    }
}

使用

@Test
public void test(){
  long id = IdGenerate.getNetId("user);
}
  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值