カジュアルにmysqlを落とす(Windows)

 先日の日記を書くために色々試している最中、あまりにも潔く mysql クライアントコマンドがさくっと落ちるので、発生条件を調べてみました。原因の特定には至っていません。

起こっていること

 Windows 10 のMySQLにて、mysql> プロンプトが出ているところで、単純なSELECT文を叩くと、無言で mysql があっさり落ちる。

mysql> SELECT * FROM n03_20_200101 WHERE RECID<20;

D:\>

使用データ

 先日の日記に書いた方法で取り込んだ「全国」のデータ(千葉県ではなく全国)の入ったテーブルを使って再現させることができます。ふつうに blob に放り込んだ値を HEX() 取ったりしても再現できるかもしれません。あ、blob である必要すらなくて、varchar とか text とかでも良さそう。

sakaik.hateblo.jp

発生条件

 どうやら、結果セットに含まれるカラム値の サイズまたは行のサイズが大きすぎるときに落ちる気がする。

mysql> SELECT RECID, LENGTH(HEX(SHAPE)),LENGTH(ST_AsText(SHAPE)) FROM n03_20_200101 WHERE RECID<20;
+-------+--------------------+--------------------------+
| RECID | LENGTH(HEX(SHAPE)) | LENGTH(ST_AsText(SHAPE)) |
+-------+--------------------+--------------------------+
|     0 |              57826 |                    55812 |
|     1 |              57634 |                    55409 |
|     2 |              27394 |                    26233 |
|     3 |              25666 |                    24674 |
|     4 |              47362 |                    45461 |
|     5 |             162210 |                   156154 |
|     6 |              52898 |                    50970 |
|     7 |              22178 |                    21460 |
|     8 |              35874 |                    34414 |
|     9 |              56194 |                    54322 |
|    10 |               1154 |                     1079 |
|    11 |                386 |                      344 |
|    12 |                642 |                      582 |
(略)

という情報を元に、RECID=5 の行のみを抽出してみる。

mysql> SELECT RECID, LENGTH(SHAPE), ST_AsText(SHAPE) FROM n03_20_200101 WHERE RECID=5;

D:\>

うん、落ちた。AsTextとかよくわかんないという人は、代わりに HEX(SHAPE)でも良いです。

閾値はどこに

 試行錯誤で範囲を絞ったので、以下のクエリで閾値捜索の最後のツメを。

SELECT RECID, LENGTH(SHAPE) a, LENGTH(ST_AsText(SHAPE)) b FROM n03_20_200101 
  WHERE LENGTH(ST_AsText(SHAPE)) >101296 AND LENGTH(ST_AsText(SHAPE))<101719 ORDER BY b ;
+--------+-------+--------+
| RECID  | a     | b      |
+--------+-------+--------+
|  99545 | 54305 | 101297 |
|  31451 | 53921 | 101297 |
|  59657 | 54225 | 101307 |OK
|  33266 | 53905 | 101361 |OK
|  48032 | 54049 | 101376 |NG
|  32203 | 54245 | 101437 |
|  11066 | 53301 | 101528 |
| 115488 | 49233 | 101718 |
+--------+-------+--------+
8 rows in set (28.52 sec)
SELECT RECID, LENGTH(SHAPE), ST_AsText(SHAPE) FROM n03_20_200101 WHERE RECID=33266; --OK
SELECT RECID, LENGTH(SHAPE), ST_AsText(SHAPE) FROM n03_20_200101 WHERE RECID=48032; --NG

1文字ずつくわえて見ると RECID=33266 は1文字でも加えたら、もうアウト。

SELECT RECID, LENGTH(SHAPE), CONCAT(ST_AsText(SHAPE),"X") FROM n03_20_200101 WHERE RECID=59657; --OK
SELECT RECID, LENGTH(SHAPE), CONCAT(ST_AsText(SHAPE),"X") FROM n03_20_200101 WHERE RECID=33266; --NG

 もしかして、出力カラムのサイズではなくレコードサイズなのでは?と 余計な取得列を少しずつ削って見ると、アタリ。他の列をすべて取得やめてみた結果が以下のもの。

