一意のシステム ID は、システムを設計するときによく遭遇する問題であり、よくこの問題に悩まされます。 ID を生成するには、さまざまなシナリオ、ニーズ、パフォーマンス要件に適応するさまざまな方法があります。したがって、一部のより複雑なシステムには複数の ID 生成戦略があります。ここでは、一般的な ID 生成戦略をいくつか示します。
1. データベースの自己増加シーケンスまたはフィールド
最も一般的な方法。データベースを使用すると、データベース全体が固有になります。
利点:
シンプルで便利なコードと許容可能なパフォーマンス。
#単一データベース、読み取り/書き込み分離、または 1 つのマスターと複数のスレーブの場合、生成できるマスター データベースは 1 つだけです。単一障害点が発生するリスクがあります。
最適化計画:
これはデータベースまたはプログラムを使用して生成でき、一般に世界で唯一のものです。
利点:
#大量のデータを転送する
## はデータベースに依存せず、柔軟で便利で、データベースよりもパフォーマンスが優れています。
#コーディングと構成に必要な作業負荷は比較的大きくなります。
4. Twitter のスノーフレーク アルゴリズム
Snowflake は Twitter のオープンソースの分散 ID 生成アルゴリズムであり、結果は長い ID になります。中心となるアイデアは、ミリ秒数として 41 ビット、マシン ID として 10 ビット (5 ビットがデータセンター、5 ビットがマシン ID)、ミリ秒以内のシリアル番号として 12 ビット (つまり、各ノードが4096 ID を生成します)、最後には符号ビットがあり、常に 0 になります。特定の実装コードは、https://github.com/twitter/snowflake
で見つけることができます。public class IdWorker { // ==============================Fields=========================================== /** 开始时间截 (2015-01-01) */ private final long twepoch = 1420041600000L; /** 机器id所占的位数 */ private final long workerIdBits = 5L; /** 数据标识id所占的位数 */ private final long datacenterIdBits = 5L; /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /** 支持的最大数据标识id,结果是31 */ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /** 序列在id中占的位数 */ private final long sequenceBits = 12L; /** 机器ID向左移12位 */ private final long workerIdShift = sequenceBits; /** 数据标识id向左移17位(12+5) */ private final long datacenterIdShift = sequenceBits + workerIdBits; /** 时间截向左移22位(5+5+12) */ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** 工作机器ID(0~31) */ private long workerId; /** 数据中心ID(0~31) */ private long datacenterId; /** 毫秒内序列(0~4095) */ private long sequence = 0L; /** 上次生成ID的时间截 */ private long lastTimestamp = -1L; //==============================Constructors===================================== /** * 构造函数 * @param workerId 工作ID (0~31) * @param datacenterId 数据中心ID (0~31) */ public IdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } // ==============================Methods========================================== /** * 获得下一个ID (该方法是线程安全的) * @return SnowflakeId */ public synchronized long nextId() { long timestamp = timeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (timestamp < lastTimestamp) { throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //如果是同一时间生成的,则进行毫秒内序列 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; //毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } } //时间戳改变,毫秒内序列重置 else { sequence = 0L; } //上次生成ID的时间截 lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID return ((timestamp - twepoch) << timestampLeftShift) // | (datacenterId << datacenterIdShift) // | (workerId << workerIdShift) // | sequence; } /** * 阻塞到下一个毫秒,直到获得新的时间戳 * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒为单位的当前时间 * @return 当前时间(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } //==============================Test============================================= /** 测试 */ public static void main(String[] args) { IdWorker idWorker = new IdWorker(0, 0); for (int i = 0; i < 1000; i++) { long id = idWorker.nextId(); System.out.println(Long.toBinaryString(id)); System.out.println(id); } }}
スノーフレーク アルゴリズムは、独自のプロジェクトのニーズに応じて変更できます。たとえば、将来のデータ センターの数、各データ センターのマシンの数、統一ミリ秒内に発生する可能性のある同時実行数を推定して、アルゴリズムに必要なビット数を調整します。
利点:
#ID は、単一マシン上で時間に応じて増加します。
は単一マシン上で増分ですが、分散環境が関与するため、各マシンクロック上のクロックは完全に同期することができず、場合によってはグローバルな増分が達成されない状況が発生する可能性があります。
zookeeper は、主に znode データ バージョンを通じてシリアル番号を生成します。32 ビットおよび 64 ビットのデータ バージョン番号を生成できます。顧客 顧客は、このバージョン番号を一意のシリアル番号として使用できます。
Zookeeper が固有 ID の生成に使用されることはほとんどありません。主な理由は、zookeeper に依存し、複数のステップで API を呼び出すためです。競合が大きい場合は、分散ロックの使用を検討する必要があります。したがって、同時実行性の高い分散環境では、パフォーマンスは理想的ではありません。
6. MongoDB の ObjectId
MongoDB の ObjectId は、スノーフレーク アルゴリズムに似ています。これは軽量になるように設計されており、同じ世界的に独自の方法を使用して、さまざまなマシンで簡単に生成できます。 MongoDB は最初から分散データベースとして設計されており、複数のノードを処理することが中心的な要件です。シャーディング環境での生成がはるかに簡単になります。形式は次のとおりです: [src/main/resources/objectId.png] ここに画像の説明を記述します:
最初の 4 バイトは、標準エポックから始まるタイムスタンプです。 、単位は秒です。タイムスタンプは、次の 5 バイトと組み合わされて、第 2 レベルの一意性を提供します。タイムスタンプが最初に来るため、ObjectId はおおよそ挿入された順序で並べ替えられることを意味します。効率化の指標として利用するなどに便利です。これらの 4 バイトは、文書が作成された時刻も意味します。ほとんどのクライアント ライブラリは、ObjectId からこの情報を取得するメソッドを公開します。次の 3 バイトはホストの一意の識別子です。通常はマシンのホスト名のハッシュです。これにより、異なるホストが競合することなく異なる ObjectId を生成することが保証されます。同じマシン上の複数の同時プロセスによって生成される ObjectId が一意であることを確認するために、次の 2 バイトは ObjectId を生成したプロセス識別子 (PID) から取得されます。最初の 9 バイトにより、同じ秒内に異なるマシン上の異なるプロセスによって生成された ObjectId が一意であることが保証されます。最後の 3 バイトは、同じ秒内に同じプロセスによって生成された ObjectId も異なることを保証するために、自動的に増加するカウンターです。各プロセスは、同じ秒内に最大 2563 (16 777 216) 個の異なる ObjectId を持つことができます。
関連する推奨事項:
php ニュース リリース管理システム開発例PHP 開発の簡単なニュース リリース システム チュートリアル以上が分散システム向けの固有 ID 生成ソリューションの概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。