分库分表之后,ID主键如何处理?
当业务量大的时候,数据库中数据量过大,就要进行分库分表了,那么分库分表之后,必然将面临一个问题,那就是ID怎么生成?因为要分成多个表之后,如果还是使用每个表的自增长ID,意味着每个表都是从1开始累加的,这样肯定是不对的。需要一个全局唯一的ID来支持。所以这也是你实际生产环境中必须考虑的一个问题。全局ID生成器,一般需要满足下列几个特性:
唯一性、高可用、递增性、安全性、高可用性
常用的主键ID生成策略有以下几种:
数据库自增ID
原理:
如果使用这种方式,那么这就意味着,你的系统里每次得到一个ID,都需要往一个库中的一个表中插入一条没有什么业务含义的数据,然后获取一个数据库自增的id.拿到这个ID之后,再往对应的分库分表里写。
这种方式的优缺点如下:
优点:非常简单,有序递增,方便分页和排序。
缺点:
a.分库分表之后,数据表的自增ID容易重复,无法直接使用(虽然可以设置步长,但是局限性明显);
b.性能吞吐量整个比较低。如果设计一个单独的数据库来实现分布式应用的数据唯一性,即使使用预先生成方案,也会因为事务问题,在高并发场景下容易出现单点的瓶颈问题。
使用场景:单数据库实例的表ID(包含主从同步场景);部分按天计数的流水号等
在分不分表场景、全局唯一性ID场景下不使用。
Redis生产全局ID
原理:
通过Redis的INCR/INCRBY自增原子操作命令,能保证生产的ID肯定是唯一的序列号,本质上实现方式与数据库一致的。
使用Redis生产全局ID的优缺点:
优点:整体吞吐量比数据库要高。因为Redis的吞吐量性能高于数据库
缺点:Redis实例或者集群宕机后,找回最新的ID值比较麻烦。但是可以在生产唯一ID的算法上进行优化,避免这种情况。
使用场景:比较适合计算场景。比如用户访问量、订单流水号(日期+流水号)等。
凯哥推荐文章:Redis实战9-全局唯一ID
UUID、GUID生成ID
优缺点:
优点:性能非常高。在本地生成,没有网络消耗;
缺点:UUID太长了,占用空间大,作为主键性能太差了;
由于UUI不具有有序性,会导致B+树索引在写的时候有过多的随机写操作。
使用场景:如果你要随机生成一个什么文件名称、编号之类的,可以考虑使用UUID,但是如果是作为数据库的主键,不建议使用UUID的。
雪花算法(snowflake)
雪花算法来源于Twitter,使用Scala语言实现,雪花算法的特性是有序、唯一且要求性能高,低延迟(每台集群每秒至少生成10K条数据,并且响应时间在2MS内),要在分布式环境(多集群、跨机房)下使用。因此雪花算法得到的ID是分段组成的。
a.与指定日期时间差(时间差到毫秒级)的,41位数字,可以使用69年;
b.机器ID+集群ID,10位,最多支持1024台机器;
c.序列号,12位。每台机器每毫秒内最多生产4096个序列号.
雪花算法的核心思想是:
分布式ID固定是一个long类型的数字,一个long类型占用8个字节,也就是8*8=64个bit位。所以,雪花算法的格式如下图:
雪花算法分段,每段含义:
第一段:也就是最高1位是符号位。固定值,就是0,标识全部ID都是正整数。
第二段:接下来的41位,标识的是时间戳。单位是毫秒。41bits标识的数字对应的是2^41次方-1.也就是可以标识2的41次方-1个毫秒值。换算成年就是标识69年的时间;
第三段:再接下来的10位标识的是机器ID。如果有异地部署,多集群的也可以配置,需要在线下提前规划好各地机房,各个集群,实例ID的编号。其中包括5位的机器id和5位的集群id.最多可以部署2^10台机器。也就是1024台。
第四段:最后12位是序列号。用于记录同一毫秒内产生的不同ID.12个比特位可以代表的最大正整数是2^12-1=4096.也就是说,可以用这12个bits代表数字来区分同一毫秒内4096个不同的ID.
此算法的优缺点如下:
雪花算法的优缺点:
优点:毫秒数在高位,自增序列在低位,所以整个ID都呈现出递增趋势;
不依赖数据库等三方系统,以服务部署方式,稳定性更高,生成ID的性能也是非常高的;
可以根据自身业务特性来分配bit位,非常灵活。
缺点:
太依赖集群的时钟,如果机器时钟回拨了,可能会导致重复或者服务处于不可用。
结束语
大家好,我是凯哥Java(kaigejava),乐于分享技术文章,欢迎大家关注“凯哥Java”,及时了解更多。让我们一起学Java。也欢迎大家有事没事就来和凯哥聊聊~~~