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
begin;
replace into SEQUENCE_ID (stub) VALUES (‘anyword’);
select last_insert_id();
commit;
stub字段在这里并没有什么特殊的意义,只是为了方便的去插入数据,只有能插入数据才能产生自增id。而对于插入我们用的是replace,replace会先看是否存在stub指定值一样的数据,如果存在则先delete再insert,如果不存在则直接insert。
这种生成分布式ID的机制,需要一个单独的Mysql实例,虽然可行,但是基于性能与可靠性来考虑的话都不够,业务系统每次需要一个ID时,都需要请求数据库获取,性能低,并且如果此数据库实例下线了,那么将影响所有的业务系统。
为了解决数据库可靠性问题,我们可以使用第二种分布式ID生成方案。
二、数据库多主模式
如果我们两个数据库组成一个主从模式集群,正常情况下可以解决数据库可靠性问题,但是如果主库挂掉后,数据没有及时同步到从库,这个时候会出现ID重复的现象。我们可以使用双主模式集群,也就是两个Mysql实例都能单独的生产自增ID,这样能够提高效率,但是如果不经过其他改造的话,这两个Mysql实例很可能会生成同样的ID。需要单独给每个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,…
对于这种生成分布式ID的方案,需要单独新增一个生成分布式ID应用,比如DistributIdService
,该应用提供一个接口供业务应用获取ID,业务应用需要一个ID时,通过rpc的方式请求DistributIdService
,DistributIdService
随机去上面的两个Mysql实例中去获取ID。
实行这种方案后,就算其中某一台Mysql实例下线了,也不会影响DistributIdService
,DistributIdService
仍然可以利用另外一台Mysql来生成ID。
但是这种方案的扩展性不太好,如果两台Mysql实例不够用,需要新增Mysql实例来提高性能时,这时就会比较麻烦。
现在如果要新增一个实例mysql3,要怎么操作呢? 第一,mysql1、mysql2的步长肯定都要修改为3,而且只能是人工去修改,这是需要时间的。 第二,因为mysql1和mysql2是不停在自增的,对于mysql3的起始值我们可能要定得大一点,以给充分的时间去修改mysql1,mysql2的步长。 第三,在修改步长的时候很可能会出现重复ID,要解决这个问题,可能需要停机才行。
为了解决上面的问题,以及能够进一步提高DistributIdService
的性能,如果使用第三种生成分布式ID机制。
三、号段模式
我们可以使用号段的方式来获取自增ID,号段可以理解成批量获取,比如DistributIdService
从数据库获取ID时,如果能批量获取多个ID并缓存在本地的话,那样将大大提供业务应用获取ID的效率。
比如DistributIdService
每次从数据库获取ID时,就获取一个号段,比如(1,1000],这个范围表示了1000个ID,业务应用在请求DistributIdService提供ID时,DistributIdService
只需要在本地从1开始自增并返回即可,而不需要每次都请求数据库,一直到本地自增到1000时,也就是当前号段已经被用完时,才去数据库重新获取下一号段。
所以,我们需要对数据库表进行改动,如下:
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;
这个数据库表用来记录自增步长以及当前自增ID的最大值(也就是当前已经被申请的号段的最后一个值),因为自增逻辑被移到DistributIdService
中去了,所以数据库不需要这部分逻辑了。
这种方案不再强依赖数据库,就算数据库不可用,那么DistributIdService
也能继续支撑一段时间。但是如果DistributIdService
重启,会丢失一段ID,导致ID空洞。
为了提高DistributIdService
的高可用,需要做一个集群,业务在请求DistributIdService
集群获取ID时,会随机的选择某一个DistributIdService
节点进行获取,对每一个DistributIdService
节点来说,数据库连接的是同一个数据库,那么可能会产生多个DistributIdService
节点同时请求数据库获取号段,那么这个时候需要利用乐观锁来进行控制,比如在数据库表中增加一个version字段,在获取号段时使用如下SQL:
update id_generator set current_max_id=#{newMaxId}, version=version+1 where version = #{version}
因为newMaxId是DistributIdService中根据oldMaxId+步长算出来的,只要上面的update更新成功了就表示号段获取成功了。
为了提供数据库层的高可用,需要对数据库使用多主模式进行部署,对于每个数据库来说要保证生成的号段不重复,这就需要利用最开始的思路,再在刚刚的数据库表中增加起始值和步长,比如如果现在是两台Mysql,那么 mysql1将生成号段(1,1001],自增的时候序列为1,3,4,5,7… mysql1将生成号段(2,1002],自增的时候序列为2,4,6,8,10…
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
言尽于此,完结
无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。
- 第一,设计模式能让专业人之间交流方便,如下:
程序员A:这里我用了XXX设计模式
程序员B:那我大致了解你程序的设计思路了
- 第二,易维护
项目经理:今天客户有这样一个需求…
程序员:明白了,这里我使用了XXX设计模式,所以改起来很快
- 第三,设计模式是编程经验的总结
程序员A:B,你怎么想到要这样去构建你的代码
程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题
- 第四,学习设计模式并不是必须的
程序员A:B,你这段代码使用的是XXX设计模式对吗?
程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的
从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
搜集费时费力,能看到此处的都是真爱!
MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!
[外链图片转存中…(img-5YdNorbr-1711822676671)]
搜集费时费力,能看到此处的都是真爱!