当前位置:   article > 正文

数据库分库分表-需要分布式全局Id_分库分表要依赖分布式id吗

分库分表要依赖分布式id吗

在项目开发中,随着业务的不断发展数据日增增大。这个时候就会出现数据表的分库分表的操作。一旦进行了分库和分表操作。传统的id就失去了意义。所以需要分布式全局ID。

分布式全局ID的特点

1:全局唯一性,不能出现重复的ID
2:单调递增,保证下一个ID一定大于上一个ID
3:范围趋势递增。在一个时间段内,生成的ID是递增趋势的比如:202012120001 202012120002….
第二天的时候又要从1开始计数。202012130001 202012130002……
4:安全性,在不同的领域中我们有些业务不要出现连续的递增,可以很好的保护数据格式和形态。因为很容易让竞争对手套取数据。

上述的2和4是互斥的。无法同时满足。

实现方式

  • UUID
  • Redis
  • Twitter的雪花算法
  • 美团的Leaf算法

redis实现分布式全局唯一ID

第一种:单调递增的的具体实现:

就我个人看来,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;
    }
}

  • 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

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
  • 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

基于范围的规则递增的分布式全局id

比如:系统需要生成根据业务类型生成流水号,每天从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;
    }
}
  • 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

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();
    }
}
  • 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

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);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/307186
推荐阅读
相关标签
  

闽ICP备14008679号