精华内容
下载资源
问答
  • 全局唯一的id怎么解决? 一般要求:1.保证生成的ID全局唯一,不可重复 2.生成的后一个Id必须大于前一个Id 3.信息安全,防止恶意用户按照规则获取id 业界方案 1.UUID 通用识别码16字节,128位的长数字。 组成...

    分库分表情况下

    跨库的问题怎么解决?

     

    分布式事务怎么解决?

     

    查询结果集集合合并的问题?

     

    全局唯一的id怎么解决?

    一般要求:
    1.保证生成的ID全局唯一,不可重复

    2.生成的后一个Id必须大于前一个Id

    3.信息安全,防止恶意用户按照规则获取id

    业界方案

    1.UUID

    通用识别码16字节,128位的长数字。

    组成部分:当前日期和时间序列,mac地址

    2.mysql本身自增

    3.redis集群生成

    4.自定义生成

     参考:http://blog.csdn.net/z69183787/article/details/61918672

    转载于:https://www.cnblogs.com/gyadmin/p/8311018.html

    展开全文
  • 经过以上设置后,当系统进入锁定状态时,其他用户就再也不能解除其他用户就再也不能解除屏幕保护程序状态了,无论怎么晃动鼠标或敲击键盘也不会出现任何要求输入密码的窗口。而唯一的解除方法就是你自己做回电脑前...
  • 分布式id生成器

    2019-09-29 10:28:04
    微软公司通用唯一识别码(UUID) Twitter公司雪花算法(SnowFlake) 基于数据库的id自增 对id进行缓存 这里我们要谈到snowflake算法了 snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心...

    在高并发或者分表分库情况下怎么保证数据id的幂等性呢

    经常用到的解决方案有以下几种。

    微软公司通用唯一识别码(UUID)
    Twitter公司雪花算法(SnowFlake)
    基于数据库的id自增
    对id进行缓存

    这里我们要谈到snowflake算法了

    snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0。

    snowflake算法所生成的ID结构

    分布式id生成器

    整个结构是64位,所以我们在Java中可以使用long来进行存储。该算法实现基本就是二进制操作,单机每秒内理论上最多可以生成1024*(2^12),也就是409.6万个ID(1024 X 4096 = 4194304)

    • 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

    • 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

    • 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)

    • 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

    • 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId

    • 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

    • 加起来刚好64位,为一个Long型。

    • SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。

    snowFlake算法的优点:

    1. 生成ID时不依赖于DB,完全在内存生成,高性能高可用。

    2. ID呈趋势递增,后续插入索引树的时候性能较好。

    SnowFlake算法的缺点:

    依赖于系统时钟的一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序

    算法代码如下

    public class SnowflakeIdWorker {
     // ==============================Fields==================
        /** 开始时间截 (2019-08-06) */
        private final long twepoch = 1565020800000L;
    
        /** 机器id所占的位数 */
        private final long workerIdBits = 5L;
    
        /** 数据标识id所占的位数 */
        private final long datacenterIdBits = 5L;
    
        /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
        private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    
        /** 支持的最大数据标识id,结果是31 */
        private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    
        /** 序列在id中占的位数 */
        private final long sequenceBits = 12L;
    
        /** 机器ID向左移12位 */
        private final long workerIdShift = sequenceBits;
    
        /** 数据标识id向左移17位(12+5) */
        private final long datacenterIdShift = sequenceBits + workerIdBits;
    
        /** 时间截向左移22位(5+5+12) */
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
        /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    
        /** 工作机器ID(0~31) */
        private long workerId;
    
        /** 数据中心ID(0~31) */
        private long datacenterId;
    
        /** 毫秒内序列(0~4095) */
        private long sequence = 0L;
    
        /** 上次生成ID的时间截 */
        private long lastTimestamp = -1L;
    
         //==============================Constructors====================
        /**
         * 构造函数
         * @param workerId 工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
        public SnowflakeIdWorker(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }
    
        // ==============================Methods=================================
        /**
         * 获得下一个ID (该方法是线程安全的)
         * @return SnowflakeId
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
    
            //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }
    
            //如果是同一时间生成的,则进行毫秒内序列
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                //毫秒内序列溢出
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            }
            //时间戳改变,毫秒内序列重置
            else {
                sequence = 0L;
            }
    
            //上次生成ID的时间截
            lastTimestamp = timestamp;
    
            //移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - twepoch) << timestampLeftShift) //
                    | (datacenterId << datacenterIdShift) //
                    | (workerId << workerIdShift) //
                    | sequence;
        }
    
        /**
         * 阻塞到下一个毫秒,直到获得新的时间戳
         * @param lastTimestamp 上次生成ID的时间截
         * @return 当前时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
        /**
         * 返回以毫秒为单位的当前时间
         * @return 当前时间(毫秒)
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        //==============================Test=============================================
        /** 测试 */
        public static void main(String[] args) {
            SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
            for (int i = 0; i < 1000; i++) {
                long id = idWorker.nextId();
                System.out.println(Long.toBinaryString(id));
                System.out.println(id);
            }
        }
    }
    

    快速使用snowflake算法只需以下几步

    引入hutool依赖

    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-captcha</artifactId>
        <version>${hutool.version}</version>
    </dependency>
    

    ID 生成器

    public class IdGenerator {
    
        private long workerId = 0;
    
        @PostConstruct
        void init() {
            try {
                workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
                log.info("当前机器 workerId: {}", workerId);
            } catch (Exception e) {
                log.warn("获取机器 ID 失败", e);
                workerId = NetUtil.getLocalhost().hashCode();
                log.info("当前机器 workerId: {}", workerId);
            }
        }
    
        /**
         * 获取一个批次号,形如 2019071015301361000101237
         * <p>
         * 数据库使用 char(25) 存储
         *
         * @param tenantId 租户ID,5 位
         * @param module   业务模块ID,2 位
         * @return 返回批次号
         */
        public synchronized String batchId(int tenantId, int module) {
            String prefix = DateTime.now().toString(DatePattern.PURE_DATETIME_MS_PATTERN);
            return prefix + tenantId + module + RandomUtil.randomNumbers(3);
        }
    
        @Deprecated
        public synchronized String getBatchId(int tenantId, int module) {
            return batchId(tenantId, module);
        }
    
        /**
         * 生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
         *
         * @return
         */
        public String simpleUUID() {
            return IdUtil.simpleUUID();
        }
    
        /**
         * 生成的UUID是带-的字符串,类似于:a5c8a5e8-df2b-4706-bea4-08d0939410e3
         *
         * @return
         */
        public String randomUUID() {
            return IdUtil.randomUUID();
        }
    
        private Snowflake snowflake = IdUtil.createSnowflake(workerId, 1);
    
        public synchronized long snowflakeId() {
            return snowflake.nextId();
        }
    
        public synchronized long snowflakeId(long workerId, long dataCenterId) {
            Snowflake snowflake = IdUtil.createSnowflake(workerId, dataCenterId);
            return snowflake.nextId();
        }
    
        /**
         * 生成类似:5b9e306a4df4f8c54a39fb0c
         * <p>
         * ObjectId 是 MongoDB 数据库的一种唯一 ID 生成策略,
         * 是 UUID version1 的变种,详细介绍可见:服务化框架-分布式 Unique ID 的生成方法一览。
         *
         * @return
         */
        public String objectId() {
            return ObjectId.next();
        }
    
    }
    

    测试类

    public class IdGeneratorTest {
    
        @Autowired
        private IdGenerator idGenerator;
    
        @Test
        public void testBatchId() {
            for (int i = 0; i < 100; i++) {
                String batchId = idGenerator.batchId(1001, 100);
                log.info("批次号: {}", batchId);
            }
        }
    
        @Test
        public void testSimpleUUID() {
            for (int i = 0; i < 100; i++) {
                String simpleUUID = idGenerator.simpleUUID();
                log.info("simpleUUID: {}", simpleUUID);
            }
        }
    
        @Test
        public void testRandomUUID() {
            for (int i = 0; i < 100; i++) {
                String randomUUID = idGenerator.randomUUID();
                log.info("randomUUID: {}", randomUUID);
            }
        }
    
        @Test
        public void testObjectID() {
            for (int i = 0; i < 100; i++) {
                String objectId = idGenerator.objectId();
                log.info("objectId: {}", objectId);
            }
        }
    
        @Test
        public void testSnowflakeId() {
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            for (int i = 0; i < 20; i++) {
                executorService.execute(() -> {
                    log.info("分布式 ID: {}", idGenerator.snowflakeId());
                });
            }
            executorService.shutdown();
        }
    
    }

     

    分布式id生成器

    在项目中我们只需要注入 @Autowired private IdGenerator idGenerator;即可

    然后设置id order.setId(idGenerator.snowflakeId() + "");

    1. SpringBoot内容聚合

    2. 面试题内容聚合

    3. 设计模式内容聚合

    4. Mybatis内容聚合

    5. 多线程内容聚合

    最后,推荐一个专注于Java学习的公众号,Java知音。分享java基础、原理性知识、JavaWeb实战、spring全家桶、设计模式及面试资料、开源项目,助力开发者成长!

    Javaç¥é³å®æ¹å¬ä¼å·

    展开全文
  • 作者:CoderZS出自:掘金原文:...微软公司通用唯一识别码(UUID)Twitter公司雪花算法(SnowFlake)基于数据库的id自增对id进行缓存这里我们要谈到snowflake算法了snowflake是Twitte...

    作者:CoderZS

    出自:掘金

    原文:https://juejin.im/post/5d8882d8f265da03e369c063


    在高并发或者分表分库情况下怎么保证数据id的幂等性呢?

    经常用到的解决方案有以下几种。

    微软公司通用唯一识别码(UUID)
    Twitter公司雪花算法(SnowFlake)
    基于数据库的id自增
    对id进行缓存

    这里我们要谈到snowflake算法了

    snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。

    其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0。

    snowflake算法所生成的ID结构,如下图:

    d6df6436d756fdcebf9a7be0c16431b5.png

    整个结构是64位,所以我们在Java中可以使用long来进行存储。

    该算法实现基本就是二进制操作,单机每秒内理论上最多可以生成1024*(2^12),也就是409.6万个ID(1024 X 4096 = 4194304)

    • 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
    • 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0
    • 41位时间截(毫秒级)。注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
    • 这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
    • 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId
    • 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
    • 加起来刚好64位,为一个Long型。
    • SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高
      经测试,SnowFlake每秒能够产生26万ID左右。

    snowFlake算法的优点:

    1. 生成ID时不依赖于DB,完全在内存生成,高性能高可用。
    2. ID呈趋势递增,后续插入索引树的时候性能较好。

    SnowFlake算法的缺点:

    依赖于系统时钟的一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序

    算法代码如下

    public class SnowflakeIdWorker {
     // ==============================Fields==================
        /** 开始时间截 (2019-08-06) */
        private final long twepoch = 1565020800000L;
    
        /** 机器id所占的位数 */
        private final long workerIdBits = 5L;
    
        /** 数据标识id所占的位数 */
        private final long datacenterIdBits = 5L;
    
        /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
        private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    
        /** 支持的最大数据标识id,结果是31 */
        private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    
        /** 序列在id中占的位数 */
        private final long sequenceBits = 12L;
    
        /** 机器ID向左移12位 */
        private final long workerIdShift = sequenceBits;
    
        /** 数据标识id向左移17位(12+5) */
        private final long datacenterIdShift = sequenceBits + workerIdBits;
    
        /** 时间截向左移22位(5+5+12) */
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
        /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    
        /** 工作机器ID(0~31) */
        private long workerId;
    
        /** 数据中心ID(0~31) */
        private long datacenterId;
    
        /** 毫秒内序列(0~4095) */
        private long sequence = 0L;
    
        /** 上次生成ID的时间截 */
        private long lastTimestamp = -1L;
    
         //==============================Constructors====================
        /**
         * 构造函数
         * @param workerId 工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
        public SnowflakeIdWorker(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }
    
        // ==============================Methods=================================
        /**
         * 获得下一个ID (该方法是线程安全的)
         * @return SnowflakeId
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
    
            //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }
    
            //如果是同一时间生成的,则进行毫秒内序列
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                //毫秒内序列溢出
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            }
            //时间戳改变,毫秒内序列重置
            else {
                sequence = 0L;
            }
    
            //上次生成ID的时间截
            lastTimestamp = timestamp;
    
            //移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - twepoch) << timestampLeftShift) //
                    | (datacenterId << datacenterIdShift) //
                    | (workerId << workerIdShift) //
                    | sequence;
        }
    
        /**
         * 阻塞到下一个毫秒,直到获得新的时间戳
         * @param lastTimestamp 上次生成ID的时间截
         * @return 当前时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
        /**
         * 返回以毫秒为单位的当前时间
         * @return 当前时间(毫秒)
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        //==============================Test=============================================
        /** 测试 */
        public static void main(String[] args) {
            SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
            for (int i = 0; i < 1000; i++) {
                long id = idWorker.nextId();
                System.out.println(Long.toBinaryString(id));
                System.out.println(id);
            }
        }
    }

    快速使用snowflake算法只需以下几步

    引入hutool依赖

    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-captchaartifactId>
        <version>${hutool.version}version>
    dependency>

    ID 生成器

    public class IdGenerator {
    
        private long workerId = 0;
    
        @PostConstruct
        void init() {
            try {
                workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
                log.info("当前机器 workerId: {}", workerId);
            } catch (Exception e) {
                log.warn("获取机器 ID 失败", e);
                workerId = NetUtil.getLocalhost().hashCode();
                log.info("当前机器 workerId: {}", workerId);
            }
        }
    
        /**
         * 获取一个批次号,形如 2019071015301361000101237
         *
         * 数据库使用 char(25) 存储
         *
         * @param tenantId 租户ID,5 位
         * @param module 业务模块ID,2 位
         * @return 返回批次号
         */
        public synchronized String batchId(int tenantId, int module) {
            String prefix = DateTime.now().toString(DatePattern.PURE_DATETIME_MS_PATTERN);
            return prefix + tenantId + module + RandomUtil.randomNumbers(3);
        }
    
        @Deprecated
        public synchronized String getBatchId(int tenantId, int module) {
            return batchId(tenantId, module);
        }
    
        /**
         * 生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
         *
         * @return
         */
        public String simpleUUID() {
            return IdUtil.simpleUUID();
        }
    
        /**
         * 生成的UUID是带-的字符串,类似于:a5c8a5e8-df2b-4706-bea4-08d0939410e3
         *
         * @return
         */
        public String randomUUID() {
            return IdUtil.randomUUID();
        }
    
        private Snowflake snowflake = IdUtil.createSnowflake(workerId, 1);
    
        public synchronized long snowflakeId() {
            return snowflake.nextId();
        }
    
        public synchronized long snowflakeId(long workerId, long dataCenterId) {
            Snowflake snowflake = IdUtil.createSnowflake(workerId, dataCenterId);
            return snowflake.nextId();
        }
    
        /**
         * 生成类似5b9e306a4df4f8c54a39fb0c
         *
    
        
    测试类
    
    public class IdGeneratorTest {
    
        @Autowired
        private IdGenerator idGenerator;
    
        @Test
        public void testBatchId() {
            for (int i = 0; i < 100; i++) {
                String batchId = idGenerator.batchId(1001, 100);
                log.info("批次号: {}", batchId);
            }
        }
    
        @Test
        public void testSimpleUUID() {
            for (int i = 0; i < 100; i++) {
                String simpleUUID = idGenerator.simpleUUID();
                log.info("simpleUUID: {}", simpleUUID);
            }
        }
    
        @Test
        public void testRandomUUID() {
            for (int i = 0; i < 100; i++) {
                String randomUUID = idGenerator.randomUUID();
                log.info("randomUUID: {}", randomUUID);
            }
        }
    
        @Test
        public void testObjectID() {
            for (int i = 0; i < 100; i++) {
                String objectId = idGenerator.objectId();
                log.info("objectId: {}", objectId);
            }
        }
    
        @Test
        public void testSnowflakeId() {
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            for (int i = 0; i < 20; i++) {
                executorService.execute(() -> {
                    log.info("分布式 ID: {}", idGenerator.snowflakeId());
                });
            }
            executorService.shutdown();
        }
    
    }
    
    

    679472fb3b2ee009515875ca847a4bcb.png


    在项目中我们只需要注入 @Autowired private IdGenerator idGenerator;即可
    然后设置id:order.setId(idGenerator.snowflakeId() + "");

    分布式视频教程:

    https://shimo.im/docs/mAZnpJeN05knO0y1shimo.im
    369acbcfe87900665e6fc72ac1f5bda5.png
    Java基础、入门、精通、架构师全套资源Java基础、入门、精通、架构师全套资源shimo.im
    369acbcfe87900665e6fc72ac1f5bda5.png

    3f08b98cc8092db908f1de32e9388fa3.png

    推荐阅读:

    java钢铁侠-马克51号:Leaf——美团点评分布式ID生成系统zhuanlan.zhihu.com
    java钢铁侠-马克51号:永远不要在代码中使用「User」这个单词!zhuanlan.zhihu.com
    ecebda307d50f25b8b658e8991dc6b59.png
    https://zhuanlan.zhihu.com/p/86730847zhuanlan.zhihu.com
    java钢铁侠-马克51号:中国顶级程序员图鉴,最后一个厉害了!java钢铁侠-马克51号:中国顶级程序员图鉴,最后一个厉害了!zhuanlan.zhihu.com
    java钢铁侠-马克51号:中国顶级程序员图鉴,最后一个厉害了!zhuanlan.zhihu.com
    展开全文
  • 链接:https://juejin.im/post/5d8882d8f265da03e369c063在高并发或者分表分库情况下怎么保证数据id的幂等性呢 经常用到的解决方案有以下几种: 微软公司通用唯一识别码(UUID) Twitter公司雪花算法(SnowFlake) ...

    关注我,可以获取最新知识、经典面试题以及微服务技术分享

    作者:CoderZS
    链接:https://juejin.im/post/5d8882d8f265da03e369c063

    在高并发或者分表分库情况下怎么保证数据id的幂等性呢 经常用到的解决方案有以下几种:

    • 微软公司通用唯一识别码(UUID)
    • Twitter公司雪花算法(SnowFlake)
    • 基于数据库的id自增
    • 对id进行缓存

    snowflake算法

    snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号,最后还有一个符号位,永远是0。

    snowflake算法所生成的ID结构

    70028a5b6904d760b4875cf9b3950338.png

    整个结构是64位,所以我们在Java中可以使用long来进行存储。 该算法实现基本就是二进制操作,单机每秒内理论上最多可以生成1024*(2^12),也就是409.6万个ID(1024 X 4096 = 4194304)

     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 
     * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
     * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
     * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
     * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
     * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
     * 加起来刚好64位,为一个Long型。<br>
     * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。

    snowFlake算法的优点:

    1. 生成ID时不依赖于DB,完全在内存生成,高性能高可用。
    2. ID呈趋势递增,后续插入索引树的时候性能较好。

    SnowFlake算法的缺点: 依赖于系统时钟的一致性。如果某台机器的系统时钟回拨,有可能造成ID冲突,或者ID乱序 算法代码如下

    public class SnowflakeIdWorker {
     // ==============================Fields==================
        /** 开始时间截 (2019-08-06) */
        private final long twepoch = 1565020800000L;
    
        /** 机器id所占的位数 */
        private final long workerIdBits = 5L;
    
        /** 数据标识id所占的位数 */
        private final long datacenterIdBits = 5L;
    
        /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
        private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    
        /** 支持的最大数据标识id,结果是31 */
        private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    
        /** 序列在id中占的位数 */
        private final long sequenceBits = 12L;
    
        /** 机器ID向左移12位 */
        private final long workerIdShift = sequenceBits;
    
        /** 数据标识id向左移17位(12+5) */
        private final long datacenterIdShift = sequenceBits + workerIdBits;
    
        /** 时间截向左移22位(5+5+12) */
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
        /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    
        /** 工作机器ID(0~31) */
        private long workerId;
    
        /** 数据中心ID(0~31) */
        private long datacenterId;
    
        /** 毫秒内序列(0~4095) */
        private long sequence = 0L;
    
        /** 上次生成ID的时间截 */
        private long lastTimestamp = -1L;
    
         //==============================Constructors====================
        /**
         * 构造函数
         * @param workerId 工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
        public SnowflakeIdWorker(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }
    
        // ==============================Methods=================================
        /**
         * 获得下一个ID (该方法是线程安全的)
         * @return SnowflakeId
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
    
            //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }
    
            //如果是同一时间生成的,则进行毫秒内序列
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                //毫秒内序列溢出
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            }
            //时间戳改变,毫秒内序列重置
            else {
                sequence = 0L;
            }
    
            //上次生成ID的时间截
            lastTimestamp = timestamp;
    
            //移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - twepoch) << timestampLeftShift) //
                    | (datacenterId << datacenterIdShift) //
                    | (workerId << workerIdShift) //
                    | sequence;
        }
    
        /**
         * 阻塞到下一个毫秒,直到获得新的时间戳
         * @param lastTimestamp 上次生成ID的时间截
         * @return 当前时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
        /**
         * 返回以毫秒为单位的当前时间
         * @return 当前时间(毫秒)
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        //==============================Test=============================================
        /** 测试 */
        public static void main(String[] args) {
            SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
            for (int i = 0; i < 1000; i++) {
                long id = idWorker.nextId();
                System.out.println(Long.toBinaryString(id));
                System.out.println(id);
            }
        }
    }
    
    
    

    快速使用snowflake算法只需以下几步

     <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-captcha</artifactId>
        <version>${hutool.version}</version>
    </dependency>

    ID 生成器

    public class IdGenerator {
    
        private long workerId = 0;
    
        @PostConstruct
        void init() {
            try {
                workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
                log.info("当前机器 workerId: {}", workerId);
            } catch (Exception e) {
                log.warn("获取机器 ID 失败", e);
                workerId = NetUtil.getLocalhost().hashCode();
                log.info("当前机器 workerId: {}", workerId);
            }
        }
    
        /**
         * 获取一个批次号,形如 2019071015301361000101237
         * <p>
         * 数据库使用 char(25) 存储
         *
         * @param tenantId 租户ID,5 位
         * @param module   业务模块ID,2 位
         * @return 返回批次号
         */
        public synchronized String batchId(int tenantId, int module) {
            String prefix = DateTime.now().toString(DatePattern.PURE_DATETIME_MS_PATTERN);
            return prefix + tenantId + module + RandomUtil.randomNumbers(3);
        }
    
        @Deprecated
        public synchronized String getBatchId(int tenantId, int module) {
            return batchId(tenantId, module);
        }
    
        /**
         * 生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
         *
         * @return
         */
        public String simpleUUID() {
            return IdUtil.simpleUUID();
        }
    
        /**
         * 生成的UUID是带-的字符串,类似于:a5c8a5e8-df2b-4706-bea4-08d0939410e3
         *
         * @return
         */
        public String randomUUID() {
            return IdUtil.randomUUID();
        }
    
        private Snowflake snowflake = IdUtil.createSnowflake(workerId, 1);
    
        public synchronized long snowflakeId() {
            return snowflake.nextId();
        }
    
        public synchronized long snowflakeId(long workerId, long dataCenterId) {
            Snowflake snowflake = IdUtil.createSnowflake(workerId, dataCenterId);
            return snowflake.nextId();
        }
    
        /**
         * 生成类似:5b9e306a4df4f8c54a39fb0c
         * <p>
         * ObjectId 是 MongoDB 数据库的一种唯一 ID 生成策略,
         * 是 UUID version1 的变种,详细介绍可见:服务化框架-分布式 Unique ID 的生成方法一览。
         *
         * @return
         */
        public String objectId() {
            return ObjectId.next();
        }
    
    }
    
    
    
    

    测试类

    public class IdGeneratorTest {
    
        @Autowired
        private IdGenerator idGenerator;
    
        @Test
        public void testBatchId() {
            for (int i = 0; i < 100; i++) {
                String batchId = idGenerator.batchId(1001, 100);
                log.info("批次号: {}", batchId);
            }
        }
    
        @Test
        public void testSimpleUUID() {
            for (int i = 0; i < 100; i++) {
                String simpleUUID = idGenerator.simpleUUID();
                log.info("simpleUUID: {}", simpleUUID);
            }
        }
    
        @Test
        public void testRandomUUID() {
            for (int i = 0; i < 100; i++) {
                String randomUUID = idGenerator.randomUUID();
                log.info("randomUUID: {}", randomUUID);
            }
        }
    
        @Test
        public void testObjectID() {
            for (int i = 0; i < 100; i++) {
                String objectId = idGenerator.objectId();
                log.info("objectId: {}", objectId);
            }
        }
    
        @Test
        public void testSnowflakeId() {
            ExecutorService executorService = Executors.newFixedThreadPool(20);
            for (int i = 0; i < 20; i++) {
                executorService.execute(() -> {
                    log.info("分布式 ID: {}", idGenerator.snowflakeId());
                });
            }
            executorService.shutdown();
        }
    
    }
    
    
    
    

    c85a0219af57a28609e869b472a9fe39.png

    在项目中我们只需要注入 @Autowired private IdGenerator idGenerator;即可 然后设置id order.setId(idGenerator.snowflakeId() + "");

    展开全文
  • 微软公司通用唯一识别码(UUID) Twitter公司雪花算法(SnowFlake) 基于数据库的id自增 对id进行缓存 一、SnowFlake算法 snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。 其核心思想是:...
  • 微软公司通用唯一识别码(UUID) Twitter公司雪花算法(SnowFlake) 基于数据库的id自增 对id进行缓存 这里我们要谈到snowflake算法了 snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。 其...
  • 微软公司通用唯一识别码(UUID)Twitter公司雪花算法(SnowFlake)基于数据库的id自增对id进行缓存一、SnowFlake算法snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒...
  • 微软公司通用唯一识别码(UUID)Twitter公司雪花算法(SnowFlake)基于数据库的id自增对id进行缓存一、SnowFlake算法snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒...
  • 点击上方“方志朋”,选择“设为星标”回复”666“获取新整理的面试资料本文来源:...微软公司通用唯一识别码(UUID)Twitter公司雪花算法(SnowFlake)基于数据库的id自增对id进行缓存这里我们要谈到snowf...
  • 基于SSM的校园二手交易平台 ...完整的设计报告在后面 喜欢就点一下star哟,谢谢亲的支持 ...个人主页 ...Java版本:1.8 数据库:MySQL 框架:Spring + Spring MVC + MyBatis 服务器:Tomcat 前端解析框架:Thymeleaf ...
  • <div><h1>前言:...那么使用<code>new Function怎么解决上面的问题呢?请看示例代码: <p><img alt="" src="https://img-blog.csdnimg.cn/img_convert/5929f12f40e8c046eabad04da80b4a46.png" /...
  • 软件工程教程

    热门讨论 2012-07-06 23:10:29
    用例只描述参与者和系统在交互过程中做些什么,并不描述怎么做。 用例图 关联关系 用例图 泛化关系 用例图 泛化关系 用例图 用例图 用例图 用例用于什么情况? 不知道什么情况不用用例 如果没有用到用例,...
  • LTS(light-task-scheduler)主要用于解决分布式任务调度问题,支持实时任务,定时任务和Cron任务。有较好的伸缩性,扩展性,健壮稳定性而被多家公司使用,同时也希望开源爱好者一起贡献。 ---> 底部有招人帖 ...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    3.2 代 分 析 .20 3.3 运 行 程 序 .23 .4 添 加 注 释 .25 3.5 小 结 .27 第二部分 C#程序设计基础.28 第四章 数 据 类 型 .28 4.1 值 类 型 .28 4.2 引 用 类 型 .33 4.3 装箱和拆箱 .39 4.4 ...
  • C#微软培训资料

    2014-01-22 14:10:17
    3.2 代 分 析 .20 3.3 运 行 程 序 .23 .4 添 加 注 释 .25 3.5 小 结 .27 第二部分 C#程序设计基础.28 第四章 数 据 类 型 .28 4.1 值 类 型 .28 4.2 引 用 类 型 .33 4.3 装箱和拆箱 .39 4.4 ...
  • 第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二...
  • 第三,实践类的操作系统书籍还是太少了,以至于你要想看看别人是怎么做的,除了读以《操作系统:设计与实现》为代表的极少数书籍之外,就是一头扎进源代码中,而结果有时相当令人气馁。我自己也气馁过,所以我在第二...
  •  查询方便,迅速解决工作中的难题  大量来自微软内部的技巧 内容简介  t-sql一直以来都是 sql server编程的基础。和传统的 t-sql书籍不同,本书以独特的 “技巧 ”形式来介绍知识点,涵盖了数据处理(增删改、...
  • CruiseYoung提供的带有详细书签的电子书籍目录 ... Oracle Database 11g数据库管理艺术(涵盖DBA必知必会的所有数据库管理知识) 基本信息 原书名: Expert Oracle Database 11g Administration 原出版社: Apress ...

空空如也

空空如也

1 2
收藏数 22
精华内容 8
关键字:

怎么解决识别唯一码