それは問題につながる:分散型のプロジェクトでは、我々はグローバルIDの生成を確実にするために使用するもの?
方法1。まず、私たちは、UUIDを生成するために、すべての可能を検討してください。
それはまた、GUID他の言語で呼ばれるユニバーサル一意の識別子UUID(汎用一意識別子)であり、長さが生成することができる32ビットのグローバル一意識別子。
文字列UUID = UUID.randomUUID()のtoString()
例の結果:
046b6c7f-0b8a-43b9-B35D-6489e6daee91
しかし、我々はそれが一つの欠点を持って使用します。
それは、グローバルに一意を保護することができますが、32は長すぎる占め、彼はあったが順不同で、パフォーマンスの低下を倉庫
なぜUUID障害は、貧しいパフォーマンスのストレージ、それにつながることができますか?
これには、 B +ツリーインデックス部門を:
リレーショナルデータベースは我々の構造の指標B +ツリーであるため、IDフィールドを取るために、例えば、インデックスツリーの各ノードは、ID番号が格納されています。
私たちのIDが昇順に挿入するようにした場合など、8,9,10は徐々にノードだけを続く新しいIDがそれらに挿入される挿入します。最後のノードがいっぱいになると、新しいノードが核分裂します。このインサートは、そのような分割ノードの最小数、および各ノードのためのスペースをフルに活用として、比較的高い性能に挿入されます。
我々は完全に無秩序挿入場合は、いくつかの中間ノードが分割を生成につながるだけでなく、それによって大幅にデータベースの挿入のパフォーマンスを低下させる、不飽和ノードの多くを作成するために、無駄になります。
方法2:インクリメントの主キーデータベース
:テーブルという名前のテーブルには、以下の構造を有していると仮定
IDのフェイルド
35 A
たびに生成されたIDがデータベースにアクセスするために、次の文を実行します。
ベギン;
テーブルにREPLACE(フェイルド)VALUES( 'A')。
LAST_INSERT_ID()を選択。
コミット;
意味INTO REPLACEはテーブルの出会いの競合で一意のインデックス値は、古いデータを交換する場合は、レコードを挿入することです。
このように、それぞれの時間がのIDをインクリメントすることができます。
性能を改善するために、分散システムDBプロキシリクエスト異なるサブライブラリーで使用することができ、それぞれ異なるサブライブラリーは、初期値を設定し、サブライブラリーに等しいステップの数:
このように、DB1は、IDを生成1,4,7,10,13です....、DB2はID 2,5,8,11,14を生成している.....
短所:
パフォーマンスに影響を与え、そしてサービスが利用できなくなったら、データベースをハングアップしないだけでなく、大きく依存してデータベース生成IDの結果として。
方法 3:スノーフレーク
何スノーフレークアルゴリズム
これは、アルゴリズムのさえずりの採用で、目的は、分散システムでグローバルに一意な傾向を生成することで、IDがインクリメントされ
スノーフレークアルゴリズム生成ID構造が似ているのですか?次の図を見てみましょう:
スノーフレークは、生成されたIDは、4つの部分の合計に分割されます。
1.最初の
職業1ビットは、その値は常に0、無実用的な効果です。
2.タイムスタンプ
ミリ秒単位まで正確な職業の41bitは、約69年間の合計を収容することができます。
3.作業機のID
上位5ビットデータセンタID(datacenterId)、低仕事5ビットノードID(workerId)、1024は複数のノードを行う請求占有10ビットは、対応することができます。
4.シリアル番号
12ビットを占有、この値は同じミリ秒で、累積的に0から4095までの同じノードを蓄積することができるです。
スノーフレークは、グローバルに一意のID番号アルゴリズムは、同じミリ秒でそれまでに生成することができますか?単純な乗算を実行します。
ミリ秒のIDと同じ数= 1024×4096 = 4194304
ほとんどの並行処理のシナリオでこの図は適切です。
スノーフレークコード器具(ツールクラス)
SnowFlakeUtilsクラス{パブリック 開始//タイムスタンプ 民間最終静的ロングSTART_STMP = 1480166465631L; //上の各部分によって占められるビットの数三 民間最終静的長いSEQUENCE_BIT = 12; // によって占められるビットのシーケンス番号 民間最終静的長いMACHINE_BIT = 5;によって占められるビットの//機械識別番号 民間最終静的長いDATACENTER_BIT = 5; //データセンタによって占められるビットの数 //最大の各部 民間最終静的長いMAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT ); プライベートロングMAX_MACHINE_NUM最終静的-1L = ^(-1L << MACHINE_BIT); プライベートロングMAX_SEQUENCE最終静的-1L = ^(-1L << SEQUENCE_BIT); 各部の//左方向変位 民間最終静的長いMACHINE_LEFT = SEQUENCE_BIT; 民間最終静的長いDATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT。 IF(machineId> MAX_MACHINE_NUM || machineId <0){ 民間最終静的長いTIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT。 プライベート長いdatacenterId。//数据中心 プライベート長いmachineId。//机器标识 プライベート長いシーケンス= 0L; //序列号 民間長いlastStmp = -1L; //上一次时间戳 公共SnowFlakeUtils(長いdatacenterId、長いmachineId){ 場合(datacenterId> MAX_DATACENTER_NUM || datacenterId <0){ "(新しいIllegalArgumentExceptionをスローしdatacenterIdは大きくすることはできませんMAX_DATACENTER_NUM未満0" )より。 } 新をスロー(「machineIdはMAX_MACHINE_NUM以上0未満にすることはできません」)。 } this.datacenterId = datacenterId。 = MachineID this.machineId; } //次のID生成 パブリックNEXTID同期ロング(){ ロングcurrStmp getNewstmp =(); IF(currStmp <lastStmp){ スローをのRuntimeException新しい新規( "クロックが後方IDを生成することを拒否移動。"); } IF(currStmp == lastStmp){ //現在のコールと最後のコールミリ秒で表される条件が同じ、のみ第三の部分内であれば、シーケンス番号の増分が一意であると判断するのでされる+1した。 配列= (シーケンス+ 1)&MAX_SEQUENCEは、 数列が同じ// MSが次のミリ秒待機する、最大値に達しているで IF(配列== 0L){ currStmp getNextMill =(); } } {他 異なる//ミリ秒以内に、シーケンス番号が0に設定されています // currTimestamp> lastTimestampは、呼び出すための最後の呼び出しでこのコントラストを説明する前提として、ブランチに実行、もはやミリ秒内で同じであり、この時間は、シリアル番号がバックをゼロに再設定することができます。 = 0L配列; } lastStmp = currStmp; //は、比較的数ミリ秒、マシンID番号と自己増力スプライシングある リターン(currStmp - START_STMP)<< TIMESTMP_LEFT // スタンプコンポーネント | datacenterId << DATACENTER_LEFT //データセンタ部分 | MachineID << MACHINE_LEFT //機械識別部 |配列; SEQ ID NOの//部分 } プライベートロングgetNextMill(){ ロングミルgetNewstmp =(); 一方、(ミル<= lastStmp){ ミルgetNewstmp =(); } ミルを返します。 } プライベートロングgetNewstmp( ){ ()を返すのSystem.currentTimeMillis; } } 私たちは、直接使用するためのプロジェクトにインポートすることができます
スノーフレークアルゴリズムの利点:
1. DBは、メモリ内に完全に生成された世代ID、高性能の可用性に依存しません。
2.ID減少傾向の増加、その後の挿入インデックス・ツリーより良いパフォーマンス。
スノーフレークアルゴリズムの欠点:
これは、システムクロックの一貫性に依存します。マシンクロックのコールバックシステムならば、紛争ID、IDまたは障害を引き起こす可能性があります。