redis分布式ID解决方案示例详解

目录
  • 常用的分布式ID解决方案
    • UUID
    • Snowflake
      • Snowflake算法的Java代码:
    • Leaf
      • Leaf算法的Java代码:
  • 基于数据库自增ID生成
  • 基于UUID生成
  • 基于Redis生成
  • 基于ZooKeeper生成

常用的分布式ID解决方案

在分布式系统中,生成全局唯一ID是非常重要的,因为在分布式系统中,多个节点同时生成ID可能会导致ID冲突。

下面介绍几种常用的分布式ID解决方案。

UUID

UUID(通用唯一标识符)是由128位数字组成的标识符,它可以保证在全球范围内的唯一性,因为其生成算法基于时间戳、节点ID等因素。UUID可以使用Java自带的UUID类来生成,如下所示:

javaCopy code
import java.util.UUID;
public class UuidGenerator {
    public static void main(String[] args) {
        UUID uuid = UUID.randomUUID();
        System.out.println(uuid.toString());
    }
}

UUID的优点是简单易用,无需额外的配置和管理,可以直接使用Java自带的UUID类生成。但是UUID长度较长(128位),不太适合作为数据库表的主键,且不易于排序和索引。

Snowflake

Snowflake是Twitter开源的一种分布式ID生成算法,它可以生成64位的唯一ID,其中包含了时间戳、数据中心ID和机器ID等信息。Snowflake算法的Java代码如下所示:

Snowflake算法的Java代码:

javaCopy code
public class SnowflakeGenerator {
    private final static long START_STMP = 1480166465631L;
    private final static long SEQUENCE_BIT = 12;
    private final static long MACHINE_BIT = 5;
    private final static long DATACENTER_BIT = 5;
    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
    private long datacenterId;
    private long machineId;
    private long sequence = 0L;
    private long lastStmp = -1L;
    public SnowflakeGenerator(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }
    public synchronized long nextId() {
        long currStmp = getNewstmp();
        if (currStmp < lastStmp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }
        if (currStmp == lastStmp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {
            sequence = 0L;
        }
        lastStmp = currStmp;
        return (currStmp - START_STMP) << TIMESTMP_LEFT
                | datacenterId << DATACENTER_LEFT
                | machineId << MACHINE_LEFT
                | sequence;
    }
    private long getNextMill() {
        long mill = getNewstmp();
        while (mill <= lastStmp) {
            mill = getNewstmp();
        }
        return mill;
    }
    private long getNewstmp() {
        return System.currentTimeMillis();
    }
}

Snowflake算法的优点是生成ID的性能高,且ID长度较短(64位),可以作为数据库表的主键,且便于排序和索引。但是需要注意,如果集群中的节点数超过了机器ID所占的位数,或者集群规模很大,时间戳位数不够用,那么就需要考虑其他的分布式ID生成算法。

Leaf

Leaf是美团点评开源的一种分布式ID生成算法,它可以生成全局唯一的64位ID。Leaf算法的Java代码如下所示:

Leaf算法的Java代码:

javaCopy code
public class LeafGenerator {
    private static final Logger logger = LoggerFactory.getLogger(LeafGenerator.class);
    private static final String WORKER_ID_KEY = "leaf.worker.id";
    private static final String PORT_KEY = "leaf.port";
    private static final int DEFAULT_PORT = 8080;
    private static final int DEFAULT_WORKER_ID = 0;
    private static final int WORKER_ID_BITS = 10;
    private static final int SEQUENCE_BITS = 12;
    private static final int MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1;
    private static final int MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1;
    private static final long EPOCH = 1514736000000L;
    private final SnowflakeIdWorker idWorker;
    public LeafGenerator() {
        int workerId = SystemPropertyUtil.getInt(WORKER_ID_KEY, DEFAULT_WORKER_ID);
        int port = SystemPropertyUtil.getInt(PORT_KEY, DEFAULT_PORT);
        this.idWorker = new SnowflakeIdWorker(workerId, port);
        logger.info("Initialized LeafGenerator with workerId={}, port={}", workerId, port);
    }
    public long nextId() {
        return idWorker.nextId();
    }
    private static class SnowflakeIdWorker {
        private final long workerId;
        private final long port;
        private long sequence = 0L;
        private long lastTimestamp = -1L;
        SnowflakeIdWorker(long workerId, long port) {
            if (workerId < 0 || workerId > MAX_WORKER_ID) {
                throw new IllegalArgumentException(String.format("workerId must be between %d and %d", 0, MAX_WORKER_ID));
            }
            this.workerId = workerId;
            this.port = port;
        }
        synchronized long nextId() {
            long timestamp = System.currentTimeMillis();
            if (timestamp < lastTimestamp) {
                throw new RuntimeException("Clock moved backwards. Refusing to generate id");
            }
            if (timestamp == lastTimestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                if (sequence == 0L) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0L;
            }
            lastTimestamp = timestamp;
            return ((timestamp - EPOCH) << (WORKER_ID_BITS + SEQUENCE_BITS))
                    | (workerId << SEQUENCE_BITS)
                    | sequence;
        }
        private long tilNextMillis(long lastTimestamp) {
            long timestamp = System.currentTimeMillis();
            while (timestamp <= lastTimestamp) {
                timestamp = System.currentTimeMillis();
            }
            return timestamp;
        }
    }
}

Leaf算法的特点是生成ID的速度比Snowflake算法略慢,但是可以支持更多的Worker节点。Leaf算法生成的ID由三部分组成,分别是时间戳、Worker ID和序列号,其中时间戳占用42位、Worker ID占用10位、序列号占用12位,总共64位。

以上是常见的分布式ID生成算法,当然还有其他的一些方案,如:MongoDB ID、UUID、Twitter Snowflake等。不同的方案适用于不同的业务场景,具体实现细节和性能表现也有所不同,需要根据实际情况选择合适的方案。

除了上述介绍的分布式ID生成算法,还有一些新的分布式ID生成方案不断涌现,例如Flicker的分布式ID生成算法,它使用了类似于Snowflake的思想,但是采用了不同的位数分配方式,相比Snowflake更加灵活,并且可以根据需要动态调整每个部分占用的位数。此外,Facebook还推出了ID Generation Service (IGS)方案,该方案将ID的生成和存储分离,提供了更加灵活和可扩展的方案,但是需要进行更加复杂的架构设计和实现。

针对不同的业务需求,可以设计多套分布式ID生成方案。下面是我个人的一些建议:

  • 基于数据库自增ID生成:使用数据库自增ID作为全局唯一ID,可以很好的保证ID的唯一性,并且实现简单,但是并发量较高时可能会导致性能瓶颈。因此,在高并发场景下不建议使用。
  • 基于UUID生成:使用UUID作为全局唯一ID,可以很好地保证ID的唯一性,但是ID长度较长(128位),不便于存储和传输,并且存在重复ID的概率非常小但不为0。因此,建议在分布式系统中使用时要考虑ID的长度和存储传输的成本。
  • 基于Redis生成:使用Redis的原子性操作,可以保证ID的唯一性,并且生成ID的速度非常快,可以适用于高并发场景。但是需要注意,如果Redis宕机或者性能不足,可能会影响ID的生成效率和可用性。
  • 基于ZooKeeper生成:使用ZooKeeper的序列号生成器,可以保证ID的唯一性,并且实现较为简单,但是需要引入额外的依赖和资源,并且可能会存在性能瓶颈。

选择适合自己业务场景的分布式ID生成方案,需要综合考虑ID的唯一性、生成速度、长度、存储成本、可扩展性、可用性等多个因素。同时需要注意,不同方案的实现细节和性能表现也有所不同,需要根据实际情况进行权衡和选择。

下面给出每种方案的详细代码demo:

基于数据库自增ID生成

javaCopy code
public class IdGenerator {
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/test";
    private static final String JDBC_USER = "root";
    private static final String JDBC_PASSWORD = "password";
    public long generateId() {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            pstmt = conn.prepareStatement("INSERT INTO id_generator (stub) VALUES (null)", Statement.RETURN_GENERATED_KEYS);
            pstmt.executeUpdate();
            rs = pstmt.getGeneratedKeys();
            if (rs.next()) {
                return rs.getLong(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (pstmt != null) {
                    pstmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return 0L;
    }
}

基于UUID生成

javaCopy code
import java.util.UUID;
public class IdGenerator {
    public String generateId() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}

基于Redis生成

javaCopy code
import redis.clients.jedis.Jedis;
public class IdGenerator {
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;
    private static final String REDIS_PASSWORD = "password";
    private static final int ID_GENERATOR_EXPIRE_SECONDS = 3600;
    private static final String ID_GENERATOR_KEY = "id_generator";
    public long generateId() {
        Jedis jedis = null;
        try {
            jedis = new Jedis(REDIS_HOST, REDIS_PORT);
            jedis.auth(REDIS_PASSWORD);
            long id = jedis.incr(ID_GENERATOR_KEY);
            jedis.expire(ID_GENERATOR_KEY, ID_GENERATOR_EXPIRE_SECONDS);
            return id;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return 0L;
    }
}

基于ZooKeeper生成

javaCopy code
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class IdGenerator implements Watcher {
    private static final String ZK_HOST = "localhost";
    private static final int ZK_PORT = 2181;
    private static final int SESSION_TIMEOUT = 5000;
    private static final String ID_GENERATOR_NODE = "/id_generator";
    private static final int ID_GENERATOR_EXPIRE_SECONDS = 3600;
    private long workerId = 0;
    public IdGenerator() {
        try {
            ZooKeeper zk = new ZooKeeper(ZK_HOST + ":" + ZK_PORT, SESSION_TIMEOUT, this);
            CountDownLatch latch = new CountDownLatch(1);
            latch.await();
            if (zk.exists(ID_GENERATOR_NODE, false) == null) {
                zk.create(ID_GENERATOR_NODE, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            workerId = zk.getChildren(ID_GENERATOR_NODE, false).size();
            zk.create(ID_GENERATOR_NODE + "/worker_" + workerId, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public long generateId() {
        ZooKeeper zk = null;
        try {
            zk = new ZooKeeper(ZK_HOST + ":" + ZK_PORT, SESSION_TIMEOUT, null);
            CountDownLatch latch = new CountDownLatch(1);
            latch.await();
            zk.create(ID_GENERATOR_NODE + "/id_", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> {}, null);
            byte[] data = zk.getData(ID_GENERATOR_NODE + "/worker_" + workerId, false, null);
            long id = Long.parseLong(new String(data)) * 10000 + zk.getChildren(ID_GENERATOR_NODE, false).size();
            return id;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (zk != null) {
                try {
                    zk.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return 0L;
    }
    @Override
    public void process(WatchedEvent event) {
        if (event.getState() == Event.KeeperState.SyncConnected) {
            System.out.println("Connected to ZooKeeper");
            CountDownLatch latch = new CountDownLatch(1);
            latch.countDown();
        }
    }
}

注意,这里使用了ZooKeeper的临时节点来协调各个工作节点,如果一个工作节点挂掉了,它的临时节点也会被删除,这样可以保证每个工作节点获得的ID是唯一的。

以上就是各种分布式ID生成方案的详细代码demo,实际上,每种方案都有其优缺点,应根据具体业务场景和系统架构选择合适的方案。

以上就是redis分布式ID解决方案示例详解的详细内容,更多关于redis分布式ID的资料请关注我们其它相关文章!

(0)

相关推荐

  • redisson分布式限流RRateLimiter源码解析

    目录 分布式限流-单位时间多实例多线程访问次数限制 1.简单使用 2. 实现限流redisson使用了哪些redis数据结构 3. 超过10s,我再次获取一个令牌,数据结构发生的变化 4. 源码浅析 分布式限流-单位时间多实例多线程访问次数限制 接前面聊一聊redisson及优雅实现 和 说一说spring boot优雅集成redisson,简单以源码的方式给大家介绍了redisson的:可重入性.阻塞.续约.红锁.联锁.加锁解锁流程和集成spring boot注意点和优雅实现方式. 接下来在讲

  • Redis分布式锁解决秒杀超卖问题

    目录 分布式锁应用场景 单体锁的分类 分布式锁核心逻辑 分布式锁实现的问题——死锁和解决 Redis解决删除别人锁的问题 分布式锁应用场景 秒杀环境下:订单服务从库存中心拿到库存数,如果库存总数大于0,则进行库存扣减,并创建订单订单服务负责创建订单库存服务负责扣减库存 模拟用户访问库存 多线程并发访问,出现超卖问题,线程不安全.没有保证原子性 单体锁的分类 单体应用锁指的是只能在 一个JVM 进程内有效的锁.我们把这种锁叫做单体应用锁 synchronized锁ReentrantLock锁一个

  • Spring Boot 集成Redisson实现分布式锁详细案例

    目录 前言 分布式锁实现 引入jar包 Redisson的配置 application.yml中引入redisson.yml配置 redisson.yml配置 封装Redisson工具类 模拟秒杀扣减库存 测试代码 总结 前言 Spring Boot集成Redis实现单机分布式锁针对单机分布式锁还是存在锁定续期.可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解. 分布式锁实现 引入jar包 <dependency> <groupId>org

  • 基于Redis实现分布式单号及分布式ID(自定义规则生成)

    目录 背景 Redis实现方式 代码实例 单号生成枚举 单号生成工具类 单号生成接口 单号生成接口实现 使用测试 总结 背景 一些业务背景下,业务要求单号需要有区分不同的前缀,那么在分布式的架构下如何自定义单号而且还能保证唯一呢? 注:分布式ID也可以此方式 Redis实现方式 Redis的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保证生成的 ID 肯定是唯一有序的. 优点:不依赖于数据库,灵活方便,且性能优于数据库:数字ID天然排序,对分页或

  • Redisson分布式限流的实现原理解析

    目录 正文 RRateLimiter使用 RRateLimiter的实现 RRateLimiter使用时注意事项 RRateLimiter是非公平限流器 Rate不要设置太大 限流的上限取决于Redis单实例的性能 分布式限流的本质 正文 我们目前在工作中遇到一个性能问题,我们有个定时任务需要处理大量的数据,为了提升吞吐量,所以部署了很多台机器,但这个任务在运行前需要从别的服务那拉取大量的数据,随着数据量的增大,如果同时多台机器并发拉取数据,会对下游服务产生非常大的压力.之前已经增加了单机限流,

  • Java实现redis分布式锁的三种方式

    目录 一.引入原因 二.分布式锁实现过程中的问题 问题一:异常导致锁没有释放 问题二:获取锁与设置过期时间操作不是原子性的 问题三:锁过期之后被别的线程重新获取与释放 问题四:锁的释放不是原子性的 问题五:其他的问题? 三.具体实现 1. RedisTemplate 2. RedisLockRegistry 3. 使用redisson实现分布式锁 一.引入原因 在分布式服务中,常常有如定时任务.库存更新这样的场景. 在定时任务中,如果不使用quartz这样的分布式定时工具,只是简单的使用定时器来

  • redis分布式ID解决方案示例详解

    目录 常用的分布式ID解决方案 UUID Snowflake Snowflake算法的Java代码: Leaf Leaf算法的Java代码: 基于数据库自增ID生成 基于UUID生成 基于Redis生成 基于ZooKeeper生成 常用的分布式ID解决方案 在分布式系统中,生成全局唯一ID是非常重要的,因为在分布式系统中,多个节点同时生成ID可能会导致ID冲突. 下面介绍几种常用的分布式ID解决方案. UUID UUID(通用唯一标识符)是由128位数字组成的标识符,它可以保证在全球范围内的唯一

  • Redis中事件驱动模型示例详解

    前言 Redis 是一个事件驱动的内存数据库,服务器需要处理两种类型的事件. 文件事件 时间事件 下面就会介绍这两种事件的实现原理. 文件事件 Redis 服务器通过 socket 实现与客户端(或其他redis服务器)的交互,文件事件就是服务器对 socket 操作的抽象. Redis 服务器,通过监听这些 socket 产生的文件事件并处理这些事件,实现对客户端调用的响应. Reactor Redis 基于 Reactor 模式开发了自己的事件处理器. 这里就先展开讲一讲 Reactor 模

  • SpringBoot 集成 ShedLock 分布式锁的示例详解

    目录 一:ShedLock 简介 二:配置maven 依赖 三:SchedulerLock 基于 Redis 的配置 四:在启动类中添加 @EnableScheduling 标签 五:test 测试案例 一:ShedLock 简介 ShedLock是一个在分布式环境中使用的定时任务框架,用于解决在分布式环境中的多个实例的相同定时任务在同一时间点重复执行的问题.ShedLock确保计划的任务最多同时执行一次.如果一个任务正在一个节点上执行,它会获得一个锁,该锁将阻止从另一个节点(或线程)执行同一任

  • Golang分布式应用之Redis示例详解

    目录 正文 分布式锁 运行测试 分布式过滤器 运行测试 分布式限流器 运行测试 其他 正文 Redis作是一个高性能的内存数据库,常被应用于分布式系统中,除了作为分布式缓存或简单的内存数据库还有一些特殊的应用场景,本文结合Golang来编写对应的中间件. 本文所有代码见github.com/qingwave/go… 分布式锁 单机系统中我们可以使用sync.Mutex来保护临界资源,在分布式系统中同样有这样的需求,当多个主机抢占同一个资源,需要加对应的“分布式锁”. 在Redis中我们可以通过s

  • Redis快速实现分布式session的方法详解

    目录 前言 Spring Security Apache Shiro Session作用 spring-session 支持功能 分布式seesion实战 步骤1:依赖包 步骤2:配置文件 步骤3:实现逻辑 步骤4:编写session拦截器 步骤5:把拦截器注入到拦截器链中 步骤6:测试 前言 我们在开发一个项目时通常需要登录认证,常用的登录认证技术实现框架有Spring Security和shiro Spring Security Spring Security是一个功能强大且高度可定制的身份

  • Redis源码设计剖析之事件处理示例详解

    目录 1. Redis事件介绍 2. 事件的抽象 2.1 文件事件结构 2.2 时间事件结构 2.3 事件状态结构 3. 事件的实现 1. Redis事件介绍 Redis服务器是一个事件驱动程序,所谓事件驱动就是输入一条命令并且按下回车,然后消息被组装成Redis协议的格式发送给Redis服务器,这个时候就会产生一个事件,Redis服务器会接收改命令,处理该命令和发送回复,而当我们没有与服务器进行交互时,服务器就会处于阻塞等待状态,它会让出CPU然后进入睡眠状态,当事件触发时,就会被操作系统唤醒

  • go实现Redis读写分离示例详解

    目录 我们为什么需要了解RESP协议? 什么是RESP协议 RESP协议规范 如何使用该协议请求Redis 使用go编写Redis中间件实现读写分离 总结 我们为什么需要了解RESP协议? 本篇文章目的为探究RESP协议,而非编写读写中间件,这点要清楚. 关于这个问题,我想通过一个实例来解释,我们编写Redis中间件,为什么需要了解RESP协议. 以上代码是编写了一个非常简单的TCP服务器,我们监听8888端口,尝试使用redis-cli -p 8888连接服务器后,而后查看打印出来的应用层报文

  • React项目使用ES6解决方案及JSX使用示例详解

    目录 不使用 ES6 绑定 JSX如何? 不使用 ES6 然而,在纯浏览器端使用ES6语法时,浏览器支持存在差异,这需要特殊处理才能正常运行. 支持ES2015桌面浏览器 Chrome:从51版开始,它可以支持ES6 97%的新功能. Firefox:53版本支持97%的ES6新功能. Safari:从版本10开始,ES6 99%的新功能都可以得到支持. IE:Edge 15可以支持96%的ES6新功能. Edge 14可以支持93%的新ES6功能.(IE7~11基本不支持ES6) class

  • Redis如何存储对象与集合示例详解

    前言 大家都知道在项目中,缓存以及mq消息队列可以说是不可或缺的2个重要技术.前者主要是为了减轻数据库压力,大幅度提升性能.后者主要是为了提高用户的体验度,我理解的是再后端做的一个ajax请求(异步),并且像ribbmitmq等消息队列有重试机制等功能. 这里主要讲redis如何把对象,集合存入,并且取出.下面话不多说了,来一起看看详细的介绍吧. 1.在启动类上加入如下代码 private Jedis jedis;private JedisPoolConfig config;private Je

  • 微服务架构之服务注册与发现实践示例详解

    目录 1 服务注册中心 4种注册中心技术对比 2 Spring Cloud 框架下实现 2.1 Spring Cloud Eureka 2.1.1 创建注册中心 2.1.2 创建客户端 2.2 Spring Cloud Consul 2.2.1 Consul 的优势 2.2.2 Consul的特性 2.2.3 安装Consul注册中心 2.2.4 创建服务提供者 3 总结 微服务系列前篇 详解微服务架构及其演进史 微服务全景架构全面瓦解 微服务架构拆分策略详解 微服务架构之服务注册与发现功能详解

随机推荐