Heim  >  Artikel  >  Datenbank  >  Welche Lösungen gibt es für die verteilte Redis-ID?

Welche Lösungen gibt es für die verteilte Redis-ID?

王林
王林nach vorne
2023-06-03 10:14:271537Durchsuche

    Häufig verwendete verteilte ID-Lösungen

    In einem verteilten System ist es sehr wichtig, eine weltweit eindeutige ID zu generieren In einem verteilten System kann es zu ID-Konflikten kommen, wenn mehrere Knoten gleichzeitig IDs generieren.

    Im Folgenden werden einige häufig verwendete verteilte ID-Lösungen vorgestellt.

    UUID

    UUID (Universally Unique Identifier) ​​​​ist eine aus 128 Ziffern bestehende Kennung, die weltweit Eindeutigkeit garantieren kann, da ihr Generierungsalgorithmus auf Zeitstempel, Knoten-ID und basiert andere Faktoren. UUID kann mit Javas eigener UUID-Klasse generiert werden, wie unten gezeigt:

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

    Die von Javas eigener UUID-Klasse generierte UUID ist sehr einfach und benutzerfreundlich und erfordert keine zusätzliche Konfiguration und Verwaltung sein Vorteil. Aufgrund seiner Länge (128 Bit) eignet sich UUID nicht als Primärschlüssel für Datenbanktabellen und ist schwer zu sortieren und zu indizieren.

    Snowflake

    Snowflake ist ein von Twitter offener, verteilter ID-Generierungsalgorithmus. Er kann eine eindeutige 64-Bit-ID generieren, die Zeitstempel, Rechenzentrums-ID und Maschinen-ID enthält andere Informationen. Der Java-Code des Snowflake-Algorithmus lautet wie folgt:

    Der Java-Code des Snowflake-Algorithmus:
    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&#39;t be greater than MAX_DATACENTER_NUM or less than 0");
            }
            if (machineId > MAX_MACHINE_NUM || machineId < 0) {
                throw new IllegalArgumentException("machineId can&#39;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();
        }
    }

    Die Vorteile des Snowflake-Algorithmus sind hohe Leistung bei der Generierung von IDs und kurze Die ID-Länge (64 Bit) kann als Primärschlüssel der Datenbanktabelle verwendet werden und eignet sich zum Sortieren und Indizieren. Es ist jedoch zu beachten, dass andere verteilte Algorithmen zur ID-Generierung erforderlich sind, wenn die Anzahl der Knoten im Cluster die Anzahl der von der Maschinen-ID belegten Ziffern übersteigt oder der Cluster sehr groß ist und die Anzahl der Zeitstempelziffern nicht ausreicht berücksichtigt werden.

    Leaf

    Leaf ist ein verteilter ID-Generierungsalgorithmus von Meituan Dianping, der eine weltweit eindeutige 64-Bit-ID generieren kann. Der Java-Code des Leaf-Algorithmus lautet wie folgt:

    Der Java-Code des Leaf-Algorithmus:
    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;
            }
        }
    }

    Obwohl der Leaf-Algorithmus IDs etwas langsamer generiert als der Snowflake-Algorithmus, ist er Kann mehr Arbeitsknoten unterstützen. Die vom Leaf-Algorithmus generierte ID besteht aus drei Teilen, nämlich Zeitstempel, Worker-ID und Seriennummer. Der Zeitstempel belegt 42 Bit, die Worker-ID belegt 10 Bit und die Seriennummer belegt 12 Bit, also insgesamt 64 Bit.

    Die oben genannten sind gängige verteilte ID-Generierungsalgorithmen. Natürlich gibt es auch andere Lösungen, wie zum Beispiel: MongoDB ID, UUID, Twitter Snowflake usw. Unterschiedliche Lösungen eignen sich für unterschiedliche Geschäftsszenarien, und auch die spezifischen Implementierungsdetails und die Leistung sind unterschiedlich. Sie müssen die geeignete Lösung basierend auf der tatsächlichen Situation auswählen.

    Zusätzlich zu dem oben vorgestellten verteilten ID-Generierungsalgorithmus gibt es auch einige neue Lösungen für die verteilte ID-Generierung, beispielsweise den verteilten ID-Generierungsalgorithmus von Flicker, der ähnliche Ideen wie Snowflake verwendet, jedoch unterschiedliche Bitzuteilungsmethoden übernommen, was flexibler als Snowflake ist und die Anzahl der von jedem Teil belegten Bits je nach Bedarf dynamisch angepasst werden kann. Darüber hinaus hat Facebook auch die ID Generation Service (IGS)-Lösung eingeführt, die ID-Generierung und -Speicherung trennt und eine flexiblere und skalierbarere Lösung bietet, jedoch ein komplexeres Architekturdesign und eine komplexere Implementierung erfordert.

    Je nach unterschiedlichen Geschäftsanforderungen können mehrere Sätze verteilter Lösungen zur ID-Generierung entworfen werden. Hier sind einige meiner persönlichen Vorschläge:

    • Basierend auf der automatischen Inkrementierungs-ID-Generierung der Datenbank: Die Verwendung der automatischen Inkrementierungs-ID der Datenbank als global eindeutige ID kann die Einzigartigkeit von sicherstellen Die ID ist einfach zu implementieren, kann jedoch bei hoher Parallelität zu Leistungsengpässen führen. Daher wird die Verwendung in Szenarien mit hoher Parallelität nicht empfohlen.

    • Basierend auf der UUID-Generierung: Die Verwendung von UUID als global eindeutige ID kann die Eindeutigkeit der ID sicherstellen, aber die ID-Länge ist lang (128 Bit) und unpraktisch Speicherung und Übertragung, und die Wahrscheinlichkeit doppelter IDs ist sehr gering, aber nicht 0. Es wird empfohlen, bei Verwendung eines verteilten Systems die ID-Länge sowie die Kosten für Speicherung und Übertragung zu berücksichtigen.

    • Basierend auf der Redis-Generierung: Mit der atomaren Operation von Redis kann die Eindeutigkeit der ID garantiert werden, und die Geschwindigkeit der ID-Generierung ist sehr schnell, was geeignet sein kann für Szenarien mit hoher Parallelität. Es ist zu beachten, dass ein Absturz oder eine schlechte Leistung von Redis die Effizienz und Verfügbarkeit der ID-Generierung beeinträchtigen kann.

    • Generiert basierend auf ZooKeeper: Die Verwendung des Seriennummerngenerators von ZooKeeper kann die Eindeutigkeit der ID sicherstellen, und die Implementierung ist relativ einfach, erfordert jedoch die Einführung zusätzlicher Abhängigkeiten und Ressourcen und es kann zu Leistungsengpässen kommen.

    Um eine verteilte ID-Generierungslösung auszuwählen, die zu Ihrem Geschäftsszenario passt, müssen Sie die Einzigartigkeit der ID, Generierungsgeschwindigkeit, Länge, Speicherkosten, Skalierbarkeit usw. umfassend berücksichtigen. Verfügbarkeit usw. Mehrere Faktoren. Die Implementierung verschiedener Lösungen erfordert die Berücksichtigung von Kompromissen und Auswahlmöglichkeiten in tatsächlichen Situationen, da auch deren Implementierungsdetails und Leistung unterschiedlich sind.

    Die detaillierte Code-Demo jeder Lösung ist unten angegeben:

    Generieren Sie basierend auf der automatischen Inkrementierungs-ID der Datenbank

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

    Generieren Sie basierend auf der UUID#🎜 🎜#
    javaCopy code
    import java.util.UUID;
    public class IdGenerator {
        public String generateId() {
            return UUID.randomUUID().toString().replace("-", "");
        }
    }

    Generiert basierend auf 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;
        }
    }

    Generiert basierend auf 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();
            }
        }
    }

    Beachten Sie, dass die temporären Knoten von ZooKeeper hier verwendet werden, um verschiedene Arbeitsknoten zu koordinieren Wenn der Knoten auflegt, werden auch seine temporären Knoten gelöscht, wodurch sichergestellt wird, dass die von jedem Arbeitsknoten erhaltene ID eindeutig ist.

    Das obige ist der detaillierte Inhalt vonWelche Lösungen gibt es für die verteilte Redis-ID?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen