目次
一般的に使用される分散 ID ソリューション
UUID
Snowflake
Snowflake アルゴリズムの Java コード:
Leaf
Leaf アルゴリズムの Java コード:
データベース自動インクリメント ID に基づいて生成
UUID に基づいて生成
Generate Redis に基づいて
ZooKeeper に基づいて生成
ホームページ データベース Redis Redis 分散 ID のソリューションは何ですか?

Redis 分散 ID のソリューションは何ですか?

Jun 03, 2023 am 10:14 AM
redis id

    一般的に使用される分散 ID ソリューション

    分散システムでは、グローバルに一意の ID を生成することが非常に重要です。分散システムでは複数のノードが存在するためです。同時に ID を生成すると、ID の競合が発生する可能性があります。

    次に、一般的に使用される分散 ID ソリューションをいくつか紹介します。

    UUID

    UUID (Universally Unique Identifier) は 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());
        }
    }
    ログイン後にコピー

    Java 独自の UUID クラスによって生成された UUID は非常にシンプルで使いやすく、追加の構成や管理を必要としません。 。 UUID はその長さ (128 ビット) のため、データベース テーブルの主キーとしては適しておらず、並べ替えやインデックス付けが困難です。

    Snowflake

    Snowflake は、Twitter がオープンソース化した分散 ID 生成アルゴリズムで、タイムスタンプ、データセンター ID、マシン ID などの情報を含む 64 ビットの一意の 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&#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();
        }
    }
    ログイン後にコピー

    Snowflake アルゴリズムの利点は、ID 生成のパフォーマンスが高く、ID の長さが短いことです (64 bits) をデータベース テーブルの主キーとして使用すると、並べ替えとインデックス付けが容易になります。ただし、クラスター内のノードの数がマシン ID が占める桁数を超える場合、またはクラスターが非常に大きくタイムスタンプの桁数が十分でない場合は、他の分散 ID 生成アルゴリズムを使用する必要があることに注意してください。考慮されます。

    Leaf

    Leaf は、Meituan Dianping によってオープンソース化された分散 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 アルゴリズムは、Snowflake アルゴリズムよりも ID の生成がわずかに遅くなりますが、より多くのワーカーをサポートできます。ノード 。 Leaf アルゴリズムで生成される ID は、タイムスタンプ、ワーカー ID、シーケンス番号の 3 つの部分で構成され、タイムスタンプが 42 ビット、ワーカー ID が 10 ビット、シーケンス番号が 12 ビットの合計 64 ビットを占めます。

    上記は一般的な分散 ID 生成アルゴリズムですが、もちろん、MongoDB ID、UUID、Twitter Snowflake など、他のソリューションもあります。ビジネスシーンに応じて適したソリューションが異なり、具体的な実装内容やパフォーマンスも異なるため、実際の状況に応じて適切なソリューションを選択する必要があります。

    上記で紹介した分散 ID 生成アルゴリズムに加えて、Flicker の分散 ID 生成アルゴリズムなど、新しい分散 ID 生成ソリューションもいくつか登場しています。これは、Snowflake と同様のアイデアを使用しますが、異なるビット割り当て方法を採用しています。 Snowflake よりも柔軟性があり、各部分が占めるビット数は必要に応じて動的に調整できます。さらに、Facebook は ID 生成サービス (IGS) ソリューションも開始しました。これは ID の生成と保存を分離し、より柔軟でスケーラブルなソリューションを提供しますが、より複雑なアーキテクチャの設計と実装が必要になります。

    さまざまなビジネス ニーズに応じて、複数セットの分散 ID 生成ソリューションを設計できます。私の個人的な提案をいくつか紹介します。

    • データベース自動インクリメント ID に基づいて生成する: データベース自動インクリメント ID をグローバルに一意な ID として使用すると、ID の一意性が確保され、簡単になります。ただし、同時実行性が高いとパフォーマンスのボトルネックが発生する可能性があります。したがって、同時実行性の高いシナリオで使用することはお勧めできません。

    • UUID に基づいて生成: グローバルに一意な ID として UUID を使用すると、ID の一意性を確保できますが、ID の長さが長く (128 ビット)、保管や保存に不便です。 ID が重複する可能性は非常に低いですが、0 ではありません。分散システムを使用する場合は、ID の長さと、保存および送信のコストを考慮する必要があることが推奨されます。

    • Redis に基づく生成: Redis のアトミック操作を使用すると、ID の一意性が保証され、ID 生成速度が非常に高速になるため、同時実行性の高いシナリオに適用できます。 Redis がクラッシュしたり、パフォーマンスが低下したりすると、ID 生成の効率と可用性に影響が出る可能性があることに注意してください。

    • ZooKeeper に基づく生成: ZooKeeper のシリアル番号ジェネレーターを使用すると、ID の一意性を確保でき、実装は比較的簡単ですが、追加の依存関係とリソースの導入が必要です。パフォーマンスの問題がボトルネックになっている可能性があります。

    ビジネス シナリオに合った分散 ID 生成ソリューションを選択するには、ID の一意性、生成速度、長さ、ストレージ コスト、拡張性、可用性などの複数の要素を総合的に考慮する必要があります。異なるソリューションを実装するには、実装の詳細やパフォーマンスも異なるため、実際の状況でのトレードオフと選択を考慮する必要があります。

    各ソリューションの詳細なコード デモを以下に示します。

    データベース自動インクリメント 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("-", "");
        }
    }
    ログイン後にコピー

    Generate 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 が固有であることを確認します。

    以上がRedis 分散 ID のソリューションは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

    リアルなヌード写真を作成する AI 搭載アプリ

    AI Clothes Remover

    AI Clothes Remover

    写真から衣服を削除するオンライン AI ツール。

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    AI Hentai Generator

    AI Hentai Generator

    AIヘンタイを無料で生成します。

    ホットツール

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    SublimeText3 中国語版

    SublimeText3 中国語版

    中国語版、とても使いやすい

    ゼンドスタジオ 13.0.1

    ゼンドスタジオ 13.0.1

    強力な PHP 統合開発環境

    ドリームウィーバー CS6

    ドリームウィーバー CS6

    ビジュアル Web 開発ツール

    SublimeText3 Mac版

    SublimeText3 Mac版

    神レベルのコード編集ソフト(SublimeText3)

    Redisクラスターモードの構築方法 Redisクラスターモードの構築方法 Apr 10, 2025 pm 10:15 PM

    Redisクラスターモードは、シャードを介してRedisインスタンスを複数のサーバーに展開し、スケーラビリティと可用性を向上させます。構造の手順は次のとおりです。異なるポートで奇妙なRedisインスタンスを作成します。 3つのセンチネルインスタンスを作成し、Redisインスタンスを監視し、フェールオーバーを監視します。 Sentinel構成ファイルを構成し、Redisインスタンス情報とフェールオーバー設定の監視を追加します。 Redisインスタンス構成ファイルを構成し、クラスターモードを有効にし、クラスター情報ファイルパスを指定します。各Redisインスタンスの情報を含むnodes.confファイルを作成します。クラスターを起動し、CREATEコマンドを実行してクラスターを作成し、レプリカの数を指定します。クラスターにログインしてクラスター情報コマンドを実行して、クラスターステータスを確認します。作る

    Redisコマンドの使用方法 Redisコマンドの使用方法 Apr 10, 2025 pm 08:45 PM

    Redis指令を使用するには、次の手順が必要です。Redisクライアントを開きます。コマンド(動詞キー値)を入力します。必要なパラメーターを提供します(指示ごとに異なります)。 Enterを押してコマンドを実行します。 Redisは、操作の結果を示す応答を返します(通常はOKまたは-ERR)。

    Redisのすべてのキーを表示する方法 Redisのすべてのキーを表示する方法 Apr 10, 2025 pm 07:15 PM

    Redisのすべてのキーを表示するには、3つの方法があります。キーコマンドを使用して、指定されたパターンに一致するすべてのキーを返します。スキャンコマンドを使用してキーを繰り返し、キーのセットを返します。情報コマンドを使用して、キーの総数を取得します。

    単一のスレッドレディスの使用方法 単一のスレッドレディスの使用方法 Apr 10, 2025 pm 07:12 PM

    Redisは、単一のスレッドアーキテクチャを使用して、高性能、シンプルさ、一貫性を提供します。 I/Oマルチプレックス、イベントループ、ノンブロッキングI/O、共有メモリを使用して同時性を向上させますが、並行性の制限、単一の障害、および書き込み集約型のワークロードには適していません。

    Redisでサーバーを開始する方法 Redisでサーバーを開始する方法 Apr 10, 2025 pm 08:12 PM

    Redisサーバーを起動する手順には、以下が含まれます。オペレーティングシステムに従ってRedisをインストールします。 Redis-Server(Linux/Macos)またはRedis-Server.exe(Windows)を介してRedisサービスを開始します。 Redis-Cli ping(Linux/macos)またはRedis-Cli.exePing(Windows)コマンドを使用して、サービスステータスを確認します。 Redis-Cli、Python、node.jsなどのRedisクライアントを使用して、サーバーにアクセスします。

    Redisロックの使用方法 Redisロックの使用方法 Apr 10, 2025 pm 08:39 PM

    Redisを使用して操作をロックするには、setnxコマンドを介してロックを取得し、有効期限を設定するために有効期限コマンドを使用する必要があります。特定の手順は次のとおりです。(1)SETNXコマンドを使用して、キー価値ペアを設定しようとします。 (2)expireコマンドを使用して、ロックの有効期限を設定します。 (3)Delコマンドを使用して、ロックが不要になったときにロックを削除します。

    Redisデータをクリアする方法 Redisデータをクリアする方法 Apr 10, 2025 pm 10:06 PM

    Redisデータをクリアする方法:Flushallコマンドを使用して、すべての重要な値をクリアします。 FlushDBコマンドを使用して、現在選択されているデータベースのキー値をクリアします。 [選択]を使用してデータベースを切り替え、FlushDBを使用して複数のデータベースをクリアします。 DELコマンドを使用して、特定のキーを削除します。 Redis-CLIツールを使用してデータをクリアします。

    Redis-Serverが見つからない場合はどうすればよいですか Redis-Serverが見つからない場合はどうすればよいですか Apr 10, 2025 pm 06:54 PM

    Redis-Serverが見つからない問題を解決するための手順:インストールを確認して、Redisが正しくインストールされていることを確認します。環境変数Redis_hostとredis_portを設定します。 Redis Server Redis-Serverを起動します。サーバーがRedis-Cli pingを実行しているかどうかを確認します。

    See all articles