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

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

[[415300]]

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

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

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

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

1. 2つのアプローチ

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

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

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

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

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

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

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

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

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

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

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

  1. 次のような変数を表示  '自動増分%'  

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

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

  1. @@auto_increment_increment=9 を設定します

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

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

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

  1. 作成する テーブルtest01(id整数 主要な  KEY auto_increment、ユーザー名varchar (255))auto_increment=8;

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

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

2.2 MySQL+MyCat+ZooKeeper

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

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

  • ローカルファイル経由で実装
  • データベースを通じて実装
  • ローカルタイムスタンプで実装
  • 分散型ZK IDジェネレーターを通じて実装
  • 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 がスノーフレーク アルゴリズムのツール クラスを示します。参照してください。

  1. パブリッククラスIdWorker {
  2. // 時間の開始点がベンチマークとして使用されます。通常はシステムの最新の時刻が使用されます(一度決定すると変更できません)
  3. プライベート最終静的ロングtwepoch = 1288834974657L;
  4. // マシン識別用のビット数
  5. プライベート最終静的ロングワーカーIdBits = 5L;
  6. // データセンター識別ビットの数
  7. プライベート最終静的長いデータセンターIdBits = 5L;
  8. // マシンIDの最大値
  9. プライベート最終静的ロング maxWorkerId = -1L ^ (-1L << workerIdBits);
  10. // データセンターIDの最大値
  11. プライベート最終静的ロング maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
  12. // ミリ秒単位でビットを増分する
  13. プライベート最終静的長いシーケンスビット = 12L;
  14. // マシンIDを12ビット左にシフトする
  15. プライベート最終静的長いworkerIdShift = sequenceBits;
  16. // データセンターIDを17ビット左にシフト
  17. プライベート最終静的ロングデータセンターIdShift = シーケンスビット + ワーカーIdビット;
  18. // 時間ミリ秒を左に 22 ビットシフト
  19. プライベート最終静的ロングタイムスタンプLeftShift = シーケンスビット + ワーカーIdビット + データセンターIdビット;
  20.  
  21. プライベート最終静的ロングシーケンスマスク = -1L ^ (-1L << シーケンスビット);
  22. /* 最終生産 ID タイムスタンプ */
  23. プライベート静的ロングlastTimestamp = -1L;
  24. // 0、同時実行制御
  25. プライベートロングシーケンス= 0L;
  26.  
  27. プライベート最終長いワーカー ID;
  28. //データ識別ID部分
  29. プライベートfinal longデータセンターId;
  30.  
  31. パブリックIdWorker(){
  32. this.datacenterId = getDatacenterId(maxDatacenterId);
  33. this.workerId = getMaxWorkerId(データセンターId、maxWorkerId);
  34. }
  35.  
  36. /**
  37. * @param ワーカーID
  38. * ワーカーマシンID
  39. * @param データセンターID
  40. * シリアルナンバー
  41. */
  42. パブリックIdWorker(long ワーカー ID、long データセンター ID) {
  43. ワーカーID > 最大ワーカーID || ワーカーID < 0 の場合 {
  44. throw new IllegalArgumentException(String.format( "ワーカー ID は %d より大きく、0 より小さくすることはできません" , maxWorkerId));
  45. }
  46. データセンターID > 最大データセンターID || データセンターID < 0 の場合 {
  47. 新しい IllegalArgumentException をスローします(String.format( "データセンター ID は %d より大きく、0 より小さくすることはできません" 、 maxDatacenterId));
  48. }
  49. this.workerId = ワーカーId;
  50. this.datacenterId = データセンターId;
  51. }
  52.  
  53. /**
  54. * 次のIDを取得する
  55. *
  56. * @戻る 
  57. */
  58. パブリック同期された長いnextId(){
  59. 長いタイムスタンプ= timeGen();
  60. if (タイムスタンプ< lastTimestamp) {
  61. throw new RuntimeException(String.format( "時計が逆戻りしました。%d ミリ秒間 ID の生成を拒否しています" , lastTimestamp - timestamp ));
  62. }
  63.  
  64. 最後のタイムスタンプ ==タイムスタンプ場合
  65. // 現在のミリ秒内で、+1
  66. シーケンス= (シーケンス+ 1) & シーケンスマスク;
  67. シーケンス== 0 の場合
  68. // 現在のミリ秒カウントがいっぱいの場合は、次の秒まで待機します
  69. タイムスタンプ= tilNextMillis(lastTimestamp);
  70. }
  71. }それ以外{
  72. シーケンス= 0L;
  73. }
  74. lastTimestamp =タイムスタンプ;
  75. // IDオフセットの組み合わせは最終的なIDを生成し、IDを返します
  76. long nextId = ((タイムスタンプ- twepoch) << timestampLeftShift)
  77. | (データセンターID << データセンターIDシフト)
  78. | (ワーカーID << ワーカーIDシフト) |順序;
  79.  
  80. 次のIdを返します
  81. }
  82.  
  83. プライベートlong tilNextMillis(final long lastTimestamp) {
  84. 長いタイムスタンプ= this.timeGen();
  85. while (タイムスタンプ<= lastTimestamp) {
  86. タイムスタンプ= this.timeGen();
  87. }
  88. 戻る タイムスタンプ;
  89. }
  90.  
  91. プライベートロングtimeGen() {
  92. System.currentTimeMillis()を返します
  93. }
  94.  
  95. /**
  96. * <p>
  97. * maxWorkerId を取得する
  98. * </p>
  99. */
  100. 保護された静的long getMaxWorkerId(long datacenterId, long maxWorkerId) {
  101. StringBuffer mpid = 新しいStringBuffer();
  102. mpid.append(データセンターID);
  103. 文字列= ManagementFactory.getRuntimeMXBean().getName();
  104. if (! name.isEmpty ()) {
  105. /*
  106. * jvmPid を取得する
  107. */
  108. mpid.append(名前.split( "@" )[0]);
  109. }
  110. /*
  111. * MAC + PIDのハッシュコードの下位16ビットを取得します
  112. */
  113. (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1)を返します
  114. }
  115.  
  116. /**
  117. * <p>
  118. * データ識別ID部分
  119. * </p>
  120. */
  121. 保護された静的long getDatacenterId(long maxDatacenterId) {
  122. ロングID = 0L;
  123. 試す {
  124. InetAddress ip = InetAddress.getLocalHost();
  125. ネットワークインターフェイス ネットワーク = NetworkInterface.getByInetAddress(ip);
  126. ネットワークがnull場合
  127. id = 1L;
  128. }それ以外{
  129. byte[] mac = network.getHardwareAddress();
  130. id = ((0x000000FF & (long) mac[mac.length - 1])
  131. | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
  132. id = id % (maxDatacenterId + 1);
  133. }
  134. } キャッチ (例外 e) {
  135. システム。出力.println( " getDatacenterId: " + e.getMessage());
  136. }
  137. IDを返します
  138. }
  139. }

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

  1. IdWorker idWorker = 新しい IdWorker(0, 0);
  2. ( int i = 0; i < 1000; i++) {
  3. システム。出力.println(idWorker.nextId());
  4. }

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 数字セグメントモード

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

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

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

  1. 作成する データベースリーフ
  2. 作成する テーブル`leaf_alloc` (
  3. `biz_tag` varchar (128)なし  NULL  デフォルト  ''
  4. `max_id`ビッグイント(20) NOT   NULL  デフォルト  '1'
  5. `step` int (11) NOT   NULL
  6. `description` varchar (256)デフォルト  NULL
  7. `update_time`タイムスタンプ ない  NULL  デフォルト 現在のタイムスタンプ の上 アップデート  CURRENT_TIMESTAMP
  8. 主要な キー(`biz_tag`)
  9. )ENGINE=InnoDB;
  10.  
  11. 入れる  leaf_alloc(biz_tag, max_id, step, description) '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 情報を次のように設定できます。

  1. leaf.snowflake.enable = true  
  2. leaf.snowflake.zk.address=192.168.91.130
  3. leaf.snowflake.port=2183

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

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

