分布式ID选型对比(1)

常见的几种ID生成方式对比:

种类

全局唯一

高性能

高可用

趋势递增

中心服务

缺点

UUID

高(本地生成,(无网络开销)

低(无序,不适用)

无序、字符串

数据库自增

单表唯一

中(宕机就会使业务服务中断)

安全性差,能猜出来规律

对于分库分表场景无法唯一

数据库自增-集群

中(宕机就会使业务服务中断)

通过设置初始值及步长进行自增

不好扩展,增加节点可能导致不唯一

数据库号段模式

低(需要频繁更新表maxId)

中(宕机就会使业务服务中断)

依赖数据库,需要频繁更新数据库maxId

乐观锁方式保证高可用

数据库号段模式+缓存

在号段模式基础上增加缓存buffer

缓存数据同步到数据库需要保证强一致性,增加了复杂度

redis的incr

单模块唯一

多模块不适用

中(宕机就会使业务服务中断)

多服务情况下单台redis无法满足

多redis集群时,主master宕机,会在较短时间内存在ID重复

雪花算法

高(不考虑回拨情况下)

高(本地生成,(无网络开销)

中(趋势递增)

存在时钟回拨问题

多节点时可能需要自定义datacenterId和machineId的生成(IP)

百度uid generator

高(本地生成,(无网络开销)

高(趋势递增)

在雪花算法基础上增加启动服务时获取workId(或初始化workId)

项目启动时间会被拉长,依赖数据库

美团Leaf

高(本地生成,(无网络开销,需要自己实现)

中(中心服务)

高(自定义方式较复杂)

百度uid模式下衍生而来

可以实现自己的ID生成中心,即leaf-server,由该项目提供获取ID的方法供调用,但该方式比较耗IO,性能会有所下降

依赖数据库或zookeper

为了提升性能,需要自己在项目里面自定义实现Leaf server

滴滴TynyId

高(本地生成,(无网络开销,需要自己实现)

中(中心服务)

高(自定义方式较复杂)

美团Leaf模式下衍生而来

可以实现自己的ID生成中心,即TynyId-server,由该项目提供获取ID的方法供调用,但该方式比较耗IO,性能会有所下降

依赖数据库或zookeper

为了提升性能,需要自己在项目里面自定义实现TynyId server

----------------------------------------------------------------------------------------------

数据库自增

一, 引入依赖:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.19</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

二, 新建表

CREATE TABLE `auto_generate` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

三, resources目录下编写自动生成代码配置文件: generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--mysql 连接数据库jar 这里选择自己本地位置;
    如果不知道maven本地仓库地址,可以使用EveryThing工具全局搜索mysql-connector-java,找到jar包位置;
    也可以手动下载一个jar放在指定位置,进行引用。
    -->
    <classPathEntry location="/Users/lizhihao/.m2/repository/mysql/mysql-connector-java/8.0.19/mysql-connector-java-8.0.19.jar"/>

    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释,true:是,false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/generate_id" userId="root"
                        password="root">
        </jdbcConnection>

        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
           NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!-- 指定javaBean生成的位置
            targetPackage:生成的类要放的包,真实的包受enableSubPackages属性控制;
            targetProject:目标项目,指定一个存在的目录下,生成的内容会放到指定目录中,如果目录不存在,MBG不会自动建目录
         -->
        <javaModelGenerator targetPackage="org.com.spi.model" targetProject="src/main/java">
            <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false;如果多个数据库改为true分目录 -->
            <property name="enableSubPackages" value="false"/>
            <!-- 设置是否在getter方法中,对String类型字段调用trim()方法 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!--  指定mapper映射文件生成的位置
           targetPackage、targetProject同javaModelGenerator中作用一样-->
        <sqlMapGenerator targetPackage="mybatis" targetProject="src/main/resources">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- 指定mapper接口生成的位置
         targetPackage、targetProject同javaModelGenerator中作用一样
         -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="org.com.spi.dao" targetProject="src/main/java">
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>
        <!-- 指定数据库表
        domainObjectName:生成的domain类的名字,当表名和domain类的名字有差异时一定要设置,如果不设置,直接使用表名作为domain类的名字;
        可以设置为somepck.domainName,那么会自动把domainName类再放到somepck包里面;
        -->
        <table tableName="worker_node"></table>
<!--        <table tableName="auto_generate2" domainObjectName="AutoGenerate2"></table>-->
    </context>
</generatorConfiguration>

application.properties数据库配置:

#   ???url
spring.datasource.url=jdbc:mysql://localhost:3306/generate_id?characterEncoding=utf8&serverTimezone=UTC    
#  ??????
spring.datasource.username=root
#  ?????
spring.datasource.password=root
#  ?????
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
###################################### MyBatis ??######################################
# ?? mapper.xml ???
mybatis.mapper-locations=classpath:mybatis/*.xml
#????????,?????????????? mapper.xml ??????????????
mybatis.type-aliases-package=net.biancheng.www.bean
#???????????????????
mybatis.configuration.map-underscore-to-camel-case=true

四, 编写层级代码

1, 对象 AutoGenerate

package org.com.spi.model;

public class AutoGenerate {
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }
}

AutoGenerateExample:

package org.com.spi.model;

import java.util.ArrayList;
import java.util.List;

public class AutoGenerateExample {
    protected String orderByClause;

    protected boolean distinct;

    protected List<Criteria> oredCriteria;

    public AutoGenerateExample() {
        oredCriteria = new ArrayList<Criteria>();
    }

    public void setOrderByClause(String orderByClause) {
        this.orderByClause = orderByClause;
    }

    public String getOrderByClause() {
        return orderByClause;
    }

    public void setDistinct(boolean distinct) {
        this.distinct = distinct;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public List<Criteria> getOredCriteria() {
        return oredCriteria;
    }

    public void or(Criteria criteria) {
        oredCriteria.add(criteria);
    }

    public Criteria or() {
        Criteria criteria = createCriteriaInternal();
        oredCriteria.add(criteria);
        return criteria;
    }

    public Criteria createCriteria() {
        Criteria criteria = createCriteriaInternal();
        if (oredCriteria.size() == 0) {
            oredCriteria.add(criteria);
        }
        return criteria;
    }

    protected Criteria createCriteriaInternal() {
        Criteria criteria = new Criteria();
        return criteria;
    }

    public void clear() {
        oredCriteria.clear();
        orderByClause = null;
        distinct = false;
    }

    protected abstract static class GeneratedCriteria {
        protected List<Criterion> criteria;

        protected GeneratedCriteria() {
            super();
            criteria = new ArrayList<Criterion>();
        }

        public boolean isValid() {
            return criteria.size() > 0;
        }

        public List<Criterion> getAllCriteria() {
            return criteria;
        }

        public List<Criterion> getCriteria() {
            return criteria;
        }

        protected void addCriterion(String condition) {
            if (condition == null) {
                throw new RuntimeException("Value for condition cannot be null");
            }
            criteria.add(new Criterion(condition));
        }

        protected void addCriterion(String condition, Object value, String property) {
            if (value == null) {
                throw new RuntimeException("Value for " + property + " cannot be null");
            }
            criteria.add(new Criterion(condition, value));
        }

        protected void addCriterion(String condition, Object value1, Object value2, String property) {
            if (value1 == null || value2 == null) {
                throw new RuntimeException("Between values for " + property + " cannot be null");
            }
            criteria.add(new Criterion(condition, value1, value2));
        }

        public Criteria andIdIsNull() {
            addCriterion("id is null");
            return (Criteria) this;
        }

        public Criteria andIdIsNotNull() {
            addCriterion("id is not null");
            return (Criteria) this;
        }

        public Criteria andIdEqualTo(Long value) {
            addCriterion("id =", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdNotEqualTo(Long value) {
            addCriterion("id <>", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdGreaterThan(Long value) {
            addCriterion("id >", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdGreaterThanOrEqualTo(Long value) {
            addCriterion("id >=", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdLessThan(Long value) {
            addCriterion("id <", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdLessThanOrEqualTo(Long value) {
            addCriterion("id <=", value, "id");
            return (Criteria) this;
        }

        public Criteria andIdIn(List<Long> values) {
            addCriterion("id in", values, "id");
            return (Criteria) this;
        }

        public Criteria andIdNotIn(List<Long> values) {
            addCriterion("id not in", values, "id");
            return (Criteria) this;
        }

        public Criteria andIdBetween(Long value1, Long value2) {
            addCriterion("id between", value1, value2, "id");
            return (Criteria) this;
        }

        public Criteria andIdNotBetween(Long value1, Long value2) {
            addCriterion("id not between", value1, value2, "id");
            return (Criteria) this;
        }

        public Criteria andNameIsNull() {
            addCriterion("name is null");
            return (Criteria) this;
        }

        public Criteria andNameIsNotNull() {
            addCriterion("name is not null");
            return (Criteria) this;
        }

        public Criteria andNameEqualTo(String value) {
            addCriterion("name =", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameNotEqualTo(String value) {
            addCriterion("name <>", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameGreaterThan(String value) {
            addCriterion("name >", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameGreaterThanOrEqualTo(String value) {
            addCriterion("name >=", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameLessThan(String value) {
            addCriterion("name <", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameLessThanOrEqualTo(String value) {
            addCriterion("name <=", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameLike(String value) {
            addCriterion("name like", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameNotLike(String value) {
            addCriterion("name not like", value, "name");
            return (Criteria) this;
        }

        public Criteria andNameIn(List<String> values) {
            addCriterion("name in", values, "name");
            return (Criteria) this;
        }

        public Criteria andNameNotIn(List<String> values) {
            addCriterion("name not in", values, "name");
            return (Criteria) this;
        }

        public Criteria andNameBetween(String value1, String value2) {
            addCriterion("name between", value1, value2, "name");
            return (Criteria) this;
        }

        public Criteria andNameNotBetween(String value1, String value2) {
            addCriterion("name not between", value1, value2, "name");
            return (Criteria) this;
        }
    }

    public static class Criteria extends GeneratedCriteria {

        protected Criteria() {
            super();
        }
    }

    public static class Criterion {
        private String condition;

        private Object value;

        private Object secondValue;

        private boolean noValue;

        private boolean singleValue;

        private boolean betweenValue;

        private boolean listValue;

        private String typeHandler;

        public String getCondition() {
            return condition;
        }

        public Object getValue() {
            return value;
        }

        public Object getSecondValue() {
            return secondValue;
        }

        public boolean isNoValue() {
            return noValue;
        }

        public boolean isSingleValue() {
            return singleValue;
        }

        public boolean isBetweenValue() {
            return betweenValue;
        }

        public boolean isListValue() {
            return listValue;
        }

        public String getTypeHandler() {
            return typeHandler;
        }

        protected Criterion(String condition) {
            super();
            this.condition = condition;
            this.typeHandler = null;
            this.noValue = true;
        }

        protected Criterion(String condition, Object value, String typeHandler) {
            super();
            this.condition = condition;
            this.value = value;
            this.typeHandler = typeHandler;
            if (value instanceof List<?>) {
                this.listValue = true;
            } else {
                this.singleValue = true;
            }
        }

        protected Criterion(String condition, Object value) {
            this(condition, value, null);
        }

        protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
            super();
            this.condition = condition;
            this.value = value;
            this.secondValue = secondValue;
            this.typeHandler = typeHandler;
            this.betweenValue = true;
        }

        protected Criterion(String condition, Object value, Object secondValue) {
            this(condition, value, secondValue, null);
        }
    }
}

2, dao层接口: AutoGenerateMapper

package org.com.spi.dao;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.com.spi.model.AutoGenerate;
import org.com.spi.model.AutoGenerateExample;
import org.springframework.stereotype.Component;

@Mapper
public interface AutoGenerateMapper {
    int countByExample(AutoGenerateExample example);

    int deleteByExample(AutoGenerateExample example);

    int deleteByPrimaryKey(Long id);

    int insert(AutoGenerate record);

    int insertSelective(AutoGenerate record);

    List<AutoGenerate> selectByExample(AutoGenerateExample example);

    AutoGenerate selectByPrimaryKey(Long id);

    int updateByExampleSelective(@Param("record") AutoGenerate record, @Param("example") AutoGenerateExample example);

    int updateByExample(@Param("record") AutoGenerate record, @Param("example") AutoGenerateExample example);

    int updateByPrimaryKeySelective(AutoGenerate record);

    int updateByPrimaryKey(AutoGenerate record);
}

3, controller层: GenerateIdController

@Autowired
AutoGenerateMapper autoGenerateMapper;
@PutMapping("/save")
public int save(@RequestBody AutoGenerate autoGenerate){
    return autoGenerateMapper.insert(autoGenerate);
}

4, resources/mybatis目录下建: AutoGenerateMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.com.spi.dao.AutoGenerateMapper">
  <resultMap id="BaseResultMap" type="org.com.spi.model.AutoGenerate">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
  </resultMap>
  <sql id="Update_By_Example_Where_Clause">
    <where>
      <foreach collection="example.oredCriteria" item="criteria" separator="or">
        <if test="criteria.valid">
          <trim prefix="(" prefixOverrides="and" suffix=")">
            <foreach collection="criteria.criteria" item="criterion">
              <choose>
                <when test="criterion.noValue">
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue">
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue">
                  and ${criterion.condition}
                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>
  <sql id="Base_Column_List">
    id, name
  </sql>
  <select id="selectByExample" parameterType="org.com.spi.model.AutoGenerateExample" resultMap="BaseResultMap">
    select
    <if test="distinct">
      distinct
    </if>
    <include refid="Base_Column_List" />
    from auto_generate
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null">
      order by ${orderByClause}
    </if>
  </select>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from auto_generate
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
    delete from auto_generate
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <delete id="deleteByExample" parameterType="org.com.spi.model.AutoGenerateExample">
    delete from auto_generate
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
  </delete>
  <insert id="insert" parameterType="org.com.spi.model.AutoGenerate">
    insert into auto_generate (id, name)
    values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="org.com.spi.model.AutoGenerate">
    insert into auto_generate
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="name != null">
        name,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=BIGINT},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <select id="countByExample" parameterType="org.com.spi.model.AutoGenerateExample" resultType="java.lang.Integer">
    select count(*) from auto_generate
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
  </select>
  <update id="updateByExampleSelective" parameterType="map">
    update auto_generate
    <set>
      <if test="record.id != null">
        id = #{record.id,jdbcType=BIGINT},
      </if>
      <if test="record.name != null">
        name = #{record.name,jdbcType=VARCHAR},
      </if>
    </set>
    <if test="_parameter != null">
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByExample" parameterType="map">
    update auto_generate
    set id = #{record.id,jdbcType=BIGINT},
      name = #{record.name,jdbcType=VARCHAR}
    <if test="_parameter != null">
      <include refid="Update_By_Example_Where_Clause" />
    </if>
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="org.com.spi.model.AutoGenerate">
    update auto_generate
    <set>
      <if test="name != null">
        name = #{name,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="org.com.spi.model.AutoGenerate">
    update auto_generate
    set name = #{name,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>
  <sql id="Example_Where_Clause">
    <where>
      <foreach collection="oredCriteria" item="criteria" separator="or">
        <if test="criteria.valid">
          <trim prefix="(" prefixOverrides="and" suffix=")">
            <foreach collection="criteria.criteria" item="criterion">
              <choose>
                <when test="criterion.noValue">
                  and ${criterion.condition}
                </when>
                <when test="criterion.singleValue">
                  and ${criterion.condition} #{criterion.value}
                </when>
                <when test="criterion.betweenValue">
                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
                </when>
                <when test="criterion.listValue">
                  and ${criterion.condition}
                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
                    #{listItem}
                  </foreach>
                </when>
              </choose>
            </foreach>
          </trim>
        </if>
      </foreach>
    </where>
  </sql>

</mapper>

特点: 对于高并发的系统,如果做了分库分表就不适合了,且数据库数量达到500万时就容易出现瓶颈,仅适合小数据量的系统.

 

 

 

 

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Loren_云淡风轻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值