1,单数据库自增ID

第一种方案仍然还是基于数据库的自增ID,需要单独使用一个数据库实例,在这个实例中新建一个单独
的表:

CREATE DATABASE `SEQID`;
CREATE TABLE SEQID.SEQUENCE_ID (
id bigint(20) unsigned NOT NULL auto_increment,
stub char(10) NOT NULL default '',
PRIMARY KEY (id),
UNIQUE KEY stub (stub)
) ENGINE=MyISAM;

这种生成分布式ID的机制,需要一个单独的Mysql实例,虽然可行,但是基于性能与可靠性来考虑的话
都不够,业务系统每次需要一个ID时,都需要请求数据库获取,性能低,并且如果此数据库实例下线
了,那么将影响所有的业务系统。

2,数据库多主模式

需要单独给每个Mysql实例配置不同的起始值和自增步长。

第一台Mysql实例配置:

set @@auto_increment_offset = 1; -- 起始值
set @@auto_increment_increment = 2; -- 步长

第二台Mysql实例配置:

set @@auto_increment_offset = 2; -- 起始值
set @@auto_increment_increment = 2; -- 步长

经过上面的配置后,这两个Mysql实例生成的id序列如下: mysql1,起始值为1,步长为2,ID生成的序列
为:1,3,5,7,9,... mysql2,起始值为2,步长为2,ID生成的序列为:2,4,6,8,10,...

但是这种方案的扩展性不太好,如果两台Mysql实例不够用,需要新增Mysql实例来提高性能时,这时
就会比较麻烦。

3,号段模式

CREATE TABLE id_generator (
id int(10) NOT NULL,
current_max_id bigint(20) NOT NULL COMMENT '当前最大id',
increment_step int(10) NOT NULL COMMENT '号段的长度',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

这种方案不再强依赖数据库,就算数据库不可用,那么DistributIdService也能继续支撑一段时间。但是
如果DistributIdService重启,会丢失一段ID,导致ID空洞。
为了提高DistributIdService的高可用,需要做一个集群,业务在请求DistributIdService集群获取ID
时,会随机的选择某一个DistributIdService节点进行获取,对每一个DistributIdService节点来说,数
据库连接的是同一个数据库,那么可能会产生多个DistributIdService节点同时请求数据库获取号段,那
么这个时候需要利用乐观锁来进行控制,比如在数据库表中增加一个version字段,在获取号段时使用

4,中非数据库方式:

雪花算法

核心思想是:分布式ID固定是一个long型的数字,一个long型占8个字节,也就是64个bit,原始
snowflake算法中对于bit的分配如下图:

 

 

时间戳部分占41bit,这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差
值(当前时间-固定的开始时间),这样可以使产生的ID从更小值开始;41位的时间戳可以使用69
年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年

根据这个算法的逻辑,只需要将这个算法用Java语言实现出来,封装为一个工具方法,那么各个业务应
用可以直接使用该工具方法来获取分布式ID,只需保证每个业务应用有自己的工作机器id即可,而不需
要单独去搭建一个获取分布式ID的应用。

在大厂里,其实并没有直接使用snowflake,而是进行了改造,因为snowflake算法中最难实践的就是
工作机器id,原始的snowflake算法需要人工去为每台机器去指定一个机器id,并配置在某个地方从而
让snowflake从此处获取机器id。
但是在大厂里,机器是很多的,人力成本太大且容易出错,所以大厂对snowflake进行了改造。

百度(uid-generator)

uid-generator使用的就是snowflake,只是在生产机器id,也叫做workId时有所不同。  

对于uid-generator中的workId,占用了22个bit位,时间占用了28个bit位,序列化占用了13个bit位,
需要注意的是,和原始的snowflake不太一样,时间的单位是秒,而不是毫秒,workId也不一样,同一
个应用每重启一次就会消费一个workId。

美团(Leaf)

美团的Leaf也是一个分布式ID生成框架。它非常全面,即支持号段模式,也支持snowflake模式。号段
模式这里就不介绍了,和上面的分析类似。
Leaf中的snowflake模式和原始snowflake算法的不同点,也主要在workId的生成,Leaf中workId是基
于ZooKeeper的顺序Id来生成的,每个应用在使用Leaf-snowflake时,在启动时都会都在Zookeeper中
生成一个顺序Id,相当于一台机器对应一个顺序节点,也就是一个workId。

 

Redis来生成分布式ID

可以利用Redis中的
incr命令来实现原子性的自增与返回,比如:

127.0.0.1:6379> set seq_id 1 // 初始化自增ID为1
OK
127.0.0.1:6379> incr seq_id // 增加1,并返回
(integer) 2
127.0.0.1:6379> incr seq_id // 增加1,并返回
(integer) 3

使用redis的效率是非常高的,但是要考虑持久化的问题。Redis支持RDB和AOF两种持久化的方式。
RDB持久化相当于定时打一个快照进行持久化,如果打完快照后,连续自增了几次,还没来得及做下一
次快照持久化,这个时候Redis挂掉了,重启Redis后会出现ID重复。

AOF持久化相当于对每条写命令进行持久化,如果Redis挂掉了,不会出现ID重复的现象,但是会由于
incr命令过得,导致重启恢复数据时间过长。

  

  

  

posted on 2023-02-23 20:21  EZgod  阅读(26)  评论(0编辑  收藏  举报