web-dev-qa-db-ja.com

データが行に収まる場合のVARCHARとTEXTのパフォーマンス

_mysql> desc temp1;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| value | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+

mysql> desc temp2;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| value | text | YES  |     | NULL    |       |
+-------+------+------+-----+---------+-------+
_

255-各行に 'a'文字(両方のテーブル)

_mysql> select * from temp1 limit 1;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| value                                                                                                                                                                                                                                                           |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

mysql> select * from temp2 limit 1;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| value                                                                                                                                                                                                                                                           |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
_

クエリテーブル1:

_select count(*) from temp1 where value like '%a';
_

クエリテーブル2:

_select count(*) from temp2 where value like '%a';
_

統計:

_No of records---temp1(varchar)---temp2(text)


2097152---------6.08(sec)--------6.91(sec)          
4194304---------12.42(sec)-------13.66(sec)
8388608---------25.08(sec)-------28.03(sec)
16777216--------52.82(sec)-------56.88(sec)
33554432--------1(min)50.17(sec)-1(min)59.36(sec)
_

私の質問:実行速度の違いはどのように説明できますか?

行の内容は両方のテーブルで同じです。

私が理解したように、VarCharおよびText列は、行サイズを超えた場合にのみコンテンツをオフページに保ちます。したがって、両方のテーブルの内容はpage size(16kb)のインラインデータになります。次に、このクエリ実行時間の違いの理由は何でしたか。

注:両方のテーブル列にはインデックスが付けられていません

_Row Format - DYNAMIC

Collation - UTF8mb3

Character set - utf8_general_ci

Storage engine -  innodb

Mysql - 5.7
_

参照リンク: https://stackoverflow.com/a/48301727/5431418

Update:同じフローで、両方のテーブルで5000文字( 'a')を使用して結果を試しました差が大きいです。

_2097152---------1(min)53.63(sec)--------2(min)4.66(sec)    
_

Update 2:同じフローで、両方のテーブルで2文字( 'a')を試しました。パフォーマンスの違いがあります

テーブルステータスの追加:

_mysql> select * FROM information_schema.tables  WHERE table_schema = "db67006db" and table_name = 'temp1';
+---------------+--------------+------------+------------+--------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------------+
| TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | TABLE_TYPE | ENGINE | VERSION | ROW_FORMAT | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | MAX_DATA_LENGTH | INDEX_LENGTH | DATA_FREE | AUTO_INCREMENT | CREATE_TIME         | UPDATE_TIME | CHECK_TIME | TABLE_COLLATION | CHECKSUM | CREATE_OPTIONS | TABLE_COMMENT |
+---------------+--------------+------------+------------+--------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------------+
| def           | db67006db    | temp1      | BASE TABLE | InnoDB |      10 | Dynamic    |   30625036 |            315 |  9659482112 |               0 |            0 | 425721856 |           NULL | 2019-09-23 20:20:17 | NULL        | NULL       | utf8_general_ci |     NULL |                |               |
+---------------+--------------+------------+------------+--------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------------+
1 row in set (0.01 sec)

mysql> select * FROM information_schema.tables  WHERE table_schema = "db67006db" and table_name = 'temp2';
+---------------+--------------+------------+------------+--------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------------+
| TABLE_CATALOG | TABLE_SCHEMA | TABLE_NAME | TABLE_TYPE | ENGINE | VERSION | ROW_FORMAT | TABLE_ROWS | AVG_ROW_LENGTH | DATA_LENGTH | MAX_DATA_LENGTH | INDEX_LENGTH | DATA_FREE | AUTO_INCREMENT | CREATE_TIME         | UPDATE_TIME | CHECK_TIME | TABLE_COLLATION | CHECKSUM | CREATE_OPTIONS | TABLE_COMMENT |
+---------------+--------------+------------+------------+--------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------------+
| def           | db67006db    | temp2      | BASE TABLE | InnoDB |      10 | Dynamic    |   30922268 |            315 |  9753853952 |               0 |            0 | 425721856 |           NULL | 2019-09-23 20:20:12 | NULL        | NULL       | utf8_general_ci |     NULL |                |               |
+---------------+--------------+------------+------------+--------+---------+------------+------------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------------+
_
9
vinieth

ストレージに関して、InnoDBはVARCHARとTEXTの両方をインラインで格納する場合と同じように処理します。ただし、InnoDBからデータをフェッチする場合、サーバーはクエリの実行前にすべてのVARCHAR列にスペースを割り当てます。 TEXTカラムのスペースは、実際に読み取られた場合にのみ割り当てられますが、動的メモリの割り当てには時間がかかります。

https://forums.mysql.com/read.php?24,645115,645164#msg-645164

1
vinieth

いくつかのツールを使いましょう

最初のハンチ(以下を参照)はミスだったので、MySQL Workbench収集するには Query Performance Stats


最初の直感(結果なし)

ちょっとした考え:

  • [〜#〜] text [〜#〜]ディスク上の列サイズは2 + Nバイト。Nは文字列の長さ
  • [〜#〜] varchar [〜#〜]1 + Nバイト(N≤255の場合)または2 + Nバイト(256≤N≤65535の場合)

列内のテキストのサイズを256文字より大きくして、テストを再実行してください。潜在的には、より密接に一致するパフォーマンスで実行されます。

また、投稿する差異はレコードあたりのマイクロ秒で表されるため、多くのOSイベントが邪魔になるか、ソースに非常に単純なif (TEXT) {do some additional IO or Housekeeping}コードパスが存在する可能性があることに注意してください。

2
diginoise

TEXTタイプは異なる格納方法を持っているため、常にVARCHARより遅くなります。 TEXT以外のすべての列が異なる方法でテーブルに格納されたVARCHARフィールド。各TEXT値は個別のオブジェクトです。つまり、TEXT値で何かを実行したい場合、MySQLはそのオブジェクトを取得するために追加の操作を行います。

公式ドキュメント からの引用:

各BLOBまたはTEXT値は、個別に割り当てられたオブジェクトによって内部的に表されます。これは、テーブルが開かれたときに列ごとに1回ストレージが割り当てられる他のすべてのデータ型とは対照的です。

2
Maksym Fedorov

最初のケースの仮定は正しくありません。 Storage RequirementsTEXTに基づく255aの場合、VARCHARよりも1バイト多く格納しますしたがって、テーブルの33554432レコードの場合、メモリにロードするために33554432 more bytesが必要であり、時間遅延について説明します。

もちろん、これは5000aには適用されません。同じドキュメントに基づいて、サイズは同じですL + 2 bytes。しかし、私はその遅延の理由が Row Size Limits に記述されていると思います:

MySQLエンジンのより大きな行をサポートできる場合でも、MySQLテーブルの内部表現の最大行サイズ制限は65,535バイトです。 [〜#〜] blob [〜#〜]および[〜#〜] text [〜#〜]は、行のサイズ制限に向けて9バイトから12バイトしか貢献しないため、それらの内容は行の残りの部分とは別に格納されます。

行データの一部であり、個別に格納されること(格納された場所から取得するためにしばらく時間が必要)はまったく異なると思います。これは時間遅延を説明します。

0
Peter Darmis