web-dev-qa-db-ja.com

innodbの大きなテーブルの自動インクリメントと複合主キー

私はこれについてリック・ジェームズとかなり長い議論をしました。これは、intが20億近くに制限されている自動インクリメントpkを置き換える複合キーを持つというアイデアで思いつきました。私のテーブルは、毎月数億近くのデータをキャプチャしているため、数か月でこの制限に簡単に到達します。以下は私のテーブルの様子です。キーテーブルはgdataなので、3つのフィールドPRIMARY KEY (alarmTypeID,vehicleID,gDateTime)を使用してプライマリを合成します。次に、アラームテーブルと呼ばれる別のテーブルがあります。両方のリンクは1対多です。 gdata内の1つのデータが0個以上のalarmsに関連している可能性があることを意味します。それらの間のリンクはvehicleIDgDateTimeです。

CREATE TABLE `gdata` ( 
    `alarmTypeID` tinyint(4) NOT NULL DEFAULT '0', 
    `fleetID` smallint(11) NOT NULL, 
    `fleetGroupID` smallint(11) DEFAULT NULL, 
    `fleetSubGroupID` smallint(11) DEFAULT NULL, 
    `deviceID` mediumint(11) NOT NULL, 
    `vehicleID` mediumint(11) NOT NULL, 
    `gDateTime` datetime NOT NULL, 
    `insertDateTime` datetime NOT NULL, 
    `latitude` float NOT NULL, 
    `longitude` float NOT NULL, 
    `speed` smallint(11) NOT NULL
     -- (see full text) 
) ;
ALTER TABLE `gdata` 
    ADD PRIMARY KEY (`alarmTypeID`,`vehicleID`,`gDateTime`), 
    ADD KEY `gDateTime` (`gDateTime`), 
    ADD KEY `fleetID` (`fleetID`,`vehicleID`,`gDateTime`); 
COMMIT; 

こちらが警報表です

