跳至主要內容

自增主键

chanchaw大约 3 分钟java

概述

单体应用中常见使用自增主键作为数据的唯一标识,但是在数据量大起来后,如果要分库分表则会产生冲突。每个分表中都有一样的ID的主键,要解决这个问题可以采用步长的方法解决。

自增主键

起始+步长分表

有分表的情况下通过步长来区分每个分表的数据,例如用户表 user01 采用起始1步长3,则自动生成的主键是:1,4,7...,那么分表 user02 起始2步长同样是3,则主键依次是:2,5,8......,类似的分表 user03 的起始是3步长也是3,则主键依次是:3,6,9......。

UUID

InnoDB 的索引结构是 B+ 树,主键索引树的叶子节点保存具体的数据,page 页默认 16k 。由于 UUID 占用空间比较大,导致索引树高度大,即深度大,遍历的次数增加,查询速度就会慢下来。UUID 是无序的,非趋势递增的,在每次插入新数据时都要先排序再插入,这里的先排序是索引树的分裂与合并,从而导致插入新数据的速度慢。所以,不光是 UUID ,应该要避免所有非趋势递增的数据类型作为主键。

雪花片

一个十进制整数的二进制表示法,长度为64位,由4部分组成。二进制表示法中左起第一位表示正负数,后面的 41bit 表示时间戳,后面的 10bit 是计算机ID,最后 12bit 是序列号。雪花片算法存在三个问题:时间回拨、机器ID管理、序列号一直是0

时间回拨


修复服务器时间为过去的一个时间会导致ID不是趋势递增,甚至可能生成的重复的ID。

为避免该问题,在生成ID时需要检测当前的时间戳如果小于之前生成的抛出异常不予生成,或者设置等待时间,超时后再抛异常。也可以更换机器ID进行生成,当然更换机器ID时也要检测对于新的机器ID是否存在时间回拨的情况。也可以制定备用方法,当时间回拨时生成随机数作为ID返回给调用方。

机器ID管理


通过配置文件可设置机器ID,但是在大规模分布式系统中要管理大量的服务器ID则需要很大的管理成本、

可通过服务注册的ID解决管理大量机器ID的问题

序列号一直是0


在一台服务器的同一个时间下并发生成很多的ID时才会递增序列号,那么在没有大规模并发的情况下则序列号一直是0。当基于ID取模进行分库分表时会导致数据偏移。例如当取奇偶数进行分库分表时会导致部分表没有数据,0一直是偶数。

当检测到序列号部分是0时,可使用时间戳的最后一位来填充序列号的数据(二进制表示的最后一位是0会使ID转换为十进制后一直是偶数)