7 つの分散型グローバル ID 生成戦略のうち、どれがお好みですか?

7 つの分散型グローバル ID 生成戦略のうち、どれがお好みですか?

マイクロサービスを使用することで、グローバル ID の問題など、もともと単純だった多くの問題が複雑になってしまいました。

最近、たまたま仕事でこのコンテンツを使用したので、市場で一般的なグローバル ID 生成戦略をいくつか調査し、参考までに簡単に比較してみました。

データベースがさまざまなライブラリとテーブルに分割されると、元の主キーの自動増分方法は使いにくくなり、新しい適切なソリューションを見つける必要があります。宋歌の要求はこのような状況下で提起された。

次は一緒に見ていきましょう。

1. 2つのアプローチ

一般的に言えば、この問題には 2 つの異なるアプローチがあります。

  • データベースに任せましょう
  • 主キーを処理し、それをデータベースに直接挿入する Java コード。

これら 2 つのアイデアは異なる解決策に対応します。一つずつ見ていきましょう。

2. データベースは自動的に作成される

データベースはそれを自動的に処理します。つまり、データを挿入するときに、主キーの問題を考慮せず、データベースの主キーの自動増分機能を引き続き使用したいと考えています。しかし、元のデフォルトの主キーの自動増分機能は今では使用できないことは明らかであり、新しい解決策が必要です。

2.1 データベース構成の変更

分割後のデータベースの構造は次のようになります (データベース ミドルウェアとして MyCat が使用されていると仮定)。

写真

このとき、元の db1、db2、db3 の主キーが増加し続けると、MyCat では主キーが自己増加せず、主キーが重複し、ユーザーが MyCat からクエリしたデータの主キーに問題が生じます。

問題の原因を見つければ、残りは簡単に解決できます。

MySQL データベースの主キーの自動増分の開始値とステップ サイズを直接変更できます。

まず、次の SQL を使用して 2 つの関連する変数の値を表示できます。

 SHOW VARIABLES LIKE 'auto_increment%'

写真

主キーの自動増分の開始値とステップ サイズはどちらも 1 であることがわかります。

開始値は簡単に変更できます。テーブルを定義するときに設定できます。ステップ サイズは、次の構成を変更することで実現できます。

 set @@auto_increment_increment=9;

変更後、対応する変数値を確認し、変更されていることを確認します。

写真

ここで、再度データを挿入すると、主キーは毎回 1 ずつではなく、毎回 9 ずつ増加します。

自動増分開始値に関しては、実は非常に簡単に設定できます。テーブル作成時に設定できます。

 create table test01(id integer PRIMARY KEY auto_increment,username varchar(255)) auto_increment=8;

MySQL では自動増分の開始値と各増加のステップ サイズを変更できるため、db1、db2、db3 があると仮定すると、これら 3 つのデータベースのテーブルの自動増分の開始値をそれぞれ 1、2、3 に設定し、自動増分のステップ サイズを 3 に設定して、自動増分を実現できます。

しかし、この方法は明らかにエレガントではなく、扱いが面倒で将来の拡張にも不便なので、お勧めできません。

2.2 MySQL+MyCat+ZooKeeper

データベースおよびテーブル シャーディング ツールとして MyCat を使用している場合は、それを Zookeeper と組み合わせることで、主キーのグローバル自動増分も実現できます。

MyCat は分散データベース ミドルウェアとして、データベース クラスターの操作をシールドし、スタンドアロン データベースと同じようにデータベース クラスターを操作できるようにします。主キーの自動増分には独自のソリューションがあります。

  1. ローカルファイル経由で実装
  2. データベースを通じて実装
  3. ローカルタイムスタンプで実装
  4. 分散型ZK IDジェネレーターを通じて実装
  5. ZK増分アプローチで実装

ここでは主にソリューション 4 について見ていきます。

設定手順は次のとおりです。

  • まず、主キーの自動増分モードを 4 に変更します。4 は、Zookeeper を使用して主キーの自動増分を実装することを意味します。

サーバー.xml

写真

  • テーブルを自動的に増分するように設定し、主キーを設定します。

スキーマ.xml

写真

