开发者

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

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

          常用的分布式ID解决方案

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

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

          redis分布式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(phpLeafGenerator.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 = (php1 << 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 bapythonckwards. 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();
                    开发者_mariadb  }
                  }
                  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

          上一篇:

          下一篇:

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          最新数据库

          数据库排行榜