7

要件 :

Page#1 -> ユーザーと最新の 10 件のブログ投稿の 1 ~ 2 行のプレビューを表示

Page#2 -> 単一のブログ投稿を全文とともに表示します。

方法 1 :

MySQL table ->   userid -> varchar 50
                 post_id -> integer
                 post_title -> varchar 100
                 post_description -> varchar 10000

page#1では、 blog_table から user_id、post_title、post_description を選択します。post_description の部分文字列は、リストにプレビューを表示するために使用されます。

page#2の場合、user_id、post_title、post_description を選択します。ここで、post_id = N

方法 2 :

 MySQL table ->   userid -> varchar 50
                  post_id -> integer
                  post_title -> varchar 100
                  post_brief -> varchar 250
                  post_description -> text

page#1では、 blog_table から user_id、post_title、post_brief を選択します。

page#2の場合、user_id、post_title、post_description を選択します。ここで、post_id = N

1 つは varchar として、もう 1 つはテキストとして (ファイル システムにアクセスし、必要な場合にのみ照会する必要があるため) 2 つの列を保存することは、パフォーマンス上の利点に値しますか?

メソッド 2 はテキストへのポインタのみを行に格納するのに対し、メソッド 1 は行に完全な varchar 10K 文字列を格納するためです。RAM に格納できるテーブル データの量に影響するので、クエリの読み取りパフォーマンスに影響しますか?

4

3 に答える 3

16

SQLクエリのパフォーマンスは、取得した列ではなく、主にJOIN、WHERE句、GROUP BY、およびORDERBYに依存します。プログラミング言語で処理するためにネットワークを経由する必要がある可能性のある非常に多くのデータが取得された場合にのみ、列はクエリの速度に顕著な影響を及ぼします。ここではそうではありません。

簡単な答え:提案された2つのセットアップ間のパフォーマンスの違いは非常に小さい可能性があります。

速度を上げるには、post_id列に(一意の)インデックスを付ける必要があります。他の列で選択、並べ替え、グループ化を行っていないため、データをテーブルから直接取得できます。これは非常に高速なプロセスです。

ここでは「ページ」について話しているので、それらはユーザーに表示されると思います。同じページにある何千ものブログ投稿の表を人間に表示したいとは思わないので、おそらくそうします。実際には、質問に含めなかったORDERBY句やLIMIT句がステートメントに含まれています。

しかし、この全体をもう少し深く見てみましょう。実際に大量のTEXT列をハードディスクから直接読み取っていると仮定すると、ドライブの最大読み取り速度に達しませんか?特に余分なLEFT()呼び出しを節約できるので、VARCHAR(250)だけを取得する方が高速ではないでしょうか。

LEFT()呼び出しをテーブルからすばやく外すことができます。文字列関数は非常に高速です。結局のところ、CPUがデータの一部を切り取るだけであり、これは非常に高速なプロセスです。顕著な遅延が発生するのは、WHERE句、JOINなどで使用されている場合のみですが、これは、これらの関数が遅いためではなく、次のことを行うために何度も(場合によっては数百万回)実行する必要があるためです。結果の1行でも生成され、さらに多くの結果が生成されます。これらの使用により、データベースがインデックスを適切に使用できなくなることがよくあるためです。

つまり、最終的には、MySQLがデータベースからテーブルの内容をどれだけ速く読み取ることができるかということになります。そしてそれは、使用しているストレージエンジンとその設定によって異なります。MySQLは、InnoDBやMyISAMを含む(ただしこれらに限定されない)多くのストレージエンジンを使用できます。これらのエンジンはどちらも、TEXT列やBLOB列などの大きなオブジェクトに対して異なるファイルレイアウトを提供します(ただし、おかしなことに、VARCHARもあります)。TEXT列が行の残りの部分とは異なるページに格納されている場合、ストレージエンジンは行ごとに2ページを取得する必要があります。残りの部分と一緒に保存すると、1ページになります。順次処理の場合、これはパフォーマンスの大きな変化になる可能性があります。

これについての背景説明を少し示します。

長い答え:それは異なります:)

どのレイアウトが実際に速いかを実際に呼び出すには、独自のハードウェアでいくつかのベンチマークテストを実行する必要があります。2番目のセットアップでは、追加の列で冗長性が導入されるため、ほとんどのシナリオでパフォーマンスが低下する可能性があります。テーブル構造により、短いVARCHAR列がディスク上の同じページに収まり、長いTEXT列が別のページに収まる場合にのみ、パフォーマンスが向上します。

編集:TEXT列とパフォーマンスの詳細

BLOBとインメモリ処理についてはよくある誤解があるようです。かなりの数のページ(StackOverflowのいくつかの回答を含む-私はそれらを見つけて追加のコメントを与える)は、TEXT列(および他のすべてのBLOB)はMySQLによってメモリ内で処理できないと述べています。パフォーマンスの独り占め。それは真実ではありません。実際に起こっていることはこれです:

TEXT列を含むクエリを実行し、そのクエリを処理するために一時テーブルが必要な場合、 MySQLのストレージエンジンはTEXT列を処理できないため MySQLはその一時テーブルをメモリではなくディスクに作成する必要があります。この関連する質問MEMORYを参照してください。

MySQLのドキュメントには次のように記載されています(段落は3.2から5.6までのすべてのバージョンで同じです)。

一時テーブルを使用して処理されるクエリの結果にBLOBまたはTEXT列が含まれると、MEMORYストレージエンジンはこれらのデータ型をサポートしないため、サーバーはメモリではなくディスク上のテーブルを使用します(セクション8.4.3.3を参照)。 「MySQLが内部一時テーブルを使用する方法」)。ディスクを使用するとパフォーマンスが低下するため、本当に必要な場合にのみ、クエリ結果にBLOB列またはTEXT列を含めてください。たとえば、すべての列を選択するSELECT*の使用は避けてください。

それは人々を混乱させる最後の文です-それはただの悪い例だからです。Simpleは一時テーブルを使用しないため、このパフォーマンスの問題のSELECT *影響受けません。たとえば、同じ選択がインデックス付けされていない列によって順序付けられた場合、一時テーブルを使用する必要がありこの問題の影響を受けます。MySQLのコマンドを使用して、クエリに一時テーブルが必要かどうかを確認します。EXPLAIN

ちなみに、これはキャッシュに影響しません。TEXT列は、他のものと同じようにキャッシュできます。クエリに一時テーブルが必要で、それをディスクに保存する必要がある場合でも、システムにそのためのリソースがあれば、結果をキャッシュでき、キャッシュは無効になりません。この点で、TEXT列は他のものとまったく同じです。

編集2:TEXT列とメモリ要件の詳細..。

MySQLは、ストレージエンジンを使用してディスクからレコードを取得します。次に、結果をバッファリングし、クライアントに順番に渡します。以下は、このバッファがディスクではなくメモリにあることを前提としています(上記の理由を参照)

TEXT列(およびその他のBLOB)の場合、MySQLは実際のBLOBへのポインターをバッファーに入れます。このようなポインタは数バイトのメモリしか使用しませんが、行がクライアントに渡されるときに実際のTEXTコンテンツをディスクから取得する必要があります。VARCHAR列(およびBLOB以外のすべて)の場合、MySQLは実際のデータをバッファリングします。ほとんどのテキストはほんの数バイトを超えるため、これは通常、より多くのメモリを使用します。計算列の場合、MySQLは、VARCHARの場合と同様に、実際のデータもバッファリングします。

これに関するいくつかの注意事項:技術的には、BLOBはクライアントに渡されるときにもバッファリングされますが、一度に1つだけであり、大きなBLOBの場合は完全ではない可能性があります。このバッファは各行の後に解放されるため、これによる大きな影響はありません。また、BLOBが実際に行の残りの部分と同じページに格納されている場合、BLOBはVARCHARのように扱われる可能性があります。正直なところ、1回のクエリで大量のBLOBを返す必要がなかったため、試したことはありません。

次に、(現在編集されている)質問に実際に答えましょう。

