数据库ID生成策略是数据库表设计中不可缺少的字段。 如何优雅地设计数据库ID以适应当前的业务场景,需要根据需要选择合适高效的战略。 在复杂的分布式系统中,往往需要唯一地识别大量的数据和消息。 介绍几种常见的身份生成策略。
Sequence ID (数据库自增长)数据库自增长序列或字段,最常用的方法。 由数据库维护,数据库表是唯一的。
优点
简单,代码方便,性能可接受。 数字ID自然排序,有助于需要分页和排序的结果。缺点
数据库语法和实现要求在数据库迁移或支持多数据库版本时进行处理。 如果一个数据库或读写分离,或者有多个主数据库,则只能生成一个主库。 有单点故障的风险。 如果性能不满足要求,则扩展相对困难。 如果需要整合多个系统,或者与数据迁移相关,则会带来相当大的痛苦。 分钟分库的时候有麻烦。 通用唯一标识符(uuid )标准样式包含32个十六进制数字,用连字符分为五段,形状为8-4-4-4-12,共36个字符。
确保由MAC地址、时间戳、命名空间、随机数和伪随机数生成的ID的唯一性。
使用JAVA代码生成
String uuid=UUID.randomUUID ().toString ); system.err.println(uuid; //a 299 D6 ef-9f 82-475 e-B3 D5-F16 df 808 FEA1system.err.println (uuid.length ); 使用Mysql函数生成:
uid(uuid主要有五种算法。
uuid1() )。
基于时间戳。 由MAC地址、当前时间戳和随机数生成。 虽然保证了全球范围内的唯一性,但MAC的使用同时也带来了安全问题,可以在局域网上使用IP而不是MAC。 uuid2() )。
基于分布式计算环境DCE (python中没有此函数)。 算法与uUID1相同,不同之处在于将时间戳的前四个位置更改为POSIX的uid。 实际上这个方法很少使用。 uuid3() )。
基于名称的MD5散列值。 计算名称和命名空间的MD5哈希值可确保同一命名空间中不同名称的唯一性和不同命名空间的唯一性,但同一命名空间中的相同名称会生成相同的uuid。 uuid4() )
基于随机数。 从伪随机数中得到,有一定的重复概率,其概率可以计算。 uuid5() )。
基于名称的SHA-1哈希值。 算法与uuid3相同,不同之处在于使用安全散列算法1。优点
简单,代码方便。 世界上唯一一个在遇到数据迁移、系统数据整合或数据库更改等情况下能够冷静应对的人。 本地生成,没有网络消耗。
缺点:不容易保存: UUID太长,16字节128位,通常用36个长度的字符串表示,很多场景不适用。 信息不安全—基于MAC地址生成UUID的算法可能会导致MAC地址泄漏。 这个漏洞曾被用来寻找梅利莎病毒创建者的位置。 MySQL公式明确建议主键尽可能短[4],长度为36个字符的UUID不符合要求。 不利于MySQL索引:用作数据库的主键时,在InnoDB引擎下,UUID的无序性可能会频繁变动数据位置,严重影响性能。 雪花算法snowflake是一种推特开源分布式ID生成算法,结果是长整型ID。 其核心思想使用41位作为毫秒,10位作为机器ID(5bit是数据中心,5bit的机器ID ),12位作为毫秒内的流水号),意味着每个节点每毫秒可以生成4096个ID ) snowflake算法可以根据自己的项目需要进行一定的修改。 例如,通过估计未来数据中心的数量、每个数据中心的计算机数量和集成毫秒内可能的并发行数来调整算法所需的位数。
//参数1是终端ID //参数2是数据中心idsnowflakesnowflake=id util.gets now flake (1,1 ); long id=snowflake.nextId (; system.err.println(id; //13839690992935280643358 www.Sina.com /
毫秒数在高位,自增序列在低位,整个ID呈趋势性增长。 不依赖于数据库等第三方系统,作为服务导入稳定性更高,ID生成的性能也非常高。 可以根据自己的业务特性分配bit位,非常灵活。优点:高度依赖机器时钟,如果时钟回拨到机器,则传出号码会重复或服务不可用。 开源分布式ID生成器百度uid -生成器地址
uid生成器由Java实现,是唯一基于Snowflake的id生成器。 充当组件,允许用户覆盖workId位和初始化策略。 因此,它适用于docker等虚拟化环境。 除此之外,我通过消耗未来的时间克服了
Snowflake算法的并发限制。通过使用RingBuffer缓存UID来并行化UID产生和使用;通过填充消除了来自RingBuffer的CacheLine伪共享。最后,每个实例可以提供超过600万个QPS。 滴滴Tinyid地址
Tinyid是ID生成器服务。它提供了一个REST API和一个用于获取ID的Java客户端。使用Java客户端时,每个单个实例超过1000万个QPS。
地址
Leaf指的是行业中一些常见的ID生成方案,包括redis,UUID,snowflare等。以上每种方法都有其自身的问题,因此我们决定实施一套分布式ID生成服务以满足要求。目前,Leaf负责美团点评公司的内部财务,餐饮,外卖,酒店旅行,猫眼电影和许多其他业务。在4C8G VM的基础上,通过公司的RPC方法,QPS压力测试结果接近5w / s,TP999为1ms。
MybatisPlus提供了多种ID生成策略。
IdType生成ID类型枚举类:
public enum IdType { /** * 数据库ID自增 * <p>该类型请确保数据库设置了 ID自增 否则无效</p> */ AUTO(0), /** * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) */ NONE(1), /** * 用户输入ID * <p>该类型可以通过自己注册自动填充插件进行填充</p> */ INPUT(2), /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */ /** * 分配ID (主键类型为number或string), * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法) * * @since 3.3.0 */ ASSIGN_ID(3), /** * 分配UUID (主键类型为 string) * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-","")) */ ASSIGN_UUID(4), /** * @deprecated 3.3.0 please use {@link #ASSIGN_ID} */ @Deprecated ID_WORKER(3), /** * @deprecated 3.3.0 please use {@link #ASSIGN_ID} */ @Deprecated ID_WORKER_STR(3), /** * @deprecated 3.3.0 please use {@link #ASSIGN_UUID} */ @Deprecated UUID(4); private final int key; IdType(int key) { this.key = key; }} AUTO数据库ID自增,该类型请确保数据库设置了ID自增, 否则无效。
设置数据库表ID自增
设置实体类IdType为AUTO
@TableId(value = "id", type = IdType.AUTO) private Integer id; 插入数据测试 OrderTbl orderTbl = new OrderTbl().setMoney(100).setUserId("123").setCommodityCode("PHONE"); orderTblMapper.insert(orderTbl); NONE该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)。
取消数据库表ID自增设置实体类IdType为NONE @TableId(value = "id", type = IdType.NONE) private Integer id; 插入数据测试 public void insertTest() { OrderTbl orderTbl = new OrderTbl().setId(11111).setMoney(100).setUserId("123").setCommodityCode("PHONE"); orderTblMapper.insert(orderTbl); } INPUT用户输入ID,该类型可以通过自己注册自动填充插件进行填充(经测试MetaObjectHandler并不能实现主键ID自动填充,因为自动填充时是获取TableInfo字段信息循环字段并填充,但是TableInfo字段中不包含主键,所以无法填充)。应该可以使用mybatis插件实现(未测试)。
方式一:
取消数据库表ID自增设置实体类IdType为INPUT @TableId(value = "id", type = IdType.INPUT) private Integer id; 插入数据测试同NONE步骤 ASSIGN_ID分配ID (主键类型为number或string),默认实现类 DefaultIdentifierGenerator(雪花算法)。
方式一使用默认生成器:
取消数据库表ID自增设置实体类IdType为ASSIGN_ID,字段类型为Long @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; 插入数据测试同NONE步骤方式二使用自定义ID生成器:取消数据库表ID自增设置实体类IdType为ASSIGN_ID,字段类型为Long @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; 添加自定义ID生成器 @Componentpublic class CustomIdGenerator implements IdentifierGenerator { @Override public Long nextId(Object entity) { //使用 hutools 雪花算法生成分布式ID //参数1为终端ID //参数2为数据中心ID Snowflake snowflake = IdUtil.getSnowflake(1, 1); return snowflake.nextId(); }} 插入数据测试同NONE步骤