Redis实战9-全局唯一ID
发布优惠券的时候,每个店铺都可以发布优惠券,当用户抢购的时候,优惠券表中的id如果使用数据库的自增长ID会存在以下问题:
1:id的规律太明显,容易被刷
2:当数据量很大的时候,会受到单表数据的限制
缺点场景分析:
id规律场景:如果我们的id具有太明显的规则,用户或者说商业对手很容易猜测出来我们的一些敏感信息,比如商城在一天时间内,卖出了多少单,这明显不合适。
单表限制:随着我们商城规模越来越大,mysql的单表的容量不宜超过500W,数据量过大之后,我们要进行拆库拆表,但拆分表了之后,他们从逻辑上讲他们是同一张表,所以他们的id是不能一样的, 于是乎我们需要保证id的唯一性。
全局ID生成器
全局ID生成器,是一种在分布式系统下用来生成全局唯一的ID工具,一般需要瞒住下列特性:
唯一性、高可用、递增性、安全性、高性能

全局唯一ID生成策略:
UUID、Redis自增长、雪花算法、数据库自增
Redis自增ID策略:
1:每天一个key,方便统计订单量;
2:ID都在是时间戳+计数器
实战:基于Redis拼接其他信息来实现全局唯一ID

全局唯一ID使用long类型的,其中时间戳是基于某一个时间点开始的。比如我们从2022.11.26 23:00:00开始,可以使用69年。
思考1:获取当前时间的秒:

思考2:时间戳,怎么计算的?
使用当前时间的秒-初始时间的秒
思考3:序列号怎么设置?
使用Redis的setnx命令,最好加上当前年月日

思考4:怎么拼接?
因为我们需要返回的是long类型的,如果使用string拼接后,还要转换。还要知道,使用string拼接,当并发量很大的时候,也会有性能问题。那么我们应该怎么处理的?
注意:我们再来看看全局唯一ID的格式。如上图,我们可以看出,共64位,其中符号位是1个,时间戳是31位。序列号是32位,发现什么了吗?如果我们把时间戳向左移动32位(因为序列号是32位。向左移动位置,空出给序列号使用),是不是就是符号位+时间戳的了?
1:我们也知道计算机中左移动最快是x<<位数。
2:我们还需要知道,在计算机中 | 或计算:按位或运算“|”

根据上面,我们可以知道位运算序号后,就是序列号的值。序列号是多少,就是多少。
所以,我们可以知道拼接代码如下:timeStamp << 32 |no;
本文由凯哥Java(公众号:kaigejava),个人博客:www.kaigejava.com 发布于博客园。
小福利
凯哥自己开发的,领取外卖、打车、咖啡、买菜、各大电商的优惠券的公¥众¥号。如下图:

