銀河鉄道~ぽんこつエンジニアのブログ~

思いつきで書いています。

思い込み?

こんなことがあった。

ある機能を設計してたメンバーが、レビュー依頼を投げてきた。その中で、あるデータをテーブルAに登録するのか、テーブルBに登録するのかという問題があり、そのメンバーはテーブルAに登録するとしていた。

 

画面上の検索処理は、テーブルA、テーブルBのデータをそれぞれ起点として検索結果を表示する用になっており、どちらのテーブルに登録しても検索結果は変わらない。

 

違いは、テーブルAは登録処理が複雑で、登録データが画面上に表示されない。

 

テーブルBは登録処理がシンプルで、登録データは画面上に表示される。

 

ユーザーは私用説明の際、特に理由を説明せずに、テーブルAに登録して欲しいと言った。

 

メンバーはそれを聞いて、画面表示させたくないんだと判断しテーブルAに登録する設計を書いた。

 

レビュー時に、なぜテーブルAに登録するのか?と指摘したところ、メンバーは「テーブルAに登録してしまうと画面に表示されてしまいます。このデータは画面に表示したくないのでテーブルAに登録する設計にしました」と答えた。

 

僕「なぜ画面に出したくないって判断したのの?」

メ「ユーザーがテーブルAに登録して欲しいと言ったからです。」

僕「でもテーブルAの登録処理は複雑だから工数かかるよね?」

メ「でも画面に出したくない要望が優先ではないでしょうか?」

 

ユーザーは仕様を理解してるので、テーブルAに登録して欲しいと、わざわざ指定したのだから、画面に表示したくないんだろう、というのがメンバーの言い分。

 

ここには大きな落とし穴があって、ユーザーの気持ちを汲んでいるようにも見えるが、ユーザーは一言も「表示したくない」とは言っていない。メンバーは仕様を知ってるユーザーがテーブルAを指定しているんだから「表示したくない」に決まっていると思い込んでいる。

 

後日ユーザーに確認したところ、「たしかに表示しないのがベストだが、それで工数がかかるのならば、表示されるテーブルBに登録で構わない」との返答だった。

 

ユーザーとしては、画面表示<工数だった。

 

ここから言えることは、ユーザーがやりたいことがユーザーの最優先事項とは限らないということ。やりたいことを聞いた上で、そうやるとこうなりますけどいいですか?とメリットデメリットを聞いた上で判断してもらうことで、隠された最優先事項が見えてくる。

 

ユーザーがやりたいと言ってるからやるのではなくて、なぜやりたいのかを確認して、それを実現するためのメリットデメリットを見せた上で判断してもらうことがユーザーの最重要項目を把握することになるというお話。

 

 

 

 

映画 蜜蜂と遠雷を見た。

蜜蜂と遠雷が公開になったので見てきました。もともと気になってた作品だけど原作は映画を見たかったので、ガマンしていました。

映画を見たのは単純に主演が松岡茉優さんだったから(笑)

 

映画について。

個人的にずっとやりたかったピアノを習い始めたこともあり、このタイミングでこの映画を見たのは運命を感じました。

全編を通して音楽を奏でているような世界観の中、それぞれのピアノを描きつつも、根底にあるのは音楽を奏でる喜びとピアノが好きだっという心。それがテーマなのかな。と思いました。

 

この世界観をどう原作は表現したのか、さっそく原作を買わなくては。

ルパンの娘、読了。

f:id:giovanni0517:20190512123345j:image

 

とても面白かった。読後感がよくて、さわやかな気持ちの中に胸に残るものがあった。

泥棒一家と警察一家の恋物語+ミステリーという設定とストーリーは少しありがちで、物語の軸となる真相については、少し浅い気がしたけど、魅力的な登場人物が織りなすエンターテイメント性と、重くなりすぎず、仕事、恋愛、家族、大事なことは何なのか、というテーマ性をバランスよく描いていて、まさにルパンの映画を見てるような感覚に近い。

文章自体も読みやすくサクサク読み進めることができ、あまり本を読まない人にもおすすめできる、素直に面白かったと思えた作品でした。

sql_mode=STRICT_TRANS_TABLESにはまった話

MySQL5.6からSQLモードに「STRICT_TRANS_TABLES」がデフォルトで追加された。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 1.7.3.3 無効データの制約

これに関連して、プロシージャーの実行にはまったのでメモ。

環境は以下の通り。
・MySQL5.7
・クライアント:DBeaver
・ドライバ:JDBCドライバ

