什么是分布式ID
以MySQL为例,数据量不大的时候,单库单表可以支撑现有业务,再大一点主从同步读写分离也可以。
但数据是不断增加的,当主从同步也不行了,就需要分库分表了。分库分表需要一个全局唯一ID做标识,这就是分布式ID。一个能生成分布式ID的系统是十分有必要的。
分布式ID特点
- 全局唯一:基本要求
- 高性能:高可用低延时,ID生成相应快,否则成为业务瓶颈;
- 高可用:不能有单点故障
- 好接入:在系统设计和实现上尽可能地简单;
- 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,我们应该尽量使用有序的主键保证写入性能;
- 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序。
- 信息安全:在一些应用场景下,会需要ID无规则、不规则生成,例如订单号。
- 分片支持:可以控制ShardingId。比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易。
- 长度适中
生成方式
- UUID
- 数据库自增ID
- 数据库多主模式
- 数据库号段模式
- Redis
- 雪花算法(SnowFlake)
- 滴滴出品(TinyID)
- 百度(Uidgenerator)
- 美团(Leaf)
- zookeeper
1.UUID
UUID uuid=UUID.randomUUID();
UUID的标准形式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000。这种ID每个都是独一无二的,不用担心冲突问题。
优点:
- 生成简单,本地生成无网络消耗,有唯一性
缺点:
- 无序字符串,不具备趋势递增的特性;
- 没有具体含义;
- 长度过长对MySQL性能消耗较大;
- 基于MAC地址生成的UUID可能造成MAC地址泄露
2.数据库自增ID
基于数据库的auto_increment自增ID完全可以充当分布式ID,需要一个单独的MySQL实例用来生成ID,建表结构如下:
CREATE DATABASE `SEQ_ID`;
CREATE TABLE SEQID.SEQUENCE_ID (
id bigint(20) unsigned NOT NULL auto_increment,
value char(10) NOT NULL default '',
PRIMARY KEY (id),
) ENGINE=MyISAM;
insert into SEQUENCE_ID(value) VALUES ('values');
当我们需要一个ID的时候,向表中插入一条记录返回主键ID
优点:
- 简单,利用数据库系统功能实现,成本小,有DBA专业维护
- ID号单调递增
缺点:
- 太依赖DB,当DB异常时整个系统不可用,属于致命问题。
- 配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。
- ID发号性能瓶颈限制在单台MySQL的读写性能
- 访问量激增时MySQL本身就是系统的瓶颈,用它来实现分布式服务风险比较大
3.数据库多主模式
对上边的方式做一些高可用优化,换成主从模式集群。再增加多几个Mysql实例,那就是多主模式集群了。
设置起始值和自增步长来解决重复ID的问题,例如:
MySQL_1起始值为1,步长为2,生成ID:1,3,5…
MySQL_2起始值为2,步长为2,生成ID:2,4,6…
当添加多一个MySQL_3时,起始值为3,步长统一改为3,那么就是
1,4,7…
2,5,8…
3,6,9…
优点:解决DB单点问题
缺点:不利于后续扩容,而且实际上单个数据库自身压力还是大,依旧无法满足高并发场景
4.数据库号段模式
从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存。表结构如下:
CREATE TABLE id_generator (
id int(10) NOT NULL,
max_id