Java 7.3 - 分布式 id

分布式 ID 介绍

什么是 ID?

ID 就是 数据的唯一标识。

什么是分布式 ID?

分布式 ID 是 分布式系统中的 ID,它不存在于现实生活,只存在于分布式系统中。

分库分表:

一个项目,在上线初期使用的是单机 MySQL。但随着需求不断增长,单机 MySQL 已经无法满足当前的需求,我们需要进行分库分表。

分库分表后,数据分布在不同服务器的数据库上,数据库的自增主键无法满足 ID 的唯一性了。此时我们如何为不同的节点生成全局唯一的主键呢?------分布式 ID

分布式 ID 需要满足哪些要求?

1、全局唯一

2、高可用:生成分布式 ID 的服务要保证可能性接近 100%

3、高性能:生成速度要快

4、方便易用

以上四点为分布式 ID 的基本要求,一个好的分布式 ID 还需要满足下列需求:

1、安全

2、有序递增

3、有具体的业务含义:生成的 ID 如果能有具体的业务含义,可以让定位问题以及开发更加透明化

4、独立部署:分布式系统专门有一个服务用来生成分布式 ID,可以和业务相关的服务解耦。

分布式 ID 常见的解决方案有哪些?

数据库

数据库主键自增

通过关系型数据库的自增主键来产生唯一 ID

以 MySQL 为例:

sql 复制代码
CREATE TABLE `sequence_id` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`stub` char(10) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

创建一个表,其主键自增。

sql 复制代码
BEGIN;
REPLACE INTO sequence_id (stub) VALUES ('stub');
SELECT LAST_INSERT_ID();
COMMIT;

插入数据我们使用 replace into 代替 insert into,解释如下:

这种方式的优缺点------

优点:实现简单

缺点:并发量不大,存在数据库单点问题、ID 没有业务含义、安全问题(通过 ID 自增量来判断每天的订单量)、每次获取 ID 都要访问数据库

数据库号段模式

对于数据库主键自增的方法,每有一个订单就需要访问一次数据库,性能比较差。我们可以通过批量获取,存在内存中,需要用到的时候直接从内存中取即可。

以 MySQL 为例:

1、创建一个数据库表

sql 复制代码
CREATE TABLE `sequence_id_generator` (
`id` int(10) NOT NULL,
`current_max_id` bigint(20) NOT NULL COMMENT '当前最⼤id',
`step` int(10) NOT NULL COMMENT '号段的⻓度',
`version` int(20) NOT NULL COMMENT '版本号',
`biz_type` int(20) NOT NULL COMMENT '业务类型',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

current_max_id 和 step 都是用来获取批量 ID,获取的批量 ID 为:current_max_id -- current_max_id + step

version 解决并发问题(乐观锁)

2、插入一行数据

sql 复制代码
INSERT INTO `sequence_id_generator` (`id`, `current_max_id`, `step`, `version`,
`biz_type`)
VALUES
 (1, 0, 100, 0, 101);

3、通过 select 获取指定业务下的批量唯一 ID

sql 复制代码
SELECT `current_max_id`, `step`,`version` FROM `sequence_id_generator` where
`biz_type` = 101

4、不够用更新后重新 select

sql 复制代码
UPDATE sequence_id_generator SET current_max_id = 0+100, version=version+1 WHERE
version = 0 AND `biz_type` = 101
SELECT `current_max_id`, `step`,`version` FROM `sequence_id_generator` where
`biz_type` = 101

优缺点 ------

优点:ID 有序递增

缺点:数据库单点问题、ID 没有业务含义、安全问题

NoSQL

一般情况下,NoSQL 方案使用 Redis 多一些。我们通过 Redis 的 incr 命令即可实现对 ID 原子顺序递增。

sql 复制代码
127.0.0.1:6379> set sequence_id_biz_type 1
OK
127.0.0.1:6379> incr sequence_id_biz_type
(integer) 2
127.0.0.1:6379> get sequence_id_biz_type
"2"

为了提高可用性和并发,我们可以使用 Redis 集群。(Redis Cluster)

优缺点 ------

优点:性能不错且 ID 有序递增

缺点:和数据库主键自增方案缺点类似

算法

UUID

UUID,Universal Unique Identifier(通用唯一标识符)。UUID 包含 32 个 16 进制数字。(8-4-4-4-12)

JDK 提供了现成的生成 UUID 的方法

java 复制代码
UUID.randomUUID();

UUID 可以保证唯一性,因为其生成规则包括 MAC 地址、时间戳、名字空间、随机或伪随机数、时序等元素,UUID 不会重复。但虽然 UUID 可以做到全局唯一性,但是我们很少使用它。

UUID 作为 MySQL 主键的时候非常不合适:

1、主键要尽量越短越好

2、UUID 无序,InnoDB 引擎中,数据库主键的无序性会严重影响性能(B+ 树)

UUID 优缺点 ------

优点:生成速度快、简单易用

缺点:空间消耗大、不安全(MAC 地址泄露)、无序、没有具体业务含义、需要解决重复 ID 问题(机器时间不对的情况下,可能生成重复 ID)

Snowflake(雪花算法)

基于 Snowflake 算法的开源实现,比如美团的 Leaf.

优缺点 ------

优点:生成速度快,ID 有序自增、比较灵活(根据业务加入业务ID)

缺点:需要解决重复 ID 问题(机器时间不对导致重复 ID)

开源框架

UidGenerator
Leaf(团子)
Tinyid(滴滴)
相关推荐
无名客07 分钟前
redis分布式锁为什么采用Lua脚本实现。而不是事务
redis·分布式·lua·事务
落日沉溺于海22 分钟前
React From表单使用Formik和yup进行校验
开发语言·前端·javascript
快乐肚皮35 分钟前
fencing token机制
java·fencing token
叶落阁主44 分钟前
Neovim 插件 i18n.nvim 介绍
java·vue.js·vim
渣哥1 小时前
让集合线程安全的几种靠谱方法
java
dylan_QAQ1 小时前
Java转Go全过程06-工程管理
java·后端·go
在未来等你1 小时前
Elasticsearch面试精讲 Day 12:数据建模与字段类型选择
大数据·分布式·elasticsearch·搜索引擎·面试
鲸屿1951 小时前
python之socket网络编程
开发语言·网络·python
没有梦想的咸鱼185-1037-16631 小时前
基于R语言机器学习方法在生态经济学领域中的实践技术应用
开发语言·机器学习·数据分析·r语言
a587691 小时前
消息队列(MQ)初级入门:详解RabbitMQ与Kafka
java·分布式·microsoft·面试·kafka·rabbitmq