ページ1。ユーザーの概要と短いブログ投稿スニペット。

あなたのオプションはほとんどこれらのクエリです

SELECT userid, post_title, LEFT(post_description, 250) FROM `table_method_1`  <-- calculated based on a VARCHAR column
SELECT userid, post_title, LEFT(post_description, 250) FROM `table_method_2`  <-- calculated based on the TEXT column
SELECT userid, post_title, post_brief FROM `table_method_2`                   <-- precalculated VARCHAR column
SELECT userid, post_title, post_description FROM `table_method_2`             <-- return the full text, let the client produce the snippet

最初の3つのメモリ要件は同じです。4番目のクエリは必要なメモリが少なくなりますが(TEXT列はポインタとしてバッファリングされます)、クライアントへのトラフィックは多くなります。トラフィックは通常ネットワーク上にあるため(パフォーマンスの点で高価です)、これは他のクエリよりも遅くなる傾向がありますが、マイレージは異なる場合があります。TEXT列のLEFT()関数は、インラインテーブルレイアウトを使用するようにストレージエンジンに指示することで高速化される場合がありますが、これは、格納されるテキストの平均の長さに依存します。

2ページ。単一のブログ投稿

SELECT userid, post_title, post_description FROM `table_method_1` WHERE post_id=... <-- returns a VARCHAR
SELECT userid, post_title, post_description FROM `table_method_2` WHERE post_id=... <-- returns a TEXT

バッファリングされる行は1つだけなので、メモリ要件はそもそも低くなっています。上記の理由により、2番目の方法では、行をバッファリングするために必要なメモリはわずかに少なくなりますが、単一のBLOBをバッファリングするために追加のメモリが必要になります。

どちらの場合でも、単一の行のみを返すselectのメモリ要件については気にしないと確信しているので、実際には問題ではありません。

概要

任意の長さのテキスト(または数キロバイト以上を必要とするもの)がある場合は、TEXT列を使用する必要があります。それが彼らの目的です。MySQLがこれらの列を処理する方法は、ほとんどの場合有益です。

日常の使用で覚えておくべきことは2つだけです。

  • 実際には必要ない場合は、TEXT列、BLOB列、および大量のデータが含まれている可能性のある(もちろん、VARCHAR(10000)を含む)他のすべての列を選択しないでください。必要なのがいくつかの値である場合の「SELECT*FROMwhatever」の習慣は、データベースに多くの不必要なストレスをかけます。
  • TEXT列または他のBLOBを選択するとき、選択で一時テーブルが使用されていないことを確認してください。EXPLAIN疑わしい場合は構文を使用してください。

これらのルールに固執すると、MySQLからかなりまともなパフォーマンスが得られるはずです。それ以上の最適化が必要な場合は、より詳細な情報を確認する必要があります。これには、ストレージエンジンとそれぞれのテーブルレイアウト、実際のデータに関する統計情報、および関連するハードウェアに関する知識が含まれます。私の経験から、私は通常、それほど深く掘り下げることなく、パフォーマンスの低下を取り除くことができました。

于 2013-03-08T21:36:23.420 に答える
2

方法 2 の方が見栄えが良いですが、HTML を保存している場合、post_brief は TEXT 列にすることもできます。純粋なテキストの場合は、すべてを 1 つの列に保存して使用できます

SELECT user_id, post_title, LEFT(post_description,255) AS post_brief FROM blog_table.

MySQL 5.6 を検討してください。これははるかに高速であり、InnoDB で FULLTEXT インデックスを使用できるため、投稿を検索する場合に非常に役立ちます。

于 2013-02-21T09:58:39.543 に答える
1

オプション2も私には良さそうです。ブログ投稿は巨大になるため、その列に関数を適用するのにも時間がかかるはずです。

また、post_description のデータ型はblob/textにする必要があります。Blob 列は検索をサポートしていませんが、それはより良いオプションです。

2 つの列を持つことの唯一の欠点は、desc と brief の両方が同期していることを確認する必要があることです (機能としても作成できる場合があります)。

于 2013-03-15T17:11:05.290 に答える