最终代码:
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
/**
* @author 凯哥Java
* @description 基于Redis实现62位的全局唯一ID
* @company
* */
@Component
public class RedisIdWorker {
private static final long BEGIN_TIMESTAMP = 1669503600L;
private final StringRedisTemplate stringRedisTemplate;
/**
* 序列号的位数
*/
private static final int COUNT_BITS = 32;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 获取id
* @param kyePrefix
* @return
*/
public long nextId(String kyePrefix){
//1:生成时间戳 = 当前时间戳-开始时间戳
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
long timeStamp = nowSecond - BEGIN_TIMESTAMP;
//2:生成序列号.使用sexNx.其中加上当前年月日
//获取当前时间的你那月日
String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
//开始32位序列号
long no = stringRedisTemplate.opsForValue().increment("icr:"+kyePrefix+":"+date);
//3:拼接返回
return timeStamp << COUNT_BITS |no;
}
/**
* 获取到指定时间的毫秒
* @param args
*/
public static void main(String[] args) {
LocalDateTime time = LocalDateTime.of(2022,11,26,23,00,00);
long second = time.toEpochSecond(ZoneOffset.UTC);
System.out.println(second);
}
}
测试:使用多线程及countdownlatch
private ExecutorService executorService = Executors.newFixedThreadPool(500);
@Resource
private RedisIdWorker redisIdWorker;
@Test
public void RedisIdWorkerTest() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(300);
Runnable task = ()->{
for(int i = 0;i<100;i++){
long id = redisIdWorker.nextId("myorder");
System.out.println(id);
}
latch.countDown();
};
long begin = System.currentTimeMillis();
for(int i = 0;i< 300;i++){
executorService.submit(task);
}
latch.await();
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-begin));
}
Redis实战9-全局唯一ID的更多相关文章
- redis 学习笔记3(哨兵模式下分布式锁的实现以及全局唯一id的生成)
redis实现分布式锁和全局唯一id应该是较为常见的应用. 实现基于redis的setNX,以及incr命令.还是比较简单的! 搭建环境以及配置好sping整合,做了下测试,有兴趣的载下来看看,自己做 ...
- 【Redis场景拓展】秒杀问题-全局唯一ID生成策略
全局唯一ID 为什么要使用全局唯一ID: 当用户抢购时,就会生成订单并保存到订单表中,而订单表如果使用数据库自增ID就存在一些问题: 受单表数据量的限制 id的规律性太明显 场景分析一:如果我们的id ...
- 如何在高并发分布式系统中生成全局唯一Id
月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也讨论了这个主题,我受益匪浅啊…… 博文示例: 1. ...
- 高并发分布式系统中生成全局唯一Id汇总
数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障. 2 以时间为序,或者ID里包含时间 ...
- 如何在高并发分布式系统中生成全局唯一Id(转)
http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文, ...
- 游戏服务器生成全局唯一ID的几种方法
在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ...
- (转)如何在高并发分布式系统中生成全局唯一Id
又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了 ...
- 高并发分布式环境中获取全局唯一ID[分布式数据库全局唯一主键生成]
需求说明 在过去单机系统中,生成唯一ID比较简单,可以使用MySQL的自增主键或者Oracle中的sequence, 在现在的大型高并发分布式系统中,以上策略就会有问题了,因为不同的数据库会部署到不同 ...
- 常见的生成全局唯一id有哪些?他们各有什么优缺点?
分布式系统中全局唯一id是我们经常用到的,生成全局id方法由很多,我们选择的时候也比较纠结.每种方式都有各自的使用场景,如果我们熟悉各种方式及优缺点,使用的时候才会更方便.下面我们就一起来看一下常见的 ...
- 分布式全局唯一ID的实现
分布式全局唯一ID的实现 前言 上周末考完试,这周正好把工作整理整理,然后也把之前的一些素材,整理一番,也当自己再学习一番. 一方面正好最近看到几篇这方面的文章,另一方面也是正好工作上有所涉及,所以决 ...
随机推荐
- 一次Java服务内存过高的分析过程
现象 年前,收到了短信报警,显示A服务的某台机器内存过高,超过80% 如上图所示,内存会阶段性增加.奇怪的是,十多台机器中只有这一台有这个问题 堆内内存分析 最先怀疑是内存泄漏的问题,所以首先使用jm ...
- hive、hbase、clickhouse
hive相当于贝利,是计算处理数据的鼻祖,hbase相当于梅西,继承了hive(贝利)的意志,但是因为现代足球的发展,梅西整体水平要强于贝利的远古踢法(mapreduce),然后clickhouse相 ...
- Ubuntu下的LabVIEW开发
1 虚拟机的安装 我用的是Virtua Box 的虚拟机,当然也有其他的类似软件:下载虚拟机的网址: https://www.virtualbox.org/wiki/Downloads 自行去下载合适 ...
- 将传统应用带入浏览器的开源先锋「GitHub 热点速览」
现代浏览器已经不再是简单的浏览网页的工具,其潜能正在通过技术不断地被挖掘和扩展.得益于 WebAssembly 等技术的出现,让浏览器能够以接近原生的速度执行非 JavaScript 语言编写的程序, ...
- 2024-07-17:用go语言,给定一个整数数组nums, 我们可以重复执行以下操作: 选择数组中的前两个元素并删除它们, 每次操作得到的分数是被删除元素的和。 在保持所有操作的分数相同的前提下,
2024-07-17:用go语言,给定一个整数数组nums, 我们可以重复执行以下操作: 选择数组中的前两个元素并删除它们, 每次操作得到的分数是被删除元素的和. 在保持所有操作的分数相同的前提下, ...
- 基础-数组_C语言
C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合.数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量. 数组的声明并不是声明一个个单独的变量,比如 runoob0. ...
- git操作之一:git add/commit/init
在日常的开发中,适用版本控制系统来进行协同开发已经是工作中的常态,使用比较多的要数git这个工具,今天就来说下git的日常用法以及在开发中的一些疑惑. 一.概述 git在日常开发中广泛应用,其概念可以 ...
- scratch源码下载 | 蜘蛛传说
程序说明: <蜘蛛传说>是一个通过Scratch平台制作的互动游戏项目.在这个故事中,玩家将扮演一只蜘蛛,其原本和平的生活被一只入侵的壁虎所打破.为了保卫自己的家园,蜘蛛必须运用智慧和勇气 ...
- 14、Spring之基于注解的声明式事务
14.1.概述 14.1.1.编程式事务 事务功能的相关操作全部通过自己编写代码来实现: Connection conn = ...; try { // 开启事务:关闭事务的自动提交 conn.set ...
- 【MySQL】主从配置
一主一从(或二从)部署: 参考博客: https://www.cnblogs.com/lelehellow/p/9633315.html 正好借着3台机器,可以做一个 1主 2从的分配 192.168 ...