3.4 Redis 生成

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

3.5 飼育係の処理

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

4. まとめ

要約すると、プロジェクトで MyCat が使用される場合は MyCat+Zookeeper を使用できますが、それ以外の場合は LEAF を使用することをお勧めします。どちらのモードも受け入れられます。

この記事はWeChatの公式アカウント「江南一店語」から転載したものです。下のQRコードからフォローできます。この記事の転載については江南易店宇公式アカウントまでご連絡ください。

<<:  雲が増えればパワーも増える?マルチクラウド アーキテクチャに隠された 11 の秘密をご存知ですか?

>>:  クラウドネイティブなビジネス変革に向けた9つのステップ

推薦する

インターネットマーケティングで失敗する運命にある企業はどれでしょうか?

月収10万元の起業の夢を実現するミニプログラム起業支援プラン企業にとって、インターネット マーケティ...

百度360と他の12社が「検索エンジン自主規律条約」に署名

11月1日午後、中国インターネット協会は北京で「インターネット検索エンジンサービス自主規律条約」(以...

マルチクラウドのトップ 10 トレンド: 包括的か、それとも混沌か?

今日のマルチクラウド環境は 1、2 年前とは大きく異なり、明日のマルチクラウド環境も同様に大きく異な...

Baidu の統計が SEO にどのように役立つか

百度統計は新年を迎えて第3版を更新した。 SEO の観点から見ると、第 3 版では第 2 版と比べて...

Kubernetes アドミッション コントローラーが必要な理由は何ですか?

Kubernetes アドミッション コントローラーは、クラスター管理に不可欠です。これらのコントロ...

H3Cがコアルーター市場に本格進出し、その強さが再び証明される

[51CTO.com からのオリジナル記事] シスコが 2004 年 6 月に最初のクラスタ ルータ...

初心者ウェブマスターのための最もタブーなウェブサイト運営を公開

私はほぼ半年かけてウェブサイトを構築してきましたが、今日はそれを皆さんと共有したいと思います。もう無...

ウェブマスターが次の3つのステップに従うことで長期的な利益を得ることは難しくありません。

最近、インターネットで「ウェブサイト運営:ウェブマスターは同じスタイルを変えずにどれくらい生き残れる...

テンセントの鍾祥平氏:デジタル経済発展の新たな原動力となるスマート交通ネットワークの構築

2021年11月3日、「デジタルと現実の融合、新たなチャンスの爆発」をテーマにしたテンセントデジタル...

Google 検索が 3 つの重要な変更と SEO への影響を発表

全文は、こちらの記事をご覧ください:「GOOGLE 検索が 20 周年に発表した 3 つの重要な調整...

なぜ Baidu は 20 日以上経ってもウェブサイトの 3 ページしかインデックスしなかったのでしょうか?

多くの初心者は、検索エンジンに含まれない問題に悩まされています。誰もが初心者の頃にこの状況を経験した...

ウェブサイトのアクセス不能が SEO データとソリューションに与える影響

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っていますこのブログ...

クラウドへの移行を支援する 7 つのステップ

データとアプリケーション デバイスが増加するにつれて、企業はローカル リソースではビジネス開発のニー...