草庐IT

简单实用!利用Redis轻松实现高并发全局ID生成器

程序员拾山 2023-03-28 原文

Redis作为高性能的KV数据库,并且操作还是原子性的,所以用来做支持高并发的发号器十分合适。

本文给大家介绍3种常见的全局ID生成方式。

1、全局递增ID

目标:一直递增的全局ID。

/**
* 一直递增的全局id
*
* @param redisTemplate redis客户端对象
* @param busId 业务id,可以按需配置
* @param step 步长,即每次递增的间隔
*/
public static String getNo(RedisTemplate<String, Object> redisTemplate, String busId, int step) {
//保存redis中的key,注意不要重复
String redisKey = "uniqueNo_";
//利用increment即redis原生incrBy命令的原子性特性生成递增的序列号
Long increment = redisTemplate.opsForValue().increment(redisKey, step);
if (increment == null) {
throw new RuntimeException("redis命令执行失败");
}
//业务id+递增id,如果需要纯数字,去掉业务id即可
return busId + increment;
}

2、以天为分割的全局ID

目标:生成格式为 yyyyMMdd + 递增序列号的全局ID。

/**
* 以天为间隔的递增序列号
* @param redisTemplate redis客户端对象
* @param busId 业务id,可以按需配置
* @param step 步长,即每次递增的间隔
*/
public static String getNo(RedisTemplate<String, Object> redisTemplate, String busId, int step) {
//当天日期,比如20221226
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
//保存redis中的key,注意不要重复
String redisKey = "uniqueNo_" + date;
//利用increment即redis原生incrBy命令的原子性特性生成递增的序列号
Long increment = redisTemplate.opsForValue().increment(redisKey, step);
if (increment == null) {
throw new RuntimeException("redis命令执行失败");
}
if (step == increment.intValue()) {
//首次执行时,给redisKey设置ttl,第二天这个key就可以被redis自动删除
redisTemplate.expire(redisKey, 25, TimeUnit.HOURS);
}
//组合 20221226 + 业务id + 0001(可以根据需要自由调整序列号的长度)
return date + busId + String.format("%04d", increment);
}

3、批量获取ID

有时我们需要批量的获取递增ID,比如给一批订单号设置ID。

/**
* 批量获取id
*
* @param redisTemplate redis客户端对象
* @param busId 业务id,可以按需配置
* @param size 获取的id个数,与步长类似
*/
public static List<String> getNoByGroup(RedisTemplate<String, Object> redisTemplate, String busId, int size) {
//保存redis中的key,注意不要重复
String redisKey = "uniqueNo_group";
//设置步长为size,相当于一次性申请size个id
Long increment = redisTemplate.opsForValue().increment(redisKey, size);
if (increment == null) {
throw new RuntimeException("redis命令执行失败");
}
long begin = increment - Long.parseLong(size + "");
List<String> rs = new ArrayList<>();
for (long i = begin + 1; i <= increment; i++) {
rs.add(busId + i);
}
return rs;
}

总结

无论我们需要什么格式的ID,其实只要我们把握住其中的核心:incrBy命令,根据其原子性的特性,就可以生成我们需要的全局ID。

但是需要注意的是,虽然incrBy命令是原子性的,但是通过组合键进行组合时,其实是破坏了这种原子性。如果有特殊的ID格式要求,务必要进行充分的测试。

有关简单实用!利用Redis轻松实现高并发全局ID生成器的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  3. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  4. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  6. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  7. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  8. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  9. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  10. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

随机推荐