分布式锁
Redis
最简单的我们使用redis.setNX(key, value, timeout)
,但是存在超时没有执行完的问题,然后线程2加锁,最后在finally中线程1删除key,之后就该锁就消失了,线程3此时又能成功加锁。
弥补措施之一:value设置当前线程id或uuid,在删除前判断是否是自己的设置的值,如果不是不许删除key。此操作是非原子,需要用lua脚本操作。
但无法解决超时问题,不设置超时有存在线程崩溃无法释放锁的问题。
Redission
使用redission即可解决,可以自动续期,使用如下:
RLock lock = redisson.getLock("className");
lock.lock();
try {
// do sth.
} finally {
lock.unlock();
}
分布式id【1】
UUID
数据库自增id
优点:实现简单,ID单调自增,数值类型查询速度快
缺点:DB单点存在宕机风险,无法扛住高并发场景
数据库双主模式集群模式
可以分段自增,或者2台机器,一台自增奇数一台偶数
MySQL_1 配置:
set @@auto_increment_offset = 1; -- 起始值
set @@auto_increment_increment = 2; -- 步长
MySQL_2 配置:
set @@auto_increment_offset = 2; -- 起始值
set @@auto_increment_increment = 2; -- 步长
优点:解决DB单点问题
缺点:不利于后续扩容,而且实际上单个数据库自身压力还是大,依旧无法满足高并发场景。
数据库的号段模式
从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存。
CREATE TABLE id_generator (
`id` int(10) NOT NULL,
`max_id` bigint(20) NOT NULL COMMENT '当前最大id',
`step` int(20) NOT NULL COMMENT '号段的步长',
`biz_type` int(20) NOT NULL COMMENT '业务类型',
`version` int(20) NOT NULL COMMENT '版本号',
PRIMARY KEY (`id`)
)
等这批号段ID用完,再次向数据库申请新号段,对max_id字段做一次update操作(采用版本号 version乐观锁方式更新),update max_id= max_id + step,update成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]。
基于Redis模式
利用redis的incr命令实现ID的原子性自增
127.0.0.1:6379> set seq_id 1 // 初始化自增ID为1
OK
127.0.0.1:6379> incr seq_id // 增加1,并返回递增后的数值
(integer) 2
基于雪花算法(Snowflake)模式
使用一个 64 bit 的 long 型的数字作为全局唯一 id
第一个部分是 1 个 bit:0,这个是无意义的。因为二进制里第一个 bit 位如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。
第二个部分是 41 个 bit:表示的是时间戳。单位是毫秒。41 bit 可以表示的数字多达 2^41 - 1,也就是可以标识 2 ^ 41 - 1 个毫秒值,换算成年就是表示69年的时间。
第三个部分是 5 个 bit:表示的是机房 id
第四个部分是 5 个 bit:表示的是机器 id。每个机房里可以代表 2 ^ 5 个机器(32 台机器),也可以根据自己公司的实际情况确定。
第五个部分是 12 个 bit:表示的序号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号。12 bit 可以代表的最大正整数是 2 ^ 12 - 1 = 4096,也就是说可以用这个 12 bit 代表的数字来区分同一个毫秒内的 4096 个不同的 id。
总结:就是用一个 64 bit 的数字中各个 bit 位来设置不同的标志位,区分每一个 id。
参考
【1】9 种分布式 ID 生成方式,总有一款适合你!:https://mp.weixin.qq.com/s?src=11×tamp=1634202299&ver=3373&signature=pTpl5NJUVpXIcBAkv*VZPwqk5m8OCyDMRDiSwVFQZDfe0mKROl-Azmx86TfRoXEQpWGmHV-x9HwhS3PXCEDXk-i7qpEmiaX7UqSHLav6CKsIE4noAuwTosjLogCp14hd&new=1