redis分布式ID解决方法有哪些

其他教程   发布日期:2023年08月04日   浏览次数:375

这篇文章主要介绍“redis分布式ID解决方法有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“redis分布式ID解决方法有哪些”文章能帮助大家解决问题。

    常用的分布式ID解决方案

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

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

    UUID

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

    1. javaCopy code
    2. import java.util.UUID;
    3. public class UuidGenerator {
    4. public static void main(String[] args) {
    5. UUID uuid = UUID.randomUUID();
    6. System.out.println(uuid.toString());
    7. }
    8. }

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

    Snowflake

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

    Snowflake算法的Java代码:

    1. javaCopy code
    2. public class SnowflakeGenerator {
    3. private final static long START_STMP = 1480166465631L;
    4. private final static long SEQUENCE_BIT = 12;
    5. private final static long MACHINE_BIT = 5;
    6. private final static long DATACENTER_BIT = 5;
    7. private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
    8. private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
    9. private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
    10. private final static long MACHINE_LEFT = SEQUENCE_BIT;
    11. private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    12. private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
    13. private long datacenterId;
    14. private long machineId;
    15. private long sequence = 0L;
    16. private long lastStmp = -1L;
    17. public SnowflakeGenerator(long datacenterId, long machineId) {
    18. if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
    19. throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
    20. }
    21. if (machineId > MAX_MACHINE_NUM || machineId < 0) {
    22. throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
    23. }
    24. this.datacenterId = datacenterId;
    25. this.machineId = machineId;
    26. }
    27. public synchronized long nextId() {
    28. long currStmp = getNewstmp();
    29. if (currStmp < lastStmp) {
    30. throw new RuntimeException("Clock moved backwards. Refusing to generate id");
    31. }
    32. if (currStmp == lastStmp) {
    33. sequence = (sequence + 1) & MAX_SEQUENCE;
    34. if (sequence == 0L) {
    35. currStmp = getNextMill();
    36. }
    37. } else {
    38. sequence = 0L;
    39. }
    40. lastStmp = currStmp;
    41. return (currStmp - START_STMP) << TIMESTMP_LEFT
    42. | datacenterId << DATACENTER_LEFT
    43. | machineId << MACHINE_LEFT
    44. | sequence;
    45. }
    46. private long getNextMill() {
    47. long mill = getNewstmp();
    48. while (mill <= lastStmp) {
    49. mill = getNewstmp();
    50. }
    51. return mill;
    52. }
    53. private long getNewstmp() {
    54. return System.currentTimeMillis();
    55. }
    56. }

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

    Leaf

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

    Leaf算法的Java代码:

    1. javaCopy code
    2. public class LeafGenerator {
    3. private static final Logger logger = LoggerFactory.getLogger(LeafGenerator.class);
    4. private static final String WORKER_ID_KEY = "leaf.worker.id";
    5. private static final String PORT_KEY = "leaf.port";
    6. private static final int DEFAULT_PORT = 8080;
    7. private static final int DEFAULT_WORKER_ID = 0;
    8. private static final int WORKER_ID_BITS = 10;
    9. private static final int SEQUENCE_BITS = 12;
    10. private static final int MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1;
    11. private static final int MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1;
    12. private static final long EPOCH = 1514736000000L;
    13. private final SnowflakeIdWorker idWorker;
    14. public LeafGenerator() {
    15. int workerId = SystemPropertyUtil.getInt(WORKER_ID_KEY, DEFAULT_WORKER_ID);
    16. int port = SystemPropertyUtil.getInt(PORT_KEY, DEFAULT_PORT);
    17. this.idWorker = new SnowflakeIdWorker(workerId, port);
    18. logger.info("Initialized LeafGenerator with workerId={}, port={}", workerId, port);
    19. }
    20. public long nextId() {
    21. return idWorker.nextId();
    22. }
    23. private static class SnowflakeIdWorker {
    24. private final long workerId;
    25. private final long port;
    26. private long sequence = 0L;
    27. private long lastTimestamp = -1L;
    28. SnowflakeIdWorker(long workerId, long port) {
    29. if (workerId < 0 || workerId > MAX_WORKER_ID) {
    30. throw new IllegalArgumentException(String.format("workerId must be between %d and %d", 0, MAX_WORKER_ID));
    31. }
    32. this.workerId = workerId;
    33. this.port = port;
    34. }
    35. synchronized long nextId() {
    36. long timestamp = System.currentTimeMillis();
    37. if (timestamp < lastTimestamp) {
    38. throw new RuntimeException("Clock moved backwards. Refusing to generate id");
    39. }
    40. if (timestamp == lastTimestamp) {
    41. sequence = (sequence + 1) & MAX_SEQUENCE;
    42. if (sequence == 0L) {
    43. timestamp = tilNextMillis(lastTimestamp);
    44. }
    45. } else {
    46. sequence = 0L;
    47. }
    48. lastTimestamp = timestamp;
    49. return ((timestamp - EPOCH) << (WORKER_ID_BITS + SEQUENCE_BITS))
    50. | (workerId << SEQUENCE_BITS)
    51. | sequence;
    52. }
    53. private long tilNextMillis(long lastTimestamp) {
    54. long timestamp = System.currentTimeMillis();
    55. while (timestamp <= lastTimestamp) {
    56. timestamp = System.currentTimeMillis();
    57. }
    58. return timestamp;
    59. }
    60. }
    61. }

    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生成

    1. javaCopy code
    2. public class IdGenerator {
    3. private static final String JDBC_URL = "jdbc:mysql://localhost:3306/test";
    4. private static final String JDBC_USER = "root";
    5. private static final String JDBC_PASSWORD = "password";
    6. public long generateId() {
    7. Connection conn = null;
    8. PreparedStatement pstmt = null;
    9. ResultSet rs = null;
    10. try {
    11. Class.forName("com.mysql.jdbc.Driver");
    12. conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
    13. pstmt = conn.prepareStatement("INSERT INTO id_generator (stub) VALUES (null)", Statement.RETURN_GENERATED_KEYS);
    14. pstmt.executeUpdate();
    15. rs = pstmt.getGeneratedKeys();
    16. if (rs.next()) {
    17. return rs.getLong(1);
    18. }
    19. } catch (Exception e) {
    20. e.printStackTrace();
    21. } finally {
    22. try {
    23. if (rs != null) {
    24. rs.close();
    25. }
    26. if (pstmt != null) {
    27. pstmt.close();
    28. }
    29. if (conn != null) {
    30. conn.close();
    31. }
    32. } catch (Exception e) {
    33. e.printStackTrace();
    34. }
    35. }
    36. return 0L;
    37. }
    38. }

    基于UUID生成

    1. javaCopy code
    2. import java.util.UUID;
    3. public class IdGenerator {
    4. public String generateId() {
    5. return UUID.randomUUID().toString().replace("-", "");
    6. }
    7. }

    基于Redis生成

    1. javaCopy code
    2. import redis.clients.jedis.Jedis;
    3. public class IdGenerator {
    4. private static final String REDIS_HOST = "localhost";
    5. private static final int REDIS_PORT = 6379;
    6. private static final String REDIS_PASSWORD = "password";
    7. private static final int ID_GENERATOR_EXPIRE_SECONDS = 3600;
    8. private static final String ID_GENERATOR_KEY = "id_generator";
    9. public long generateId() {
    10. Jedis jedis = null;
    11. try {
    12. jedis = new Jedis(REDIS_HOST, REDIS_PORT);
    13. jedis.auth(REDIS_PASSWORD);
    14. long id = jedis.incr(ID_GENERATOR_KEY);
    15. jedis.expire(ID_GENERATOR_KEY, ID_GENERATOR_EXPIRE_SECONDS);
    16. return id;
    17. } catch (Exception e) {
    18. e.printStackTrace();
    19. } finally {
    20. if (jedis != null) {
    21. jedis.close();
    22. }
    23. }
    24. return 0L;
    25. }
    26. }

    基于ZooKeeper生成

    1. javaCopy code
    2. import java.util.concurrent.CountDownLatch;
    3. import org.apache.zookeeper.CreateMode;
    4. import org.apache.zookeeper.WatchedEvent;
    5. import org.apache.zookeeper.Watcher;
    6. import org.apache.zookeeper.ZooDefs.Ids;
    7. import org.apache.zookeeper.ZooKeeper;
    8. public class IdGenerator implements Watcher {
    9. private static final String ZK_HOST = "localhost";
    10. private static final int ZK_PORT = 2181;
    11. private static final int SESSION_TIMEOUT = 5000;
    12. private static final String ID_GENERATOR_NODE = "/id_generator";
    13. private static final int ID_GENERATOR_EXPIRE_SECONDS = 3600;
    14. private long workerId = 0;
    15. public IdGenerator() {
    16. try {
    17. ZooKeeper zk = new ZooKeeper(ZK_HOST + ":" + ZK_PORT, SESSION_TIMEOUT, this);
    18. CountDownLatch latch = new CountDownLatch(1);
    19. latch.await();
    20. if (zk.exists(ID_GENERATOR_NODE, false) == null) {
    21. zk.create(ID_GENERATOR_NODE, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    22. }
    23. workerId = zk.getChildren(ID_GENERATOR_NODE, false).size();
    24. zk.create(ID_GENERATOR_NODE + "/worker_" + workerId, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
    25. } catch (Exception e) {
    26. e.printStackTrace();
    27. }
    28. }
    29. public long generateId() {
    30. ZooKeeper zk = null;
    31. try {
    32. zk = new ZooKeeper(ZK_HOST + ":" + ZK_PORT, SESSION_TIMEOUT, null);
    33. CountDownLatch latch = new CountDownLatch(1);
    34. latch.await();
    35. zk.create(ID_GENERATOR_NODE + "/id_", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> {}, null);
    36. byte[] data = zk.getData(ID_GENERATOR_NODE + "/worker_" + workerId, false, null);
    37. long id = Long.parseLong(new String(data)) * 10000 + zk.getChildren(ID_GENERATOR_NODE, false).size();
    38. return id;
    39. } catch (Exception e) {
    40. e.printStackTrace();
    41. } finally {
    42. if (zk != null) {
    43. try {
    44. zk.close();
    45. } catch (Exception e) {
    46. e.printStackTrace();
    47. }
    48. }
    49. }
    50. return 0L;
    51. }
    52. @Override
    53. public void process(WatchedEvent event) {
    54. if (event.getState() == Event.KeeperState.SyncConnected) {
    55. System.out.println("Connected to ZooKeeper");
    56. CountDownLatch latch = new CountDownLatch(1);
    57. latch.countDown();
    58. }
    59. }
    60. }

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

    以上就是redis分布式ID解决方法有哪些的详细内容,更多关于redis分布式ID解决方法有哪些的资料请关注九品源码其它相关文章!