主キーを自動インクリメントに設定し、主キーを id に設定します。

  • Zookeeper情報を設定する

myid.properties で Zookeeper 情報を設定します。

写真

  • 増分するテーブルを設定する

シーケンスconf.プロパティ

写真

テーブル名は大文字にする必要があることに注意してください。

  1. TABLE.MINID スレッドの現在の間隔の最小値
  2. TABLE.MAXID スレッドの現在の間隔の最大値
  3. TABLE.CURID スレッドの現在の間隔における現在の値
  4. ファイル構成の MAXID と MINID によって、取得されるたびに間隔が決まります。これは各スレッドまたはプロセスに対して有効です。
  5. ファイル内の 3 つのプロパティ構成は、最初のプロセスの最初のスレッドに対してのみ有効です。他のスレッドとプロセスはZKを動的に読み取ります
  • MyCatテストを再開する

最後に、MyCat を再起動し、以前に作成したテーブルを削除してから、テスト用に新しいテーブルを作成します。

この方法はより便利で、スケーラビリティも優れています。データベースおよびテーブル シャーディング ツールとして MyCat を選択した場合、これが最適なソリューションです。

上記で紹介した 2 つの方法はどちらも、データベースまたはデータベース ミドルウェア レベルで主キーの自動増分を処理するため、Java コードで追加の作業は必要ありません。

次に、Java コードで処理する必要があるいくつかのソリューションを見てみましょう。

3. Javaコード処理

3.1 UUID

最も簡単に考えられるのは、UUID (Universally Unique Identifier) です。 UUID の標準形式には、ハイフンで 5 つのセグメントに分割された 32 桁の 16 進数 (8-4-4-4-12、36 文字) が含まれます。これは Java が組み込まれており、使いやすいです。最大の利点は、ローカルで生成され、ネットワークが消費されないことです。しかし、企業内で開発を行うパートナーであれば誰でも、これが企業のプロジェクトではあまり使用されていないことを知っています。理由は次のとおりです。

  1. 文字列が長すぎるため、MySQL でインデックスできません。
  2. UUID のランダム性は、I/O を集中的に使用するアプリケーションにとって非常に不親切です。 「クラスター化インデックスへの挿入は完全にランダムになり、データのクラスタリングが排除されます。」
  3. 情報セキュリティの欠如: MAC アドレスに基づいて UUID を生成するアルゴリズムにより、MAC アドレスが漏洩する可能性があります。この脆弱性は、Melissa ウイルスの作成者の所在を特定するために使用されました。

したがって、UUID は最適なソリューションではありません。

3.2 スノーフレーク

Snowflake アルゴリズムは、Twitter が公開した分散主キー生成アルゴリズムです。異なるプロセスの主キーの非重複性と、同じプロセスの主キーの秩序性を保証できます。同じ処理では、まず時間ビットによって重複しないことが保証され、さらに時間が同じであればシーケンスビットによって保証されます。

同時に、時間ビットは単調に増加し、サーバーが時間的にほぼ同期されている場合、生成された主キーは分散環境で一般的に順序付けられていると見なすことができ、インデックス フィールドを挿入する効率が保証されます。

たとえば、MySQL の Innodb ストレージ エンジンの主キー。スノーフレーク アルゴリズムによって生成される主キーは、上位から下位の順に、1 ビットの符号ビット、41 ビットのタイムスタンプ ビット、10 ビットの作業プロセス ビット、および 12 ビットのシーケンス番号ビットの 4 つの部分で構成されるバイナリ表現を持ちます。

写真

  • 符号ビット(1ビット)

予約された符号ビットは常にゼロです。

  • タイムスタンプビット(41ビット)

41 ビットのタイムスタンプが保持できるミリ秒数は 2 の 41 乗です。 1 年に使用されるミリ秒数は、365 * 24 * 60 * 60 * 1000 です。計算により、次のことがわかります。Math.pow(2, 41) / (365 * 24 * 60 * 60 * 1000L);結果はおよそ69.73年に相当します。

ShardingSphere のスノーフレーク アルゴリズムの時間エポックは、2016 年 11 月 1 日 0:00 から始まり、2086 年まで使用できます。ほとんどのシステムの要件を満たすことができると考えられています。

  • 作業工程ビット(10ビット)

