web-dev-qa-db-ja.com

base64でエンコードされたデータをBLOBまたはTEXTデータ型として格納する

MySQLのInnoDBテーブルには、base64でエンコードされた小さなjavascriptファイルの約10列と、base64でエンコードされたpng(<2KBサイズ)画像も保持されています。

挿入数は少なく、読み取りも比較的多いですが、その後の読み取りを避けるために、出力はMemcachedインスタンスに数分間キャッシュされます。

現在、これらの列にBLOBを使用していますが、パフォーマンスまたはスナップショットのバックアップの観点からTEXTデータ型に切り替えることには利点があるのでしょうか。

私の検索結果は、私のケースのBLOBTEXTがほぼ同じであることを示しており、実際にどのタイプのデータが実際に保存されるのか事前にわからないので、BLOB

この特定のケースに対するTEXT対BLOBの議論についての指針はありますか?

Base64でエンコードされたデータをデータベースに保存しないでください...

Base64は、印刷可能なテキスト文字のみを使用して任意のバイナリデータを表す方法です。これは、印刷可能なテキストのみを処理できるプロトコルまたはメディア(SMTP /電子メールなど)を介してそのようなバイナリデータを転送する必要がある状況向けに設計されました。データサイズが増加し(33%増加)、エンコード/デコードの計算コストが増えるため、どうしても必要な場合以外は避けてください。

対照的に、BLOB列の重要な点は、生のバイナリ文字列を格納することです。先に進んで、最初のBase64エンコードを行わずに、直接BLOB列にデータを保存してください。通常、ファイルバージョン/最終更新日、メディアタイプ、(JavaScriptソースなどのテキストファイルの場合は)文字エンコーディングなど、関連するメタデータを他の列に格納します。 MySQLが文字エンコーディングをネイティブに追跡するだけでなく、代替文字セットにトランスコードしたり、テキストを検査/操作したりできるように、テキストファイルにTEXTタイプの列を使用することもできます。必要に応じて(現在または将来)。

SQLデータベースが任意のバイナリデータを処理するためにBase64のような印刷可能なテキストエンコーディングを必要とするという(誤った)考えは、多くの誤った情報に基づくチュートリアルによって永続化されています。この考えは、SQLは他のコンテキストでは印刷可能なテキストのみで構成されるため、バイナリデータに対しても(少なくともデータ転送ではなくデータ転送のために)確実にそれを必要とするという誤った考えに基づいているようです。これは単に真実ではありません。SQLは、プレーン文字列リテラル(他の文字列と同様に適切に引用符で囲まれ、エスケープされている場合)を含む、さまざまな方法でバイナリデータを伝達できます。もちろん、データベースに(任意のタイプの)データを渡すための推奨される方法は、パラメーター化されたクエリを使用する方法です。パラメーターには、他のデータと同じくらい簡単にバイナリデータを含めることができます。

それだけの価値があるので、私は通常、このようなアイテムをRDBMSに保存することを完全に避け、代わりにfilesystemsと呼ばれる高度に最適化されたファイルストレージデータベースを使用することを好みますが、それはまったく別の問題です。

...パフォーマンス上の理由でキャッシュされない限り...

Base64でエンコードされたデータを保存することでメリットがある唯一の状況は、データベースからデータが頻繁に取得され、そのエンコードを必要とするプロトコルを介して送信される場合です。この場合、Base64でエンコードされた表現を保存すると、フェッチのたびに、それ以外の場合は未処理のデータに対してエンコード操作を実行します。

ただし、この意味では、Base64でエンコードされたストレージはキャッシュとして機能するだけであり、パフォーマンス上の理由で非正規化データを格納する場合と同様です。

...その場合はTEXTではなくBLOBである必要があります

上記で言及したように、TEXTBLOBの違いは、実際にはTEXT列がテキスト固有のメタデータ(文字エンコーディングおよびcollat​​ion)に対して、BLOB列はそうではありません。この追加のメタデータにより、MySQLはストレージと接続の文字セット(適切な場合)間で文字をトランスコードし、空想的な文字の同等性/順序付けを実行できます。

一般的に言えば、異なる文字セットで動作する2つのクライアントが同じbytesを表示する必要がある場合は、BLOB列が必要です。同じ文字が表示される場合は、TEXT列が必要です。

Base64では、これら2つのクライアントは、最終的にデータが同じbytesにデコードされることを検出する必要があります。ただし、エンコードされたデータには同じ文字が含まれていることがわかります。たとえば、'Hello world!''SGVsbG8Gd29ybGQh')のBase64エンコーディングを挿入したいとします。挿入アプリケーションがUTF-8文字セットで機能している場合、バイトシーケンス0x53475673624738676432397962475168がデータベースに送信されます。

  • そのバイトシーケンスがBLOB列に格納され、UTF-16で動作するアプリケーションによって後で取得される場合*同じバイトが返されます。これは'升噳扇㡧搲㥹扇全'を表し、目的のBase64エンコード値ではありません。一方

  • そのバイトシーケンスがTEXT列に格納され、後でUTF-16で動作するアプリケーションによって取得された場合、MySQLはオンザフライでトランスコードしてバイトシーケンス0x0053004700560073006200470038006700640032003900790062004700510068を返します。これは、必要に応じて、元のBase64エンコード値'SGVsbG8Gd29ybGQh'

もちろん、それでもBLOB列を使用して、他の方法で文字エンコードを追跡することはできますが、メンテナンスが複雑になり、意図しないエラーが発生するリスクがあるため、不必要にホイールを再発明します。


*実際には、MySQLは、ASCIIとバイト互換性のないクライアント文字セットの使用をサポートしていません(したがって、Base64エンコーディングは、それらのすべての組み合わせで常に一貫しています)。ただし、この例は、 BLOBTEXTの列タイプの違いを示し、TEXTが実際にエラーなしで機能するにもかかわらず、BLOBがこの目的のために技術的に正しい理由を説明します( MySQLが非ASCII互換のクライアント文字セットのサポートを追加するまでは最低限です)。

43
eggyal