SELECT CONCAT(ST_AsText(SHAPE),"XXXXX") FROM n03_20_200101 WHERE RECID=33266;  //OK
SELECT CONCAT(ST_AsText(SHAPE),"XXXXXX") FROM n03_20_200101 WHERE RECID=33266; //NG

 101361+5= 101366 byte まではOK、 101367バイトがNGと推測できそうです。

環境

 Windows 10 上に、MySQL Installer を使ってインストールしたもの。サーバは utf8mb4、
クライアントは cp932 でも utfmb4 でも発生。

mysql> status
--------------
mysql  Ver 8.0.22 for Win64 on x86_64 (MySQL Community Server - GPL)

Connection id:          30
Current database:       shptest
Current user:           root@localhost
SSL:                    Cipher in use is TLS_AES_256_GCM_SHA384
Using delimiter:        ;
Server version:         8.0.22 MySQL Community Server - GPL
Protocol version:       10
Connection:             localhost via TCP/IP
Server characterset:    utf8mb4
Db     characterset:    utf8mb4
Client characterset:    cp932
Conn.  characterset:    cp932
TCP port:               3306
Binary data as:         Hexadecimal
Uptime:                 3 hours 35 min 55 sec

Threads: 4  Questions: 117  Slow queries: 8  Opens: 190  Flush tables: 3  Open tables: 111  Queries per second avg: 0.009
mysql> status
--------------
C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe  Ver 8.0.22 for Win64 on x86_64 (MySQL Community Server - GPL)

Connection id:          29
Current database:       shptest
Current user:           root@localhost
SSL:                    Cipher in use is TLS_AES_256_GCM_SHA384
Using delimiter:        ;
Server version:         8.0.22 MySQL Community Server - GPL
Protocol version:       10
Connection:             localhost via TCP/IP
Server characterset:    utf8mb4
Db     characterset:    utf8mb4
Client characterset:    utf8mb4
Conn.  characterset:    utf8mb4
TCP port:               3306
Binary data as:         Hexadecimal
Uptime:                 3 hours 35 min 21 sec

Threads: 3  Questions: 113  Slow queries: 8  Opens: 190  Flush tables: 3  Open tables: 111  Queries per second avg: 0.008

結局分からず仕舞い

 現時点では Linux での動作を試していないので、これが Windows固有の現象なのか OS問わず発生するものなのか、分かりません。また、サーバのエラーログには記録なしで、一切の調査の足がかりがないのが、なかなか辛いですね。 まぁ、許容サイズを超えてエラーになるのはまだしも、ストンと落ちてしまう(接続が切れてしまう)のはいただけないなぁというのが私の感覚です。

 mysql params でざっと見てみた感じでは、それっぽい制限値はなさそうで、もにゃもにゃした気分です。
variable / MySQL Parameters


 これ、なんなんですかね。

その晩の追記

 その後いろいろ試してみたら、とりあえず上で書いた「閾値」候補は、一旦とりさげ。
例えばこれ、

SELECT RECID, LENGTH(SHAPE), ST_AsText(SHAPE) FROM n03_20_200101 WHERE RECID=33266; --OK
SELECT RECID, LENGTH(SHAPE), ST_AsText(SHAPE) FROM n03_20_200101 WHERE RECID=48032; --NG

実行順序を逆にしたら、NGだったやつは通って、OKだったやつがダメになりました。もう少し再現性ある内容を書く前に、yoku0825さんが試してくれたものを紹介すると、これだけで落ちるらしい。

mysql> SELECT repeat('a', 101367);
(結果が表示される)

mysql> SELECT 'a';

D:\>

 「次のクエリをダメにするクエリ」みたいなのがあるっぽいと考えたほうがいいのかも?


もちょっと追記。「次のクエリをダメにするクエリ」という観点で試してみたら、こんな落とし方もあります、

mysql> SELECT repeat('a', 100000);
(結果が表示される)

mysql> STATUS

D:\>