このフラグは Java プロセス内で一意です。分散アプリケーションのデプロイメントの場合は、各作業プロセスの ID が異なることを確認します。デフォルト値は 0 で、プロパティを介して設定できます。

  • シリアル番号ビット(12ビット)

このシーケンスは、同じミリ秒内に異なる ID を生成するために使用されます。このミリ秒内に生成された数値が 4096 (2 の 12 乗) を超える場合、ジェネレーターは次のミリ秒まで待機して生成を続行します。

注意: このアルゴリズムには「クロック ダイヤルバック」の問題があります。サーバー クロックのダイヤルバックにより、シーケンスが重複する可能性があります。したがって、デフォルトの分散プライマリ キー ジェネレーターは、許容可能な最大クロック ダイヤルバック ミリ秒を提供します。クロックが最大許容ミリ秒しきい値を超えて戻された場合、プログラムはエラーを報告します。許容範囲内であれば、デフォルトの分散主キー ジェネレーターは、クロックが最後の主キー生成の時刻に同期されるまで待機してから、作業を続行します。クロック セットバックの最大許容ミリ秒数は、デフォルト値が 0 で、プロパティを介して設定できます。

以下に、Song Ge がスノーフレーク アルゴリズムのツール クラスを示します。参照してください。

 public class IdWorker { // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) private final static long twepoch = 1288834974657L; // 机器标识位数private final static long workerIdBits = 5L; // 数据中心标识位数private final static long datacenterIdBits = 5L; // 机器ID最大值private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); // 数据中心ID最大值private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 毫秒内自增位private final static long sequenceBits = 12L; // 机器ID偏左移12位private final static long workerIdShift = sequenceBits; // 数据中心ID左移17位private final static long datacenterIdShift = sequenceBits + workerIdBits; // 时间毫秒左移22位private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; private final static long sequenceMask = -1L ^ (-1L << sequenceBits); /* 上次生产id时间戳*/ private static long lastTimestamp = -1L; // 0,并发控制private long sequence = 0L; private final long workerId; // 数据标识id部分private final long datacenterId; public IdWorker(){ this.datacenterId = getDatacenterId(maxDatacenterId); this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); } /** * @param workerId * 工作机器ID * @param datacenterId * 序列号*/ 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; } /** * 获取下一个ID * * @return */ public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { // 当前毫秒内,则+1 sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { // 当前毫秒内计数满了,则等待下一秒timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; // ID偏移组合生成最终的ID,并返回ID long nextId = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; return nextId; } private long tilNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } /** * <p> * 获取maxWorkerId * </p> */ protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { StringBuffer mpid = new StringBuffer(); mpid.append(datacenterId); String name = ManagementFactory.getRuntimeMXBean().getName(); if (!name.isEmpty()) { /* * GET jvmPid */ mpid.append(name.split("@")[0]); } /* * MAC + PID 的hashcode 获取16个低位*/ return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); } /** * <p> * 数据标识id部分* </p> */ protected static long getDatacenterId(long maxDatacenterId) { long id = 0L; try { InetAddress ip = InetAddress.getLocalHost(); NetworkInterface network = NetworkInterface.getByInetAddress(ip); if (network == null) { id = 1L; } else { byte[] mac = network.getHardwareAddress(); id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; id = id % (maxDatacenterId + 1); } } catch (Exception e) { System.out.println(" getDatacenterId: " + e.getMessage()); } return id; } }

使用方法は次のとおりです。

 IdWorker idWorker = new IdWorker(0, 0); for (int i = 0; i < 1000; i++) { System.out.println(idWorker.nextId()); }

3.3 リーフ

Leaf は Meituan のオープンソース分散 ID 生成システムです。最も初期の要望は、さまざまな事業ラインの注文 ID を生成する必要性でした。 Meituan の初期には、一部の企業は DB 自動増分を通じて ID を直接生成し、一部の企業は Redis キャッシュを通じて ID を生成し、一部の企業は UUID を使用して ID を直接生成していました。上記の方法にはそれぞれ問題があるため、Meituan はニーズを満たすために分散 ID 生成サービスを実装することにしました。現在、Leaf は Meituan Dianping 内の金融、ケータリング、食品配達、ホテル旅行、Maoyan Movies など、多くの事業ラインをカバーしています。4C8G VM に基づき、同社の RPC 呼び出しを介して、QPS ストレス テストの結果はほぼ 5w/s、TP999 1ms でした (TP = Top Percentile、Top percentage は統計用語で、平均値や中央値と同じです。TP50、TP90、TP99 などの指標は、システム パフォーマンス監視シナリオでよく使用され、50%、90%、99% などのパーセンタイルを超える状況を指します)。

現在、LEAF を使用するには、セグメント モードと SNOWFLAKE モードの 2 つの方法があります。両方のモードを同時に有効にすることも、特定のモードを指定して有効にすることもできます (デフォルトでは両方のモードが無効になっています)。

GitHub から LEAF をクローンすると、その構成ファイルは leaf-server/src/main/resources/leaf.properties にあります。各構成の意味は次のとおりです。

写真

ご覧のとおり、数値セグメント モードを使用する場合は、データベースのサポートが必要です。 SNOWFLAKE モードを使用する場合は、Zookeeper のサポートが必要です。

3.3.1 数字セグメントモード

数値セグメント モードは依然としてデータベースに基づいていますが、次のように考え方が少し変更されています。

  1. プロキシ サーバーを使用してデータベースから ID をバッチで取得し、そのたびにセグメントの値 (ステップによってサイズが決定されます) を取得し、使用後にデータベースにアクセスして新しいセグメントを取得します。これにより、データベースへの負荷が大幅に軽減されます。
  2. 各事業者の異なる番号発行要件は、biz_tag フィールドによって区別されます。各ビジネスタグの ID は個別に取得され、相互に影響を与えません。
  3. 新しいビジネスに拡張ゾーン ID が必要な場合は、テーブル レコードを追加するだけで済みます。

数値セグメント モードを使用する場合は、まずデータ テーブルを作成する必要があります。スクリプトは次のとおりです。

 CREATE DATABASE leaf CREATE TABLE `leaf_alloc` ( `biz_tag` varchar(128) NOT NULL DEFAULT '', `max_id` bigint(20) NOT NULL DEFAULT '1', `step` int(11) NOT NULL, `description` varchar(256) DEFAULT NULL, `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`biz_tag`) ) ENGINE=InnoDB; insert into leaf_alloc(biz_tag, max_id, step, description) values('leaf-segment-test', 1, 2000, 'Test leaf Segment Mode Get Id')

この表のフィールドの意味は次のとおりです。

  • biz_tag: ビジネスタグ (ビジネスによって番号セグメントのシーケンスが異なる場合があります)
  • max_id: 現在の番号セグメントの最大 ID
  • ステップ: 各数値セグメントのステップ長
  • 説明: 説明情報
  • update_time: 更新時間

設定が完了したら、プロジェクトを起動し、http://localhost:8080/api/segment/get/leaf-segment-test パス (パスの末尾の leaf-segment-test はビジネス タグ) にアクセスして ID を取得します。

セグメント モードの監視ページには、次のアドレスからアクセスできます: http://localhost:8080/cache。

数値セグメントモードの利点と欠点:

"アドバンテージ"

  • リーフ サービスは簡単に線形に拡張でき、そのパフォーマンスはほとんどのビジネス シナリオを完全にサポートできます。
  • ID 番号は、上記のデータベース ストレージの主キー要件を満たす、増加傾向にある 8 バイトの 64 ビット番号です。
  • 高い耐災害性: リーフ サービスには内部セグメント キャッシュがあります。 DB がダウンした場合でも、Leaf は短時間で外部に通常のサービスを提供することができます。
  • max_id サイズはカスタマイズ可能なので、従来の ID 方式からビジネスを移行する場合に非常に便利です。

「欠点」

  • ID 番号は十分にランダムではなく、発行された番号の数に関する情報が漏洩する可能性があり、あまり安全ではありません。
  • DB 障害が発生すると、システム全体が使用できなくなります。

3.3.2 スノーフレークモード

SNOWFLAKE モードは Zookeeper と併用する必要がありますが、SNOWFLAKE の Zookeeper への依存性は弱いです。 Zookeeper を起動した後、SNOWFLAKE で Zookeeper 情報を次のように設定できます。

 leaf.snowflake.enable=true leaf.snowflake.zk.address=192.168.91.130 leaf.snowflake.port=2183

その後、プロジェクトを再起動します。起動が成功すると、次のアドレスから ID にアクセスできます。

 http://localhost:8080/api/snowflake/get/test

3.4 Redis 生成

これは主に Redis の incrby を使用することで実現されますが、これについては特に説明する必要はないと思います。

3.5 飼育係の処理

Zookeeper でもこれを行うことができますが、面倒なためお勧めできません。

<<:  AIチップの継続的なアップグレードに加えて、NVIDIAの量子クラウドプラットフォームも無視できない。

>>:  クラウド コンピューティングを活用して IT 業界で環境の持続可能性を実現するにはどうすればよいでしょうか?

推薦する

「fantong.com」は閉鎖され、良いドメイン名fantong.comが残ったと言われています

eName.cnは4月23日、かつて有名だった飲食O2Oサイト「Fantong.com」が現在破産の...

中小企業が新しいアイデアを生み出し、ウェブサイトで質の高い記事を見つけるにはどうすればいいのか

ウェブサイトのコンテンツは、あらゆるサイトが長期的に存在し発展していくための基盤であり、特に均質化が...

ユーザーエクスペリエンスを満たす高品質な記事の書き方

ウェブサイトの最適化は簡単そうに見えますが、操作には注意を払うべき細かい点がたくさんあります。今日は...

ウェブマスターは、ウェブサイトのSEOをうまく行うために分業と協力が必要です。

SEOを学んでから1年近く実践・運用していますが、やっていることは同じです。クライアントのウェブサイ...

Google AnalyticsとBaidu Statisticsの原理の分析

Google AnalyticsとBaidu Statisticsの統計データには違いがあり、その差...

hostyun 香港 vps はどうですか?メガコンピュータルームの「香港EPYCシリーズ」VPSの簡単なレビュー

Hostyunは昨日、香港EPYCシリーズVPSの販売を正式に開始しました。母鶏は10Gbpsの帯域...

intovps - 5 ドル/1g メモリ/20g SSD/1T トラフィック/OpenStack/5 つのオプション データ センター

intovps についてあまり詳しくない人もいるようですが、実際ブロガーも知りません。intovps...

ウェブサイトの包括的な分析能力は、SEOマスターへのもう一つの架け橋です。

ウェブサイト分析能力は、キーワード分析とマイニング能力、基本的なウェブサイトのルールとデザイン能力、...

調査レポート: パブリッククラウドストレージはお金の無駄

[[270235]]昨年末、Cohesity の調査によると、IT 幹部の 47% が自社の IT ...

QVODサーバーの閉鎖と警察によるQvodの捜査に耐えられるでしょうか?

4月17日にQVODサーバーを閉鎖するというニュース(QVOD技術を使用した海賊版や下品なコンテンツ...

softshellweb: 年間 40 ドル、サンノゼ/ウーマー、KVM/3G メモリ/3 コア/60g SSD/6T トラフィック (超過した場合は帯域幅が 100M に削減されます)

Softshellwebの秋のプロモーションが始まり、アメリカ西海岸の「サンノゼ」データセンターとオ...

SEO の優位性: オンライン ブランディング

フォーブスは以前、マーケティング専門家のケン・クローグ氏による「SEOは死に絶え、ソーシャルでリアル...

IT ハイブリッド クラウド戦略: 何を、なぜ、どのように構築するのか?

「ハイブリッド」という言葉は、通常は自動車に関連していますが、ほとんどの人はその言葉を知っています。...

「オープンクラウド」とは実際には何を意味するのでしょうか?

クラウドに適用された場合、オープンとは実際には何を意味するのでしょうか?現代のソフトウェア エンジニ...

12星座のマーケティングに関する洞察についての簡単な議論

最近、映画「十二支」が公開されました。多くの大ヒット映画の登場により、旧正月シーズンはさらに盛り上が...