赞
踩
在项目开发中,随着业务的不断发展数据日增增大。这个时候就会出现数据表的分库分表的操作。一旦进行了分库和分表操作。传统的id就失去了意义。所以需要分布式全局ID。
1:全局唯一性,不能出现重复的ID
2:单调递增,保证下一个ID一定大于上一个ID
3:范围趋势递增。在一个时间段内,生成的ID是递增趋势的比如:202012120001 202012120002….
第二天的时候又要从1开始计数。202012130001 202012130002……
4:安全性,在不同的领域中我们有些业务不要出现连续的递增,可以很好的保护数据格式和形态。因为很容易让竞争对手套取数据。
上述的2和4是互斥的。无法同时满足。
就我个人看来,redisAtomicLong做分布式ID还是有一定的局限性。单纯针对redisAtomicLong数据丢失的解决方案,如果有什么好的建议,期待您的继续分享
思路:调用incr命令
1:Redis的incr命令具备了incr and get的原子性操作,即增加了结果的原子性操作,这个原子性操作很方便实现了唯一的全局分布式ID。
2:Redis本身是单线程架构。INCR命令不会出现重复的ID
代码:
1:采用redis的incr命令,从1自增id.
2:海量的数据存储一般都采用分库分表来进行存储。一般最多分1024个表,一般建表如:product_0,product_1,product_2……product_1023个表。
service
package com.kuangstudy.service.creatorid; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.support.atomic.RedisAtomicLong; import org.springframework.stereotype.Service; @Service @Log4j2 public class RedisCreatorIdService { @Autowired private RedisTemplate redisTemplate; /** * @Description 生成全局id * @Date 18:50 2021/5/20 **/ public Long incrmentid() { RedisAtomicLong entityIdCounter = new RedisAtomicLong("id:creator:product", redisTemplate.getConnectionFactory());//0 // 计数器累加 Long increment = entityIdCounter.incrementAndGet(); if (null == incr || incr.longValue() == 0) {// 初始设置过期时间 // 设置过期时间 24小时 entityIdCounter.expire(24, TimeUnit.HOURS); // 这里取第二次 incr 就是从1开始了,默认从0开始 incr = entityIdCounter.getAndIncrement(); } log.debug(keyName + "==========" + incr); return incr; // 或者 //Long increment = redisTemplate.opsForValue().increment(key); //return increment; } }
controller
package com.kuangstudy.controller.creatorid; import com.kuangstudy.entity.Product; import com.kuangstudy.service.creatorid.RedisCreatorIdService; import com.kuangstudy.vo.R; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ProductController { @Autowired private RedisCreatorIdService redisCreatorIdService; @PostMapping("/product/creator") public R creatorIds() { // 1:创建产品的分布式全局ID Long productid = redisCreatorIdService.incrmentid(); // 2:创建产品 Product product = new Product(); product.setId(productid); product.setTitle("title:" + productid); System.out.println("保存产品是:" + product.toString()); // 3: 存储到指定的数据库中 int table = (int) (productid % 1024); System.out.println("保存到的数据库表是:product_" + table); return R.ok().data("tablename", "product_" + table); } }
比如:系统需要生成根据业务类型生成流水号,每天从1开始生成,第二天会清零继续从0开始,流水号格式为: bizCode + date + incr 如:TT-201711230000100。
思路:
1、利用Redis Incr 生成序列号,使用日期加业务编码作为组合Key,这样保证第二天生成的序列号又是从1开始。
2、由于我们业务量不是很大,这里在生成序列号之前先判断一下当前key是否存在,若不存在,设置此key过期时间为当天晚上23:59:59,避免生成很多过期的key。
service
package com.itheima.itheimadistributelock.redis; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.support.atomic.RedisAtomicLong; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Component public class CacheService { //这里因为有其他的template,所以名字起得不好看 @Autowired RedisTemplate redisTemplate; public Long getIncrementNum(String key) { // 不存在准备创建 键值对 RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());//0 // 计数器累加 Long counter = entityIdCounter.incrementAndGet(); System.out.println("=========================>"+ counter); if ((null == counter || counter.longValue() == 1)) {// 初始设置过期时间 System.out.println("设置过期时间为1天!"); // 设置清除的目的,是让每天的计数器都从0开始 entityIdCounter.expire(1, TimeUnit.DAYS);// 单位天 } return counter; } }
utils
package com.kuangstudy.service.creatorid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; /** * @Author 徐柯 * @Description 统一订单流水号 * @Date 19:29 2021/5/20 * @Param * @return **/ @Component public class SequenceUtils { @Autowired CacheService cacheService; // 1:每天公司的订单数量,如果一天是几百写 3 几千就 4 几万 5 static final int DEFAULT_LENGTH = 3; public String getSequence() { // 1: 时间前缀 String currentDate = new SimpleDateFormat("yyyyMMdd").format(new Date()); // 2:redis递增获取每天的递增数量 Long num = cacheService.getIncrementNum("id:generator:order:" + currentDate); // 3:获取递增长度,是否小于DEFAULT_LENGTH 如果小于就前面补零。如果大于就递增即可 String str = String.valueOf(num); // 4:补零操作 int len = str.length(); // 4-1:是否小于DEFAULT_LENGTH 如果小于就前面补零。如果大于就递增即可 StringBuilder sb = new StringBuilder(); // 5:添加时间 sb.append(currentDate); if (len >= DEFAULT_LENGTH) { sb.append(str); return sb.toString(); } int rest = DEFAULT_LENGTH - len; for (int i = 0; i < rest; i++) { sb.append('0'); } sb.append(str); // 6: 时间+补零操作返回订单流水号 return sb.toString(); } }
controller
package com.kuangstudy.controller.creatorid; /** * @author 飞哥 * @Title: 学相伴出品 * @Description: 我们有一个学习网站:https://www.kuangstudy.com * @date 2021/5/20 18:46 */ @RestController public class ProductController { @Autowired private SequenceUtils sequenceUtils; @PostMapping("/product/creator2") public R creatorIds2() { // 1:创建产品的分布式全局ID List<String> ids = new ArrayList<>(); for (int i = 0; i < 100; i++) { String sequence = sequenceUtils.getSequence(); ids.add(sequence); } return R.ok().data("ids", ids); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。