干货技术实战:聊一聊分布式系统全局唯一ID的几种实现方式
Posted 程序员实战基地
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了干货技术实战:聊一聊分布式系统全局唯一ID的几种实现方式相关的知识,希望对你有一定的参考价值。
▲点击查看大图(若图片不清晰可点击文章底部阅读原文)
简介:这一点相信写过代码的小伙伴都晓得,主要利用主键ID的auo_increment特性,每进来一条数据时数据库自动为其生成当前最大的ID并作为该条记录的主键;
简介:DB只存储当前最大的ID值,每次需要ID时,则按照顺序批量生成N个有序的ID列表,并将最大的ID值 + N。
简介:比如按照规则:yyyyMMdd+ N位随机数 或者 yyyyMMddHHmmss + N位随机数。
简介:JUC包下经典的原子操作类,可以基于它生成自增、有序且全局唯一的编号
简介:熟悉这个命令的应该都知道它是啥意思,不知道的 自己打开redis-cli执行下该命令就可以了!
简介:这个大家可能有点陌生,其实就是利用ZooKeeper底层树形节点ZNode(类似于Windows的文件目录数)的有序性,循环不断生成其对应的版本号或者节点本身的数据
CREATE TABLE `qr_code` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '编码',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
private QrCodeMapper qrCodeMapper;
//随机的后6位商品编号,毫秒级上限为999999,应该是满足的 (只要间隔一定的频率重新发布/重//启应用时,则当前计数器将重置为 100000)
private static AtomicLong atomicLong=new AtomicLong(100000);
"generate/code/v2") (
public BaseResponse generateCodeV2(){
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
//方式一:原子操作数(单体应用系统架构)
qrCodeMapper.insertSelective(new QrCode(generateCodeInV1()));
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
//全局唯一编码 - 正常情况 - 单体应用系统架构下可用 “原子操作数” 控制并发(加上本身就有//计数功能)
private String generateCodeInV1(){
SimpleDateFormat format=new SimpleDateFormat("yyyyMMddHHmmss");
return format.format(new Date()) + atomicLong.getAndIncrement();
}
SELECT
`code`,
COUNT(id) AS total
FROM
qr_code
GROUP BY
`code`
HAVING total > 1
0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
private QrCodeMapper qrCodeMapper;
//随机的后6位商品编号,毫秒级上限为999999,应该是满足的 (只要间隔一定的频率重新发布/重启应用时,则当前计数器将重置为 100000)
private static AtomicLong atomicLong=new AtomicLong(100000);
"generate/code/v2") (
public BaseResponse generateCodeV2(){
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
//方式二:雪花算法(单体/分布式)
qrCodeMapper.insertSelective(new QrCode(generateCodeInV2()));
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
private static final Snowflake snowflake=new Snowflake(5,5);
//全局唯一编码 - 雪花算法
private String generateCodeInV2(){
//为了凑够20位
return snowflake.nextIdStr()+RandomStringUtils.randomNumeric(1);
}
我们仍然假设 N=6,即可以将其初始值设定为99999,然后通过INCRBY命令对应的操作不断进行 +1 操作;此种方式主要是利用Redis的命令具有原子操作的特性(单线程,但支持并发),因此在分布式高并发的场景下这一方式是顶得住的;
private RedisTemplate redisTemplate;
private static final String RedisKeyCode="sb:technology:code:v1";
private static final Long LimitMaxCode=1000L;
private static final Long InitKeyCode=99L;
//每次项目重启都可以将其重置为初始值
public void init(){
redisTemplate.delete(RedisKeyCode);
redisTemplate.opsForValue().set(RedisKeyCode,InitKeyCode);
}
//全局唯一编码 - Redis:要使用 Redis的 INCRBY命令,需要设置缓存中key的序列化机制为://StringRedisSerializer;
//不然会出现:ERR value is not an integer or out of range
private String generateCodeInV3(){
SimpleDateFormat format=new SimpleDateFormat("yyyyMMddHHmmss");
Long currCode=redisTemplate.opsForValue().increment(RedisKeyCode,1L);
//编码上限/阈值检测机制
if (Objects.equals(LimitMaxCode,currCode)){
redisTemplate.opsForValue().set(RedisKeyCode,InitKeyCode);
currCode=redisTemplate.opsForValue().increment(RedisKeyCode,1L);
}
return format.format(new Date()) + currCode;
}
//zookeeper生成全局唯一标志符的方式
private static final String ID_NODE = "/QRCodeV2";
//zk客户端实例
private CuratorFramework client;
//全局唯一编码 - zookeeper
private String generateCodeInV4() throws Exception{
if (null == client.checkExists().forPath(ID_NODE)) {
//PERSISTENT(0, false, false) 持久型节点; PERSISTENT_SEQUENTIAL(2, false, true) 持久顺序型节点;
//EPHEMERAL(1, true, false) 临时型节点;EPHEMERAL_SEQUENTIAL(3, true, true) 临时顺序型节点;
client.create().withMode(CreateMode.PERSISTENT).forPath(ID_NODE, new byte[0]);
}
//根据节点的版本号-从0开始递增的,因此位数也是不断在变化的(只要path不变)
Stat stat = client.setData().forPath(ID_NODE,new byte[0]);
SimpleDateFormat format=new SimpleDateFormat("yyyyMMddHHmmss");
return format.format(new Date()) + stat.getVersion();
}
public class ZooKeeperConfig {
private static final String ZK_ADDRESS = "127.0.0.1:2181";
public CuratorFramework curatorFramework(){
//如果获取链接失败,则重试3次,每次间隔2s
RetryPolicy policy=new RetryNTimes(3,2000);
//获取链接到zk服务的客户端实例
CuratorFramework curatorFramework= CuratorFrameworkFactory.builder()
.connectString(ZK_ADDRESS).sessionTimeoutMs(5000).connectionTimeoutMs(10000)
.retryPolicy(policy).build();
//启动客户端
curatorFramework.start();
return curatorFramework;
}
}
↑↑↑长按识别关注
下面是Debug最近写的一些干货,每篇都很用心,欢迎各位小伙伴阅读/点赞/分享:
1.
2.
3.
4.
5.
程序员实战基地
以上是关于干货技术实战:聊一聊分布式系统全局唯一ID的几种实现方式的主要内容,如果未能解决你的问题,请参考以下文章