概述

设计目标:每秒最大生成10万个ID,ID单调递增且唯一。Reidis可以不需要持久化ID。

要求:集群时钟不能倒退。

总体思路:集群中每个节点预生成生成ID;然后与redis的已经存在的ID做比较。如果大于,则取节点生成的ID;小于的话,取Redis中最大ID自增。

Java代码

import org.apache.commons.lang3.RandomStringUtils;

import org.apache.commons.lang3.StringUtils;

import org.apache.commons.lang3.time.FastDateFormat;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import java.io.IOException;

import java.nio.file.Files;

import java.nio.file.Path;

import java.nio.file.Paths;

import static com.google.common.base.Preconditions.checkArgument;

/**

* 生成递增的唯一序列号, 可以用来生成订单号,例如216081817202494579

*

* 生成规则:

* 业务类型 + redis中最大的序列号

*

* 约定:

* redis中最大的序列号长度为17,包括{6位日期 + 6位时间 + 3位毫秒数 + 2位随机}

*

* 建议:

* 为了容错和服务降级, SeqGenerator生成失败时最好采用UUID替换

*

* Created by juemingzi on 16/8/19.

*/

public class SeqGenerator {

private static final Logger logger = LoggerFactory.getLogger(SeqGenerator.class);

private static final Path filePath = Paths.get(Thread.currentThread().getContextClassLoader().getResource("lua/get_next_seq.lua").getPath());

//线程安全

private static final FastDateFormat seqDateFormat = FastDateFormat.getInstance("yyMMddHHmmssSSS");

private static final RedisExtraService redisExtraService = SpringContext.getBean(RedisExtraService.class);

private final byte[] keyName;

private final byte[] incrby;

private byte[] sha1;

public SeqGenerator(String keyName) throws IOException {

this(keyName, 1);

}

/**

* @param keyName

* @param incrby

*/

public SeqGenerator(String keyName, int incrby) throws IOException {

checkArgument(keyName != null && incrby > 0);

this.keyName = keyName.getBytes();

this.incrby = Integer.toString(incrby).getBytes();

init();

}

private void init() throws IOException {

byte[] script;

try {

script = Files.readAllBytes(filePath);

} catch (IOException e) {

logger.error("读取文件出错, path: {}", filePath);

throw e;

}

sha1 = redisExtraService.scriptLoad(script);

}

public String getNextSeq(String bizType) {

checkArgument(StringUtils.isNotBlank(bizType));

return bizType + getMaxSeq();

}

private String generateSeq() {

String seqDate = seqDateFormat.format(System.currentTimeMillis());

String candidateSeq = new StringBuilder(17).append(seqDate).append(RandomStringUtils.randomNumeric(2)).toString();

return candidateSeq;

}

/**

* 通过redis生成17位的序列号,lua脚本保证序列号的唯一性

*

* @return

*/

public String getMaxSeq() {

String maxSeq = new String((byte[]) redisExtraService.evalsha(sha1, 3, keyName, incrby, generateSeq().getBytes()));

return maxSeq;

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

lua脚本

--

-- 获取最大的序列号,样例为16081817202494579

--

-- Created by IntelliJ IDEA.

-- User: juemingzi

-- Date: 16/8/18

-- Time: 17:22

local function get_max_seq()

local key = tostring(KEYS[1])

local incr_amoutt = tonumber(KEYS[2])

local seq = tostring(KEYS[3])

local month_in_seconds = 24 * 60 * 60 * 30

if (1 == redis.call(\'setnx\', key, seq))

then

redis.call(\'expire\', key, month_in_seconds)

return seq

else

local prev_seq = redis.call(\'get\', key)

if (prev_seq < seq)

then

redis.call(\'set\', key, seq)

return seq

else

--[[

不能直接返回redis.call(\'incr\', key),因为返回的是number浮点数类型,会出现不精确情况。

注意: 类似"16081817202494579"数字大小已经快超时lua和reids最大数值,请谨慎的增加seq的位数

--]]

redis.call(\'incrby\', key, incr_amoutt)

return redis.call(\'get\', key)

end

end

end

return get_max_seq()1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

测试代码

public class SeqGeneratorTest extends BaseTest {

@Test

public void testGetNextSeq() throws Exception {

final SeqGenerator seqGenerater = new SeqGenerator("orderId");

String orderId = seqGenerater.getNextSeq(Integer.toString(WaitingOrder.KIND_TAKE_OUT));

assertNotNull(orderId);

System.out.println("orderId is: " + orderId);

}

@Test

public void testGetNextSeqWithMultiThread() throws Exception {

int cpus = Runtime.getRuntime().availableProcessors();

CountDownLatch begin = new CountDownLatch(1);

CountDownLatch end = new CountDownLatch(cpus);

final Set seqSet = new ConcurrentSkipListSet<>();

ExecutorService executorService = Executors.newFixedThreadPool(cpus);

final SeqGenerator seqGenerater = new SeqGenerator("orderId");

for (int i = 0; i < cpus; i++) {

executorService.execute(new Worker(seqGenerater, seqSet, begin, end));

}

begin.countDown();

end.await();

assertEquals(seqSet.size(), cpus * 10000);

System.out.println("finish!");

}

private static class Worker implements Runnable {

private final CountDownLatch begin;

private final CountDownLatch end;

private final Set seqSet;

private final SeqGenerator seqGenerator;

public Worker(SeqGenerator seqGenerator, Set seqSet, CountDownLatch begin, CountDownLatch end) {

this.seqGenerator = seqGenerator;

this.seqSet = seqSet;

this.begin = begin;

this.end = end;

}

@Override

public void run() {

try {

begin.await();

for (int i = 0; i < 10000; i++) {

String seq = seqGenerator.getNextSeq("2");

if (!seqSet.add(seq)) {

System.out.println(seq);

fail();

}

}

System.out.println("end");

} catch (Exception e) {

e.printStackTrace();

} finally {

end.countDown();

}

}

}

}1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

java redis 生成唯一id_Redis在集群环境中生成唯一ID相关推荐

  1. 在集群环境中使用 EhCache 缓存系统|RMI 集群模式

    RMI 是 Java 的一种远程方法调用技术,是一种点对点的基于 Java 对象的通讯方式.EhCache 从 1.2 版本开始就支持 RMI 方式的缓存集群.在集群环境中 EhCache 所有缓存对 ...

  2. 在集群环境中安装sql2005的sp2

    打开从微软网站下载好的补丁,就看下面图片的安培过程吧: 注意:此补丁要在当前活动的节点上运行才有效,在其它非活动节点安装无效. 注意:上页面列出此集群环境中所有的sql服务器,在这里选择所有的,就是给 ...

  3. 大数据之-Hadoop完全分布式_集群中分发脚本xsync_集群环境中同时配置大量主机---大数据之hadoop工作笔记0033

    然后我们来说一下,如果在集群环境中动不动,10000以上台主机,那么这个时候 我们怎么去配置,不能一台一台的去配置,我们可以这样. 使用xsync来进行文件同步. 这个时候我们需要一个基于xsync编 ...

  4. powerha_在IBM PowerHA集群环境中实现存储数据的服务器端缓存

    本文介绍了如何将IBMPowerHA®集群配置为使用定制的应用程序脚本为应用程序提供高可用性(HA)时,如何在IBM®AIX®操作系统上启用服务器端存储数据缓存 . AIX提供AIX 7.1 TL4 ...

  5. Redis Cluster高可用(HA)集群环境搭建详细步骤

    1.为什么要有集群 由于Redis主从复制架构每个数据库都要保存整个集群中的所有数据,容易形成木桶效应,所以Redis3.0之后的版本添加特性就是集群(Cluster) 2.Redis集群架构说明 架 ...

  6. CentOS 7.5 编译安装redis集成至系统服务(包括集群环境)

    用户 需要一个有sudo权限的用户或者是root用户 #若没有可执行下面的操作 #赋予一个用户(如learn)拥有sudo权限(centos7),需要root权限 id learn #查看已加入的用户 ...

  7. sql 树状结构中知道 父节点与孙节点_集群环境中使用Zookeeper实现分布式幂等控制...

    一.什么是Zookeeper? Zookeeper(业界简称zk)是一种提供配置管理.分布式协同以及命名的中心化服务,这些提供的功能都是分布式系统中非常底层且必不可少的基本功能,但是如果自己实现这些功 ...

  8. java中一级缓存_java – 集群环境中的hibernate一级缓存

    您的第一个语句不正确,hibernate中的第一级缓存不在同一JVM的边界内维护.它保持在休眠会话的边界​​内. Hibernate分别处理会话(会话中的实体,这是第一级缓存),即使在同一个jvm中, ...

  9. java集群调度_集群环境下定时调度的解决方案之Quartz集群

    集群环境可能出现的问题 在上一篇博客我们介绍了如何在自己的项目中从无到有的添加了Quartz定时调度引擎,其实就是一个Quartz 和Spring的整合过程,很容易实现,但是我们现在企业中项目通常都是 ...

最新文章

  1. 量化策略研究员 - 工具篇
  2. Leetcode 217. 存在重复元素 解题思路及C++实现
  3. ttl传输中过期的原因_Redis流行的原因
  4. FPGA课程设计使用VHDL语言
  5. [js]写一个获取非行间样式的方法
  6. 博士生是大学的廉价劳动力吗
  7. MAC版CRT使用心得
  8. 弄潮儿数据_SINX 信息数据的“弄潮儿”
  9. python中通过pip安装套件
  10. Java不支持创建范型数组分析
  11. Turboc C 编译出错信息的中文翻译 - C/C++ / C语言。
  12. js 文件上传 图片上传 传输速度计算
  13. EXCEL 根据超链接直接显示图片
  14. PS不会用?史上最全面的PS快捷键图文使用指南来了!
  15. 英语论文拟定论文标题的基本原则
  16. 呕心沥血大放血,今天小企鹅来给大家送福利了!!!Mac.Win.Lin虚拟机映像/资源超全[分享]
  17. 光环《全脑思维下的敏捷产品构建》总结
  18. 左飞老师——凝聚人生智慧的6句话
  19. 软件开发沉思录读书笔记
  20. RSF-Center,集群模式下-协调数据结构

热门文章

  1. 高德地图 android 调用 amap.clear()后定位蓝点消失 如何重新显示定位
  2. uva11361数位dp
  3. c语言获取dll文件路径,C语言URLDownloadToFile获取文件下载进度
  4. aMDcpu不支持mysql_Oracle 11.2.0.1在AMD CPU 64位硬件,32位操作系统下的BUG 8670579
  5. 鸿蒙是内核名字,华为徐直军:鸿蒙只是内核的名字,是媒体给误解成操作系统...
  6. ubuntu 远程桌面
  7. A20 网卡驱动分析
  8. half-sync/half-async 和 Leader/Followers 模式的主要区别
  9. 海量数据持久层解决方案_爱数AnyBackup重磅发布海量非结构化数据超可用解决方案...
  10. 香肠派对电脑版_《香肠派对》是不是除了《和平精英》最成功的吃鸡手游:靠恶搞火了?...