上記の環境で、DBeaverからストアドプロシージャーを適用し、実行すると「1366 (HY000): Incorrect string value」というエラーが発生。
ストアドプロシージャーの中で、int型変数を空文字('')で初期化しようとしていたことを怒られたのですが、
この現象、以前は発生していませんでした。

以前はと何が違うかというと、クライアントにMySQL workbenchを使用していました。
今回、諸事情により、クライアントをDBeaverに変更したことでこの事象が発生するようになりました。

調査した結果、DBeaverが使用しているJDBCドライバは、デフォルトで「STRICT_TRANS_TABLES」を使用する仕様(バグではなさそう。JDBC準拠?)になっており、
DBeaverでつなぐとセッション変数のsql_modeに強制的に「STRICT_TRANS_TABLES」が追加されていたことがわかりました。

MySQL Bugs: #24526: JDBC connector forces strict mode regardless of server setting
MySQL :: MySQL 5.1 リファレンスマニュアル :: 24.4.4.3 Java 、JDBC および MySQL のタイプ



しかも、もう一つはまった要因として、

「ストアドプロシージャーはCreateされた時のsql_modeで実行される」ということ。
このおかげで、いくらsql_modeを変更してもストアドプロシージャーの動きが変わらずエラーが出続けていました。


対応策として、以下のどちらかで、正常終了するようです。
JDBCドライバの接続のURLでjdbc:mysql://localhost/dbname?jdbcCompliantTruncation=false とする。
②セッション変数のsql_modeから「STRICT_TRANS_TABLES」を外した状態で、Create Procedure を行う。

ちなみにストアドプロシージャーが何のsql_modeに紐づいているかを確認するには、「SHOW CREATE PROCEDURE test1 \G」を実行するとよいです。

2019/09/05追記:
DBvearで①の設定をするためには、画面左端の「データペースナビゲーション」から
・該当のデータベースを右クリック→「編集 接続」
・開いた画面の「ドライバーのプロパティ」から「jdbcCompliantTruncation」を「false」に設定

MySQLパフォーマンスチューニング

その1 ボトルネックをみつける。
 Explainにを読んでどこに時間がかかっているか確認する。
 対象SQLのJOINを一つずつ外してみる。
 
その2 インデックスの検討。
 正しいインデックを使っているか。
 複合インデックスを使う。
 余計なインデックスを外す

その3 SQLの改善。
 サブクエリ、相関サブクエリをJOIN句に書き換える。
 LEFT JOINをExists文に書き換える。
 UNIONに書き換える。
 UNIONを外す。

その4 テーブルIOの改善。
 バルクインサートを使う。
 ファイルに書き出してからloadコマンドで取り込む。
 メモリテーブルに変更する。

その5 ロジックの検討。
 ぐるぐる系をどかん系に変更する。
 ワークテーブルを使う。

その6 ハードの改善
 メモリを増やす。
 性能の良いハードディスクに替える。

「羊と鋼の森」を読んだ。

バイブルにしたい本に出会いました。

文体が軽やかで、すらすら読めます。すらすら読めすぎて、気がついたら物語に引き込まれてました。

登場する主人公含め調律師4人は、それぞれ人間的で、それでいて魅力的です。

 

音楽、ピアノ、技術。苦悩、葛藤、憧れ。調律師という職業と主人公の半人前感が、エンジニアとして通じるものがあり、大事なことを教えてくれている気がました。

f:id:giovanni0517:20180605092210j:image

 

MySQL5.7でSELECTでロックが保持されてしまう

MySQLで日次バッチ実行中に、ロック待ちタイムアウトが発生しエラーとなりました。

java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction


原因はもう一つ裏で動いていたプロシージャーB。
そのプロシージャー内の処理で、以下のように記述をしていた箇所で、
なぜか行ロック(Sロック)を取得していました。

※カーソルによるループ

start transaction;

    if exists( select 1 from t_table where id = xxxx ) then
        /* 何らかの処理 */
    end if;

commit;


そのため、プロシージャーAでt_tableのid = xxxx のデータをUPDATEしようとしてロック待ちが発生し、
プロシージャーBの処理が長引いたため、タイムアウトが発生したようです。

select ~ for updateをつけているわけでもなく、単純なSELECT文なのに
Sロックを取得する理由がわからず...。
ちなみに分離レベルは、REPEAD TABLEでした。

いったん上記を下記のように修正したところ、ロックを取得することはなくなりました。

※カーソルによるループ

start transaction;

    select count(*) into HOGE from t_table where id = xxxx;

    if HOGE > 0 then
        /* 何らかの処理 */
    end if;

commit;