CREATE TABLE `alarm` (
    `alarmTypeID` tinyint(4) NOT NULL, 
    `vehicleID` mediumint(9) NOT NULL, 
    `gDateTime` datetime NOT NULL, 
    `insertDateTime` datetime NOT NULL, 
    `alarmValue` varchar(5) NOT NULL, 
    `readWeb` enum('n','y') NOT NULL DEFAULT 'n', 
    `readWebDateTime` datetime NOT NULL, 
    `readMobile` enum('n','y') NOT NULL DEFAULT 'n', 
    `readMobileDateTim` datetime NOT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

ALTER TABLE `alarm` 
    ADD PRIMARY KEY (`alarmTypeID`,`vehicleID`,`gDateTime`); 
COMMIT;

すべてがよさそうですが、最近私は関連するトピックについてグーグルをやっていて、それがいくつかの議論であることを発見しました https://www.quora.com/Is-it-a-bad-idea-to-have-a-primary- key-on-3-or-more-columns は複合主キーに反し、主に挿入目的で自動インクリメントを使用することを好みます。プライマリの複合キーを維持したり、自動インクリメントに戻したりするために、これにさらに光を当てることができますか?

6
user8012596

複合キーに問題はありません。ただし、 InnoDBがデータを格納する方法 を考慮する必要があります。

上記のリンクされたドキュメントを引用:

各InnoDBテーブルのデータはページに分割されます。各テーブルを構成するページは、Bツリーインデックスと呼ばれるツリーデータ構造に配置されます。テーブルデータとセカンダリインデックスはどちらもこのタイプの構造を使用します。テーブル全体を表すBツリーインデックスは、クラスター化インデックスと呼ばれ、に従って編成されます。主キー列。インデックスデータ構造のノードには、その行のすべての列(クラスター化インデックスの場合)、またはインデックス列と主キー列(セカンダリインデックスの場合)の値が含まれます。

つまり、InnoDBはPRIMARY KEYに従ってデータを格納します。挿入するデータのPKが増加している場合、ページの断片化は発生しません。それは常にAUTO_INCREMENTで起こります。データを年代順で挿入する場合(つまり、gDateTimeは常に単調増加します)、順序を変更しますPKを構成する列のうち、

PRIMARY KEY (`gDateTime`, `alarmTypeID`, `vehicleID`)

...「新しい行を他の行の中央に合わせる」必要がないという点で同じ利点があります(つまり、Bツリーは挿入ごとに断片化されません)。

ただし、このテーブルを他の(関連する)テーブルから参照する場合は、参照するテーブルに常にPK(gDateTimealarmTypeIDvehicleID)を格納する必要があります。これは、7バイトまたは8バイトのストレージを毎回保存することを意味します。複合PKは2 + 1 + 8 = 11バイトの情報を使用します(おそらく、アライメントのために12バイトを使用します)。一方、INT UNSIGNED AUTO_INCREMENTは、参照テーブルで4バイトのみを使用します。 PKには2 ^ 32の異なる値に制限されています。 2 ^ 32を超える値が必要な場合は、BIGINT AUTO_INCREMENTが必要です。これにより、2 ^ 64が得られます(これが十分に大きくない実際的なケースはまだ見つかりません)。

これが理にかなっているかどうかは、特定のシナリオに大きく依存します。

8
joanolo

joanoloにはいくつかの良い点があります。

  • 5.6.4以降、DATETIMEおよびTIMESTAMPは、小数秒なしで、それぞれ5バイトを使用します。 (つまり、問題のPKは合計9バイトです。)
  • データの断片化はそれほど悪くありません。そして、それが他の行動の大幅な改善を可能にするならば、それは価値があるかもしれません。 (以下を参照してください。)BTreeは本質的に約69%まで落ち着きます。 (ブロック分割により、100%のフルブロックが2つの50%のフルブロックになり、その後、両方とも徐々に補充されます。)
  • DATETIMEまたはTIMESTAMPキーでPRIMARY(またはUNIQUE)を使用するのは危険です。2つのエントリが同時に発生した場合はどうなりますか? (この質問はアプリケーションによって異なります。たとえば、トラックの位置を測定する場合、1秒以内に2つのGPS読み取り値は必要ありません。)
  • PKに関するリンクは、「太い」PKについて語っています。問題のPKはわずか9バイトです-それほど太っていません。そのため、リンクはわずかに関連しています。さらに、fatnessは、fat列を含まない2つ以上のセカンダリインデックスがある場合にのみ適用されます。
  • テーブルは4バイトのINTをオーバーフローする恐れがあります。AUTO_INCREMENTの次の選択肢は8バイトのBIGINTです。 9バイトと大差ありません。
  • MEDIUMINTは3バイトです(vehicleID)。
  • InnoDB構造内にフィールドのno "alignment"があると確信しています。 InnoDBは、ファイルがすべてのハードウェアアーキテクチャで互換性を持つように設計されています。
  • MySQL 必須 PKが一意であること。 alarmTypeIDを削除すると一意性が失われる場合は、しないでください

詳細...

ADD PRIMARY KEY (`alarmTypeID`,`vehicleID`,`gDateTime`), -- 1+3+5 = 0 bytes
ADD KEY `gDateTime` (`gDateTime`),                       -- 5 + 1+3 = 9
ADD KEY `fleetID` (`fleetID`,`vehicleID`,`gDateTime`);   -- 2+3+5 + 1 = 11

PKは残りの列に含まれているため、0バイトと言います。セカンダリキーの数値は、セカンダリキー列のサイズ+追加のPK列です。 (もちろん、インデックスにはかなりのオーバーヘッドがあるため、これらの数値を使用してBTreeの最終的なサイズを計算することはできません。ファッジファクターとして3倍が必要になる場合があります。)

SELECT

WHERE alarmTypeID = constant
  AND vehicleID = constant
  AND gDateTime ... (some range)

alarmTypeIDvehicleIDgDateTimeよりも(gDateTimealarmTypeIDvehicleID)の方がはるかに適切に処理されます)。これが一般的なクエリである場合、断片化を避けたいという欲求を上回ると私は主張します。

PRIMARY KEY(alarmTypeIDvehicleIDgDateTime)は、2次キーとデータの間のバウンスを回避します。

PRIMARY KEY(gDateTimealarmTypeIDvehicleID)は、アラームや車両を使用できず、対象外のアラームや車両をまたぐ必要があります。または、セカンダリキーを使用して、前後にバウンスするようにします。どちらの場合も、はるかに遅くなります。 (Rule of Thumb:データがキャッシュされていない場合、回転ディスクの場合は10倍遅くなります。)

4
Rick James