主キーとしてのMySQL intとvarchar(InnoDB Storage Engine?


13

私はWebアプリケーション(プロジェクト管理システム)を構築していますが、パフォーマンスに関してはこれについて疑問に思っていました。

課題テーブルがあり、その中には他のさまざまなテーブルにリンクする12の外部キーがあります。そのうち8つは、他のテーブルからタイトルフィールドを取得するために参加する必要があります。これは、Webアプリケーションでレコードが意味をなすようにするためです。これらの結合ごとに1つのフィールド。

今、永続的な理由で自動増分主キーを使用するように言われました(シャーディングがGUIDを使用する必要がある場合を除きます)が、varchar(最大長32)のパフォーマンスを使用するのはどれほど悪いですか?つまり、これらのテーブルのほとんどは、おそらく多くのレコードに含まれないでしょう(それらのほとんどは20未満でなければなりません)。また、タイトルを主キーとして使用すると、95%の時間を結合する必要がないため、SQLの95%でパフォーマンスヒットさえ発生します(私は思う)。私が考えることができる唯一の欠点は、私はより高いディスクスペース使用量を持っているということです(しかし、1日は本当に大したことです)。

列挙の代わりにこのようなものの多くにルックアップテーブルを使用している理由は、これらの値のすべてがアプリケーション自体を介してエンドユーザーによって構成可能である必要があるためです。

多くのレコードを持つことを除いて、テーブルの主キーとしてvarcharを使用することの欠点は何ですか?

更新-いくつかのテスト

それで、私はこのものでいくつかの基本的なテストをすることにしました。私は100000レコードを所有しており、これらは基本クエリです。

ベースVARCHAR FKクエリ

SELECT i.id, i.key, i.title, i.reporterUserUsername, i.assignedUserUsername, i.projectTitle, 
i.ProjectComponentTitle, i.affectedProjectVersionTitle, i.originalFixedProjectVersionTitle, 
i.fixedProjectVersionTitle, i.durationEstimate, i.storyPoints, i.dueDate, 
i.issueSecurityLevelId, i.creatorUserUsername, i.createdTimestamp, 
i.updatedTimestamp, i.issueTypeId, i.issueStatusId
FROM ProjectManagement.Issues i

ベースINT FKクエリ

SELECT i.id, i.key, i.title, ru.username as reporterUserUsername, 
au.username as assignedUserUsername, p.title as projectTitle, 
pc.title as ProjectComponentTitle, pva.title as affectedProjectVersionTitle, 
pvo.title as originalFixedProjectVersionTitle, pvf.title as fixedProjectVersionTitle, 
i.durationEstimate, i.storyPoints, i.dueDate, isl.title as issueSecurityLevelId, 
cu.username as creatorUserUsername, i.createdTimestamp, i.updatedTimestamp, 
it.title as issueTypeId, is.title as issueStatusId
FROM ProjectManagement2.Issues i
INNER JOIN ProjectManagement2.IssueTypes `it` ON it.id = i.issueTypeId
INNER JOIN ProjectManagement2.IssueStatuses `is` ON is.id = i.issueStatusId
INNER JOIN ProjectManagement2.Users `ru` ON ru.id = i.reporterUserId
INNER JOIN ProjectManagement2.Users `au` ON au.id = i.assignedUserId
INNER JOIN ProjectManagement2.Users `cu` ON cu.id = i.creatorUserId
INNER JOIN ProjectManagement2.Projects `p` ON p.id = i.projectId
INNER JOIN ProjectManagement2.`ProjectComponents` `pc` ON pc.id = i.projectComponentId
INNER JOIN ProjectManagement2.ProjectVersions `pva` ON pva.id = i.affectedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvo` ON pvo.id = i.originalFixedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvf` ON pvf.id = i.fixedProjectVersionId
INNER JOIN ProjectManagement2.IssueSecurityLevels isl ON isl.id = i.issueSecurityLevelId

また、次の追加でこれらのクエリを実行しました。

  • 特定の項目を選択します(i.key = 43298)
  • i.idによるグループ化
  • 順序(int FKの場合はit.title、varchar FKの場合はi.issueTypeId)
  • 制限(50000、100)
  • グループ化して一緒に制限する
  • グループ化、順序付け、および制限

これらの結果は次のとおりです。

クエリタイプ:VARCHAR FK TIME / INT FK TIME


基本クエリ:〜4ms /〜52ms

特定の項目を選択してください:〜140ms /〜250ms

i.idによるグループ化:〜4ms /〜2.8sec

オーダー:〜231ms /〜2sec

制限:〜67ms /〜343ms

グループ化と制限:〜504ms /〜2sec

グループ化、順序付け、および制限:〜504ms /~2.3sec

現在、どちらか一方(または両方)を高速化するためにどのような構成を行うことができるかわかりませんが、データのクエリでVARCHAR FKがより高速に表示されるようです(場合によってははるかに高速です)。

その速度の改善が追加のデータ/インデックスサイズの価値があるかどうかを選択する必要があると思います。


テストは何かを示しています。また、デフォルトのMySQL設定は実際にはInnoDB用に最適化されていないため、さまざまなInnoDB設定(バッファープールなど)でテストします。
ypercubeᵀᴹ

挿入/更新/削除のパフォーマンスもインデックスサイズの影響を受ける可能性があるため、テストする必要があります。通常、すべてのInnoDBテーブルの1つのクラスター化キーはPKであり、この(PK)列は他のすべてのインデックスにも含まれます。これはおそらく、InnoDBの大きなPKとテーブルの多くのインデックスの大きな欠点の1つです(ただし、32バイトはやや中程度であり、大きくないため、問題ではない可能性があります)。
ypercubeᵀᴹ

また、テーブルが100K(実際には大きくない)より大きくなることが予想される場合は、より大きなテーブル(たとえば10〜100M行の範囲、またはそれ以上)でテストする必要があります。
ypercubeᵀᴹ

@ypercubeしたがって、データを200万に増やすと、varchar外部キーがかなり安定している場合、int FKのselectステートメントが指数関数的に遅くなります。varcharは、selectクエリ(この特定のテーブルおよび他のいくつかのテーブルで重要になる)を得るためのディスク/メモリ要件の価格に見合う価値があると思います。
ryanzec

結論を出す前に、db(特にInnoDB)の設定も確認してください。小さな参照テーブルで、私は指数関数的に増加期待していない
ypercubeᵀᴹ

回答:


9

主キーについては、次のルールに従います。

a)ビジネス上の意味はないはずです-開発中のアプリケーションから完全に独立している必要があります。したがって、自動生成された整数を使用します。ただし、追加の列を一意にする必要がある場合は、一意のインデックスを作成してそれをサポートします

b)結合で実行する必要があります-varchars vs integerへの結合は、主キーの長さが大きくなるにつれて約2倍から3倍遅くなります。そのため、整数としてキーを使用します。すべてのコンピューターシステムはバイナリであるため、文字列がバイナリに変更され、他のシステムと比較すると非常に遅いと思われます

c)可能な限り最小のデータ型を使用します-テーブルに52の米国州などの非常に少ない列があると予想される場合、2桁のコードに可能な限り最小の型(CHAR(2)を使用しますが、tinyint (128)列と最大20億になる可能性のある大きな整数

また、たとえばプロジェクト名が変更された場合(珍しいことではありません)、主キーから他のテーブルに変更をカスケードするという課題があります。

主キーの整数を順次自動インクリメントし、データベースシステムが将来の変更をサポートする組み込みの効率を取得します


1
文字列はバイナリに変更されません。それらは最初からバイナリで保存されます。他にどのように保存されますか?おそらく、大文字と小文字を区別しない比較を可能にする操作を考えていますか?
すべての取引のジョン

6

テストでは、varcharとintキーのパフォーマンスの違いではなく、複数の結合のコストを比較しています。1つのテーブルのクエリが多くのテーブルを結合するよりも高速であることは驚くことではありません。
varchar主キーの欠点の1つは、atxdbaが指摘したようにインデックスサイズが大きくなることです。ルックアップテーブルにPK以外のインデックスがない場合(これは非常に可能性は低いですが、可能です)、ルックアップを参照する各テーブルにはこの列にインデックスがあります。
自然な主キーのもう1つの悪い点は、その値が変更され、多くのカスケード更新が発生する可能性があることです。すべてのRDMS、たとえばOracleではなく、on update cascade。一般に、非常に悪い習慣として考慮して主キー値を変更する。自然な主キーは常に悪であるとは言いたくありません。ルックアップ値が小さく、決して変更されない場合、これは許容できると思います。

検討したいオプションの1つは、マテリアライズドビューを実装することです。Mysqlは直接サポートしていませんが、基になるテーブルのトリガーを使用して目的の機能を実現できます。したがって、表示する必要があるすべてのものを含む1つのテーブルができます。また、パフォーマンスが許容できる場合は、現時点では存在しない問題と格闘しないでください。


3

最大の欠点は、PKの繰り返しです。ディスク領域の使用量の増加を指摘しましたが、インデックスサイズの増加が大きな懸念事項であることは明らかです。innodbはクラスター化インデックスであるため、すべてのセカンダリインデックスはPKのコピーを内部に保存し、最終的に一致するレコードを見つけるために使用します。

テーブルは「小さい」と予想されます(20行は非常に小さいです)。innodb_buffer_pool_sizeを次の値に設定するのに十分なRAMがある場合

select sum(data_length+index_length) from information_schema.tables where engine='innodb';

その後、それを行うと、おそらくかなり座っているでしょう。ただし、一般的なルールとして、他のmysqlのオーバーヘッドとキャッシュのために、システムメモリ全体の少なくとも30%〜40%を残しておきます。そして、それは専用のDBサーバーであると仮定しています。システム上で他のものを実行している場合は、それらの要件も考慮する必要があります。


1

@atxdbaの答えに加えて、数値を使用する方がディスクスペースに優れている理由を説明しました:2つのポイントを追加したかった:

  1. IssuesテーブルがVARCHAR FKベースであり、20個の小さなVARCHAR(32)FKがある場合、レコードは20x32バイトの長さになりますが、他のテーブルはルックアップテーブルであるため、INT FKはTINYINT FKになります20フィールドの場合、20バイトのレコード。数百のレコードについてはあまり変わらないことを知っていますが、数百万に達すると、スペース節約に感謝するでしょう

  2. 速度の問題については、カバーインデックスの使用を検討します。このクエリでは、インデックスをカバーするために使用するルックアップテーブルからそれほど多くのデータを取得していないようで、VARCHAR FK / W / COVERINGで提供されたテストをもう一度実行しますインデックスおよび通常のINT FK。

それが役立つことを願って、

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.