自分探しの旅 2nd
はい!ということで、転職することになりました!!(どーん!)
面接が大の大の苦手な私が、まさか2度も転職することになるとは…人生何があるかわかりませんね、本当。
このブログを書いた人
前回転職した理由を振り返ってみよう
↓前回の転職ブログ
tomomo1015.hatenablog.com
「所属会社」という肩書きが無い貴方に、何ができるかを考えてみなさい。
端的に、これを探す旅に出ました。
詳細は↑のブログに書いてあるので割愛しますが、私が自分探しの旅に出ていました。
月に多くて、1回。
これは、前職の私の、TerraTermの起動回数です。
その中でもSQLを打つなんて言うのは、年に数回。
私がSQL打つなんていうのは、チームメンバーの仕事を奪ったに近かった。
検証作業なんてメンバーに任せて、一つでも案件を生み出すプレゼンを、見積もりを。
そんな私が、「エンジニア」なんて肩書を持っていることが嫌いでした。
何もできないじゃない。人に指示するばっかりで。
結局私には何も、なんにもできないじゃない。
何もできない私は、実質それまで築いたPMの経歴をすべて捨てて、DBエンジニアの道を選びました。
どれほどPMとしての成果を評価されても、嬉しくなかった。
「所属会社」という肩書きも、それまでの経歴も無くした私が、何が出来るのか知りたかったからです。
1回目の転職してみて
オンプレしか触ったことがない人が、AWSのイベントに出るまで
オンプレしか触ったことがない私を、上司と同僚達は、驚くほど丁寧にオンボーディングしてくれました。2on1も頻繁に実施してくれ、コロナ禍で直接話すことはなくとも、多くの方と接点を持ってくれました。
入社直後に発生したトラブル対応もさせて頂き、あれほど憧れていたSQL実行を頻繁にできるようになりました。
特にAWSに関しては2度も登壇の機会を頂きました。AWSさん、声かけて頂きありがとうございました!
でも登壇するっていうのは私の希望じゃなくて、某上司がかっ…イヤナンデモナイデス
1つ目のオンプレからAmazon RDS for OracleDBへの移行は、AWS触って初めて実施したプロジェクトでした。本格的に触って、約半年程度でしょうか。
もちろんオンプレ時代の経験も生きたとは思いますが、上司と同僚達オンボーディングと、関係各社の支援無しにこれは、決して成し得られませんでした。
AWS Summitに出ろと言われた時は、信じられなくて3回ぐらいSlackを再起動させました。(事実)
2つ目のElastiCacheを使った事例は、転職後初の、PM兼DBエンジニアの仕事でした。
これも、当時のテックリードの多大な支援を頂いて成し得た成果ですが、このプロジェクトに過去携わってきた全て皆さんの挑戦無しには、この成果は生まれなかったと思います。誰一人、欠けてはできませんでした。今でも胸を張ってそう言えます。
憧れのDBRE
前職SIer時代、当時あわっちさんが書かれたDBREについて書かれたブログを拝見して、衝撃を受けました。
いつもインフラと開発の板挟み、問題出た時だけしか呼ばれないDBA。
DBREの、特にデータストア教育者でありメンターでありプラットフォーマーであるという点に、非常に魅力を感じました。
当時チーム内でこのブログを展開し、いつかDBREやってみたいね。なんて話をしていたんです。
そんな私が…DBREのSlackを立ち上げ、DBRE Summitというイベントに参加させていただく事ができました。
私が出て大丈夫なのこれ?!と、当日まで半泣きになっていたのですが、無事死亡話はできました。
ちょっと風変わりなコストの話をしてみましたが、いつかは技術メインの話をしてみたいな。
何より、あの私が。
DBエンジニアの前で、DBの話ができた。これが何より、嬉しかったです。
私、DBREみたいなこと、少しはできたかな?
結論
もうsqlplus打つの飽きたわ!と笑って言っていた時、私が涙目になっていたことを、多分同僚達は知らないでしょう。
それほど、嬉しかった。あの頃ずっと憧れていたSQLを打つということ、DBエンジニアとしての仕事ができたんです。
もちろん現実は泥臭すぎて笑っちゃいますが、それを笑えるほど楽しかった。
私の選択は、間違っていなかった。ここに来てよかった。
今もそう思います。
何一つ無駄な時間なんてなかった。
苦労したこと、失敗したこと、つまらなかったことも、何一つ無駄なことなんてなかったんです。
では何故、転職するのか?
それでも私は、マネジメントをする人間なんだなと気付いたから
誤解されないように、少し丁寧に書きます。
DBエンジニアとして転職しましたが、徐々にマネジメントの仕事を求められるようになりました。
前職で散々PMをしていた私からすると、PMまたはPLなんていうのは苦でもなんでもなく、仕事においては息をするようなものです。
誰もやらないなら喜んで、と手を上げました。
当時の上司は、私がPMから転身していたことを知っていたので、渋々、ごめんねという言葉を添えてくれていました。
ところがその上司が変わり、新しい上司が来ました。それは別段よくあることです。
新しい上司も非常にできる方で、私の特性をすぐ見抜きました。そして、DBよりマネジメントを主軸にしてほしいと言われました。
ここまで言うと、DBエンジニアの仕事ができないから辞める、と思われそうですが、違います。
だからこそ、PMとして転職することにしました。
結局のところ、私はPMの仕事も好きだったんです。
自分が調整することで、エンジニアが生き生きと仕事をしているのを見るのが、好きです。
彼らのパフォーマンスを最大に出させ、プロジェクトの目的を達成させることが、好きです。
そして彼らが目標を達成した、と喜んでいる姿を見るのが、大好きなんです。
過去の私には、技術という点での自信がゼロでした。
でも今は違う。小さいながらも、自分が得意な技術で、事例紹介できるような実績に携わることができました。
だからもう、自信のない以前の私じゃない。
揉めたら一番面倒なデータベース、それを鎮める事ができるPMがいてもいい。
DBエンジニア辞めるなんて、言ってないですよ?
だってDBは、どこにでもいるもの。
次に会うDBがどんな子か、楽しみにしてるね。
転職しなくてもできるけど、あえて転職という選択を選んだ理由
とはいえ現職でマネジメント(組織とプロジェクトどちらも)を求められたわけですから、転職しなくてもできます。
そこで、再度自分のキャリアプランを考え直しました。よくある、3年後5年後、自分がどうありたいのか。
もやもやしながら考えていた時、当時同僚だったある方に、こんなニュアンスのことを言われました。
「事業会社のエンジニアって、その事業がよほど好きか興味がないと、続かないよね。他でも大体同じことが出来るしね。」
それが、答えでした。
もちろん興味があって入りました。その点では嘘ではないです。
ただ、自分がこれまで携わった業界の中で、一番興味があるかと言われたら、NOでした。
だから、自分の一番興味のある業界に行く。そこで3年後こんなことをして、5年後にはこんなことしよう。
そんなキャリアプランを書いてみたら、すぐ書けました。くどいですがそれが、答えでした。
あとはこの判断軸です↓
ありがたいことに、過去携わった業界の中で一番興味があった業界の会社に、拾っていただくことができました。
実は今でも半信半疑で、未だ実感ゼロです。え、私、転職するんだよね?
子育てと年齢という壁
最後に。少し現実的なことを書きます。
子育てが少し山を超えたという点です。
転職時子供が3歳、まだまだ手のかかる年齢でした。今は小学生になり、一人で朝登校できるようになったり、自分でできることが増えてきたことで、子育てが少し落ち着いてきたという点があります。その為、仕事のセーフを少し外すことにしました。
いやまぁ、別の苦労増えましたけど…宿題とか、習い事とか(以下略)
次に、年齢です。
数えるといい年齢になってしまったんですよ、私。つい最近まで新人扱いだったのなぁ(落ち着け)
転職するかもしれない、と考えた時、どうしても年齢という壁を意識する必要がありました。
もう転職できない、とは考えていません。ですが自分が選考者だった時、後何年働いてくれるかなと言うのはどうしても考える訳で、逆の立場でそれを考えると、これがラストチャンスなのかなと考えたのです。
所属会社の肩書のない自分に、何が出来るのか
こうしてPMとして転職することができたのは、新卒入社したSIer時代の経験のおかげです。
あの頃苦労したこと、不安だったこと、辛かったこと。全部全部、今の私に繋がっています。
所属会社の肩書のない自分に、何が出来るのか。
結論、自分では何もできないです。助けてくれる人がいるから、私は何かを成し得ることが出来るだけです。
それが、私の答えです。
その「助けてくれる人」で一番助けてくれる人に、過去の自分がいます。
過去のキャリアは何一つ無駄にならない、未来を作ってくれるものなんだよ。
どうか自信を持って。大丈夫、ちゃんとDBエンジニアできたよ。
自分探しの旅、次の道に行きます
突然なんですけど、私、スレイヤーズってアニメが好きなんですよ。
特にNEXTの主題歌「Give a reason」って曲が大好きで。
あの歌は躓いた時に大抵聞くんですが、こんなフレーズがあります。
ゴールに もたれたりしない
たとえ辿り着いたって
新しい夢がきっと
私の背中押すから
3年前に目指したゴール、 100点ではなかったけど辿り着いたので。
次の新しい夢に背中をバシバシ押してもらいながら…
自分探しの旅に、また行ってきます!!!
Amazon RDS for OracleでインストールされているRDS固有のプロシージャリストを調べる
完全にメモです。
Amazon RDS for Oracleには、標準OracleDBには無いrdsadmin系のプログラムが導入されています。
これ、想定外のタイミングで増えたりするので調べる方法ないかなーと思ったら、なんとこれでわかるそうです。
desc rdsadmin.rdsadmin_util;
これで調べられます。
出力例(内容載せると怒られそうなので、XXXXしています)
SQL> desc rdsadmin.rdsadmin_util; PROCEDURE ADD_INST_STORE_TEMPFILE 引数名 タイプ In/Out Default? ------------------------------ ----------------------- ------ -------- XXXXXXXX VARCHAR2 IN PROCEDURE XXXXXXXXX
あくまで2023年12月時点の情報なので、今後なくなるかも知れないです。
その場合怒っちゃだめ。マネージドですからね。
どこぞの統計アドバイザ止めたいとか、SYSでやりたいことはRDSの場合はこれらプロシージャを利用して実行するわけですが。
偶にバージョンによって導入されていなかったりしますので、その場合はこちらのコマンドで調べて、サポートの方に相談してみてください。
メモ書きでした!
MySQLの勉強をリファレンスを読みながらやってみることにした話
この記事はMySQL Advent Calendar 2023 12日目の記事です。
昨日は山崎さんの 2023年にMySQL Shellのデータダンプユーティリティにwhereオプションが追加されました+α でしたね。
MySQL Shell 8.0.32からdumpに様々な機能が搭載されたとのことですが、それが山崎さんのリクエストで実現した!というのが胸熱でした…!
機能がない場合は、ナイナイと文句言う前にコミュニティに声を上げることが大切ですね。しみじみ。
さて今日は、技術的な話でなくてポエムです。
タイトルの通り、「MySQLの勉強をリファレンスを読みながらやってみた」という話です。
MySQLの勉強をしようと思った背景
そもそも私がMySQLを触ったのは、とあるシステムで相談を受けた際だったと思います。
当時は100% OracleDBでした。そんな人間が、MySQLでの問題を相談を受けてた訳ですが。
MySQL固有の問題ではなくRDBMSの一般的な問題だったのもあって、「なんとなくこうかな?」という回答で解決できました。
ディスクとかCPUとかそうゆう類だと思ってください
そんな感じで「なんとなく対応した」が「MySQLも出来るよねあの人」になっていってしまったのです。
はっきり言いますが、いま現時点でも私のMySQLレベルは非常に低いです。
2-3年MySQLを使って開発されてる若手の方の方が、よっぽど知っていると思います。
ところが俄な知識で ちょっとInnoDB Clusterの案件 とかやってみたせいで、なんかMySQLできます体になってる。
まぁOracleDBも全然わからないんですけどね?
まずい。これはまずいぞ私!!
ということでMySQLの勉強を、いちからちゃんとしようと思ったのが、今年のきっかけです。
何故マニュアルを?
ここで普通、本を買います。ええ、本は持っております。そしてこの本、めちゃくちゃ良いですおすすめです。
ただ、あえてのリファレンスを読むことにしました。その理由が以下です。
- 網羅性:リファレンスが全てである。網羅性においてはリファレンスこそが最もカバー範囲が広い内容である
- 正当性:(ドキュメントBugもあるが)基本的に書かれている内容は正しい
- 汎用性:オンラインでどこでも読める。「これ、あそこに書いてあったよね?」がわかるだけでも、MySQL力は上がる
- 読解力:マニュアルを読める力も、エンジニアにとっては必要
一つずつ、私の考えを書いていきます。
網羅性
これに関しては説明不要かと。リファレンスは開発者が記載しているドキュメントであり、製品が具備している機能が記載されています。
もちろん本にも様々な機能が書かれていますが、あくまで「よく使われる」機能であって、網羅性はリファレンスの方が当然広いですよね。
今回、MySQLのもつ機能を広く知りたいという想いもあって、この網羅性からリファレンスを読み始めることにしました。
正当性
ドキュメントBugがあるのはわかっていますが、それでもこのドキュメントは開発者が記載しているドキュメントです。
「こうゆう機能を作ったよ!」という情報を学ぶ際、最も正しく学べると考えました。
汎用性
ちょっと別の話になりますが、皆さんOracle Master Plutinumという怖い試験をご存知ですか。
OracleDBの実機試験ですね。指示通りに構築やセットアップを行っていかなければならないのですが、参考できる文献はリファレンスのみです。
あの試験で大事なポイントに「リファレンスのどこに何の機能、コマンド、制限事項が書いてあるか知っておくこと」という点があります。
問題発生時、障害発生時。あれ、ここどうゆう仕様だっけ?を、すぐリファレンスマニュアル見て調べられる。
こうゆうことがしたいんだけど、できないよねー…に対して、そういえばこんな機能あったよね?を思い出して、リファレンスみて調べられる。
それだけで結構、片付く課題が増えると考えています。まぁ今は、AIに探してもらうほうが早そうですけどね。
読解力
リファレンスの日本語難しいんですよ助けて←
ということで、リファレンスの日本語読解力をあげようと思いました。解釈間違えると、誤ったパラメータ値を設定してしまうこととかあるんです。私はあります。ごめんなさい。
解釈が難しくて、時々慣れない英語版見ます…それもまた、勉強…
何故ブログに書いた?
そう考え始めたマニュアル読む会ですが、何かしらアウトプット出さないとだめな人間なので、自分のメモ用にブログに書き始めました。
今日はここ読む、こんな機能あるんだ、これなんだろー?という、完全に独り言ですね。
気軽に書けるように、本当に独り言形式、メモにしました。
内容は誰かに見せる用ではないので、読みやすさゼロです。というか、誰も読まないよねと思って書いてました。
やってみて、どうだった?
まさかの、読んでくださる方がいらっしゃるんですよ!!神ですか!優しさの神様がいるんですか!!
間違っている点を指摘していただいたり(土下座)、ここわからないって呟いたのを教えてくださったり(感謝感激で半泣きになって拝見していました)
個人的に この投稿の イテレータエグゼキューター について、論文や紹介されているブログを教えてくださったりして、非常に学びになりました。
また、実行計画について書かれていたので「Oracleと比較してみようかな?」と思いやってみたところ、はてぶさんの注目ブログに載せて頂いたりしまして(感謝)
tomomo1015.hatenablog.com
当初考えていた学びの3倍以上の学び、経験をさせていただきました。
見ていただいた方々、そしてコメント頂いた皆様に心から感謝を。ありがとうございます!!
MySQLのリファレンスを読む 1章の続き その⑨
まいえすきゅーえりたい ぽすぐれない おらくるってる(狂ってる)tomoです。
やるぞやるぞ詐欺を経て、ようやく戻ってまいりました!
あぁMySQL、寂しかったよMySQL。
ちょっと登壇したり、社内のごにょごにょしたり、Oraくるってました。
speakerdeck.com
もうMySQLばっかりで、ええんやで←
今日もこれの続き。
tomomo1015.hatenablog.com
今日読んだもの
1.3 MySQL 8.0 の新機能
ここの「SQL 標準の明示的なテーブル句およびテーブル値コンストラクタ」から。
--- 以下2つは同じSQL文 SELECT a FROM t1 WHERE b > ANY (TABLE t2) ; SELECT a FROM t1 WHERE b > ANY (SELECT * FROM t2) ;
--- (i_a, i_b, i_c) にはインデックスマージが使用されるパターン SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c)*/ * FROM t1 WHERE b = 1 AND c = 2 AND d = 3;
- JSON_VALUE() 関数が増えたよ。これはJSON カラムのインデックス付けを簡略化するためのもの…ほうほう?
- これ、マニュアルに書いてあるSQLをそのまま流してみたんだけど…あれ?8.1だから??
- このあたり正直不慣れすぎるので、ちょうどいいつまり方したかも。
mysql> SELECT version(); +-----------+ | version() | +-----------+ | 8.1.0 | +-----------+ 1 row in set (0.00 sec) mysql> mysql> CREATE TABLE t1( -> j JSON, -> INDEX i1 ( (JSON_VALUE(j, '$.id' RETURNING UNSIGNED)) ) -> ); Query OK, 0 rows affected (0.03 sec) mysql> INSERT INTO t1 VALUES ROW('{"id": "123", "name": "shoes", "price": "49.95"}'); Query OK, 1 row affected (0.01 sec) mysql> SELECT name, price FROM t1 -> WHERE JSON_VALUE(j, '$.id' RETURNING UNSIGNED) = 123; ERROR 1054 (42S22): Unknown column 'name' in 'field list' mysql> select * from t1; +--------------------------------------------------+ | j | +--------------------------------------------------+ | {"id": "123", "name": "shoes", "price": "49.95"} | +--------------------------------------------------+ 1 row in set (0.00 sec) mysql>
という、ここで詰まって今日はタイムアウト。明日このエラーの原因調べようと思います…!
MySQLとOracleの実行計画を比較してみた
まいえすきゅーえりたい ぽすぐれない おらくるってる(狂ってる)tomoです。
今日はいつものMySQLリファレンスを読むではなく、夏休みの宿題にしていたこれをやってみます。
MySQLとOracleDBの実行計画を比較してみた
さて同じようなテーブルで同じデータを載せて。
実行計画を取ってみた時、どのくらい情報量が違うのか簡単に違いを見てみましょう。
前提として、以下をご認識ください。
- 一方はOSSのDBエンジン、もう一方はガチガチ商用DBエンジンです。情報量が違うのは当たり前であって、良し悪しを比較したいのではありません。そして製品比較をしたいのではありません。いつも商用DBメインで使っているエンジニアが、OSSのDBにこうゆう情報も出してほしいな!というのをお願いしたいと思っていて、それを考える元ネタメモだと思ってください。
- OSSでこれだけの情報出せるMySQLや、今回紹介しませんがPostgreSQL等、OSSのDBは本当すごいです。OSSに関わるすべての皆様に対する賛辞を最初にお伝えしたいです。あのワンコマンドでこれだけの情報出すだなんて、すごすぎるかっこよすぎる!最高ですね!!
- 性能検証が目的ではありません
- HWスペックが違いますし、そもそも性能検証結果を出すのはNGです
- 検証対象DB
- DDLと投入データ
- SELECT文
- MySQLの実行計画
- OracleDBの実行計画
- MySQLの実行計画(EXPLAIN ANALYZE)に欲しい情報
- 余談:OracleDBで更に詳細なSQLのコスト情報を出す
検証対象DB
MySQLはコミュニティ、バージョンはできたてほやほやの8.1.0です。
8.1おめでとう!待ってたよー!!
mysql> SELECT version(); +-----------+ | version() | +-----------+ | 8.1.0 | +-----------+ 1 row in set (0.00 sec)
OracleDBのバージョンはエンジンがEnterprise、現時点で long support version の19シリーズの19.9です。
私の相棒ちゃんです←
TOMO@oracle > select banner_full from v$version; BANNER_FULL ------------------------------ Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 -ProductionVersion 19.9.0.0.0 経過: 00:00:00.01
DDLと投入データ
作ったTABLEはこんな感じ。意味のないカラムとかありますけど、見なかったことにしてください…
データはこんなの入れました。
MySQLでもOracleDBでも同じSQLで入れられます。
(このデータに「ん?」と思った皆さんは、握手)
--- m_custにデータを入れる insert into m_cust(cust_id,cust_flag) values('test-01', 1); insert into m_cust(cust_id,cust_flag) values('test-02', 1); insert into m_cust(cust_id,cust_flag) values('test-03', 1); insert into m_cust(cust_id,cust_flag) values('test-04', 0); insert into m_cust(cust_id,cust_flag) values('test-05', 0); insert into m_cust(cust_id,cust_flag) values('test-06', 0); --- m_cust_flagにデータを入れる insert into m_cust_flag(cust_flag, cust_gender,cust_country) values(1, 0, 'Hyrule'); insert into m_cust_flag(cust_flag, cust_gender,cust_country) values(1, 1, 'Hyrule'); insert into m_cust_flag(cust_flag, cust_gender,cust_country) values(0, 0, 'past-Hyrule'); insert into m_cust_flag(cust_flag, cust_gender,cust_country) values(0, 1, 'past-Hyrule'); --- m_cust_infoにデータを入れる insert into m_cust_info(cust_id, first_name,last_name, cust_gender) values('test-01', 'Swordsman', 'Rink', 1); insert into m_cust_info(cust_id, first_name,last_name, cust_gender) values('test-02', 'PhD', 'Purua', 0); insert into m_cust_info(cust_id, first_name,last_name, cust_gender) values('test-03', 'PhD', 'Robely', 1); insert into m_cust_info(cust_id, first_name,last_name, cust_gender) values('test-04', 'Zonai', 'Raule', 1); insert into m_cust_info(cust_id, first_name,last_name, cust_gender) values('test-05', 'Princess', 'Zelda', 0);
SELECT文
検索は以下のSELECT文にしてみました。
もうちょっと凝ったことがしたかったんですが、left joinとcase入れたから多少なにかエンジン側で工夫してくれたらいいな。
これもMySQLとOracleDB、同じSQL文で通ります。
select distinct c.cust_country as country, b.last_name as name, case b.cust_gender when 0 then 'female' when 1 then 'male' else 'seacret' end as gender from m_cust a left join m_cust_info b on a.cust_id = b.cust_id left join m_cust_flag c on a.cust_flag = c.cust_flag order by country;
MySQLの実行計画
さて、大好きな実行計画タイムです。
まずは普通のEXPLAINをしてみる。
mysql> EXPLAIN select distinct c.cust_country as country, b.last_name as name, -> case b.cust_gender -> when 0 then 'female' -> when 1 then 'male' -> else 'seacret' end as gender -> from m_cust a -> left join m_cust_info b on a.cust_id = b.cust_id -> left join m_cust_flag c on a.cust_flag = c.cust_flag -> order by country; +----+-------------+-------+------------+------+---------------+---------+---------+----------------+------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+---------+---------+----------------+------+----------+--------------------------------------------+ | 1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using temporary; Using filesort | | 1 | SIMPLE | b | NULL | ref | cust_id | cust_id | 42 | tomo.a.cust_id | 1 | 100.00 | NULL | | 1 | SIMPLE | c | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+------+---------------+---------+---------+----------------+------+----------+--------------------------------------------+ 3 rows in set, 1 warning (0.00 sec) mysql>
個人的な意見です
- 好きなところ
- 転記すると見辛いですが、すごく見やすい。特にどのTABLEを使っていて、どのKEYを使ったのかというのは本当にわかりやすい
- 余談ですが、私はMySQLのSELECT等の「アウトプットの見やすさ」が何より好きです
- 各「select_type」に対して何をしたのかというアクションがわかりやすい
- 例えばaテーブルに対しては一時テーブル使って、Fileソートしたんだな、とか
- partitionsじゃないよ、typeは何したよ、Index何使ったよ等などが明瞭ですよね
- 転記すると見辛いですが、すごく見やすい。特にどのTABLEを使っていて、どのKEYを使ったのかというのは本当にわかりやすい
- 苦手なところ
では、ANALYZEしてみよう。
mysql> EXPLAIN ANALYZE select distinct c.cust_country as country, b.last_name as name, -> case b.cust_gender -> when 0 then 'female' -> when 1 then 'male' -> else 'seacret' end as gender -> from m_cust a -> left join m_cust_info b on a.cust_id = b.cust_id -> left join m_cust_flag c on a.cust_flag = c.cust_flag -> order by country; +------------------+ | EXPLAIN | +------------------+ | -> Sort: c.cust_country (actual time=0.12..0.12 rows=5 loops=1) -> Table scan on <temporary> (cost=4.52..7.13 rows=20) (actual time=0.108..0.109 rows=5 loops=1) -> Temporary table with deduplication (cost=4.38..4.38 rows=20) (actual time=0.106..0.106 rows=5 loops=1) -> Left hash join (c.cust_flag = a.cust_flag) (cost=2.38 rows=20) (actual time=0.0575..0.0833 rows=10 loops=1) -> Nested loop left join (cost=2.5 rows=5) (actual time=0.021..0.0437 rows=5 loops=1) -> Table scan on a (cost=0.75 rows=5) (actual time=0.00607..0.00799 rows=5 loops=1) -> Index lookup on b using cust_id (cust_id=a.cust_id) (cost=0.27 rows=1) (actual time=0.00568..0.00674 rows=1 loops=5) -> Hash -> Table scan on c (cost=0.13 rows=4) (actual time=0.019..0.0245 rows=4 loops=1) | +------------------+ 1 row in set (0.01 sec) mysql>
- 好きなところ
- 「a=cのJOINはhashで、Nested loopして突き合わせさせたんだな」という「DBがどうやってTABLEを検索しに行ったのか」の流れがわかりやすい
- SQL作成時に脳内で描く「こうやって検索して〜」という流れとDBが実際内部で実施した流れが同じなのか、違うのかがわかる
- 遅い時って大抵「あれここなんでこの順序でやった?」ということが多い
- costやactual time、loop等でどの処理で時間かかったのかわかりやすい
- 既存のEXPLAINでも分かるんですが、「ここのhash join異様に時間かかっているな。ということはこのjoinの箇所が問題だな」という問題箇所の特定がより明確
- 「a=cのJOINはhashで、Nested loopして突き合わせさせたんだな」という「DBがどうやってTABLEを検索しに行ったのか」の流れがわかりやすい
- 苦手なところ
- 既存の「EXPLAIN」と比較すると、情報がごちゃごちゃして読み辛い
- もちろんちゃんと読めるですが、元々が表形式で見やすかっただけに、余計見辛い感じがする
- 既存の「EXPLAIN」と比較すると、情報がごちゃごちゃして読み辛い
さて、TREEにした場合どうなるかというと
mysql> EXPLAIN FORMAT=TREE select distinct c.cust_country as country, b.last_name as name, -> case b.cust_gender -> when 0 then 'female' -> when 1 then 'male' -> else 'seacret' end as gender -> from m_cust a -> left join m_cust_info b on a.cust_id = b.cust_id -> left join m_cust_flag c on a.cust_flag = c.cust_flag -> order by country; +------------------+ | EXPLAIN | +------------------+ | -> Sort: c.cust_country -> Table scan on <temporary> (cost=4.52..7.13 rows=20) -> Temporary table with deduplication (cost=4.38..4.38 rows=20) -> Left hash join (c.cust_flag = a.cust_flag) (cost=2.38 rows=20) -> Nested loop left join (cost=2.5 rows=5) -> Table scan on a (cost=0.75 rows=5) -> Index lookup on b using cust_id (cust_id=a.cust_id) (cost=0.27 rows=1) -> Hash -> Table scan on c (cost=0.13 rows=4) | +------------------+ 1 row in set (0.00 sec) mysql>
ちょっと情報がコンパクトになりましたね?
ざっと動きを見るならTREEの方が見やすそうですが、actual time、loop等が抜けているのでリリース前に実行計画見るのであればTREEでない方がいいかも。
OracleDBの実行計画
さて、同じようなTABLEで同じデータが入ったOracleDBで、実行計画を取ってみます。
TOMO@oracle > select distinct c.cust_country as country, b.last_name as name, case b.cust_gender when 0 then 'female' when 1 then 'male' else 'seacret' end as gender from m_cust a left join m_cust_info b on a.cust_id = b.cust_id left join m_cust_flag c on a.cust_flag = c.cust_flag order by country; 実行計画 ---------------------------------------------------------- Plan hash value: 3044041168 -------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2040 | 173K| 10 (20)| 00:00:01 | | 1 | SORT UNIQUE | | 2040 | 173K| 9 (12)| 00:00:01 | |* 2 | HASH JOIN RIGHT OUTER| | 2040 | 173K| 8 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL | M_CUST_FLAG | 409 | 12270 | 3 (0)| 00:00:01 | |* 4 | HASH JOIN OUTER | | 409 | 23313 | 5 (0)| 00:00:01 | | 5 | TABLE ACCESS FULL | M_CUST | 82 | 1640 | 2 (0)| 00:00:01 | | 6 | TABLE ACCESS FULL | M_CUST_INFO | 409 | 15133 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("A"."CUST_FLAG"="C"."CUST_FLAG"(+)) 4 - access("A"."CUST_ID"="B"."CUST_ID"(+)) 統計 ---------------------------------------------------------- 49 recursive calls 0 db block gets 117 consistent gets 1 physical reads 0 redo size 853 bytes sent via SQL*Net to client 676 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 11 sorts (memory) 0 sorts (disk) 5 rows processed
前半の「統計」までを、MySQLには無いなと思うところ見ていきましょう。
くどいですが、これは有償DBです。情報量が多いのは当たり前なので、良し悪しを言いたいのではありません。
MySQLを見た後にみると、「MySQLのEXPLAINとEXPLAIN ANALYZEのいいとこ取り」をしているように思います。
- 実行計画に対し一意性(hash value値)がある
- Name欄に「どのTABLEに対して」「どうやって処理をして」「どの順に」が明確である
- ここはMySQLの既存のEXPLAINの好きなところと、EXPLAIN ANALYZEの好きなところが両方入ってる
- hash joinの箇所等に「どのTABLEのどのカラムを、どうやって結合したよ」を*をつけて「Predicate Information」で欄外で詳細を記載してくれているので読みやすい
- 「"A"."CUST_FLAG"="C"."CUST_FLAG"(+)」 は、a.cust_falgに対し、c.cust_flagをjoinしたんだよ、という意味です。ちゃんとleft joinしたのね偉いこ
- 長文SQLだとこの「Predicate Information」は大量に出るので、注釈してくれていると読みやすいのです
- 「HASH JOIN RIGHT OUTER」は外部ハッシュ結合したよってことです。おや、DISK読み込みしたのかしら?とここで思ったりします
- 「"A"."CUST_FLAG"="C"."CUST_FLAG"(+)」 は、a.cust_falgに対し、c.cust_flagをjoinしたんだよ、という意味です。ちゃんとleft joinしたのね偉いこ
- Timeはミリ秒までなのでMySQLの「actual time」よりは精度は低い
MySQLに無いのは「統計」の箇所ですね
MySQLの実行計画(EXPLAIN ANALYZE)に欲しい情報
ということで単純比較してみました。
個人的な意見としては、
- 既存「EXPLAIN」は、流れは分かりづらいけれど、読みやすい
- 「EXPLAIN ANALYZE」は流れは分かるけど、読みづらい
なので、個人的に欲しいなとおもうのは
- 実行計画に一意性をもたせる値がほしい
- 「EXPLAIN ANALYZE」はOracleDBみたいに、元の「EXPLAIN」みたいに表形式にできないか
- 箇条書きみたいしているのが、分かるようで分かりづらい
- OracleDBにある「統計」みたいなのが出せないか
- 個人的にphysical readsとconsistent gets、sorts等はほしいなぁ
余談:OracleDBで更に詳細なSQLのコスト情報を出す
ここは余談ですが、こんなことすると「このSQLがCPUどのくらい使ったか」が詳細にとれます。
TOMO@oracle > select name,value from v$mystat my inner join v$statname snon my.STATISTIC# = sn.STATISTIC# where sn.name in ('parse time cpu','parse time elapsed','DB time','CPU used by this session') order by name; NAME VALUE -------------------- ---------- CPU used by this ses 52 sion DB time 207 parse time cpu 20 parse time elapsed 61
DB Time(DB CPUと待機時間の合計)が取れるのもいいですよね。
DB Timeは「津島博士のパフォーマンス講座 第17回 チューニングについて」に詳しく書かれているのでこちらをぜひ。
また、parseをどのくらいしているのかも分かるので、思ったより多かったりすると「あれおかしいな、なんでこんなにしてるの?」と疑ったりしてます。
MySQLのリファレンスを読む 1章の続き その⑧
まいえすきゅーえりたい ぽすぐれない おらくるってる(狂ってる)tomoです。
まずはこのブログ、拝見頂いてありがとうございます!
レスおっっそいかもですが、頂いたコメントに対してお返事したい所存ですので、是非気軽に「ここまちがってるでー」と指摘いただけると幸いです。
そして前回更新から、少し間があきました><
夏休み第一弾をしてきました!
お昼の進捗です𐩢𐩺 pic.twitter.com/zsMuKM7iQG
— tomo🫠 (@tomomo1015) 2023年7月26日
某避暑地に行ったのですが、全然涼しくなかった…今年の日本の暑さは本当異常ですね。
みなさま、ご自愛くださいませ。
で。でででで。
明後日から一週間、別の所に旅に行ってきますので、またまたしばしお休みになります。
ということで、今日も読んでいこー!!
今日もこれの続き。
tomomo1015.hatenablog.com
今日読んだもの
1.3 MySQL 8.0 の新機能
ここのEXPLAIN ANALYZE ステートメントから。
- これはEXPLAIN ANALYZE による情報の取得をちゃんと読んだほうがいい
mysql> EXPLAIN ANALYZE SELECT * FROM t1 JOIN t2 ON (t1.c1 = t2.c2)\G *************************** 1. row *************************** EXPLAIN: -> Inner hash join (t2.c2 = t1.c1) (cost=4.70 rows=6) (actual time=0.032..0.035 rows=6 loops=1) -> Table scan on t2 (cost=0.06 rows=6) (actual time=0.003..0.005 rows=6 loops=1) -> Hash -> Table scan on t1 (cost=0.85 rows=6) (actual time=0.018..0.022 rows=6 loops=1)
- クエリーキャストは、暗黙的型変換のことであっているかな?
- ↑に書いたEXPLAIN ANALYZE使うと、CASTしているところはcastと出る。わかりやすい。
mysql> EXPLAIN FORMAT=TREE SELECT * from d JOIN n ON d.dt = n.i\G *************************** 1. row *************************** EXPLAIN: -> Inner hash join (cast(d.dt as double) = cast(n.i as double)) (cost=0.70 rows=1) -> Table scan on n (cost=0.35 rows=1) -> Hash -> Table scan on d (cost=0.35 rows=1)
- TIMESTAMP および DATETIME のタイムゾーンサポートの箇所は「タイムゾーンオフセットを受け入れます」と書かれていて、ナンノコッチャーと思ったのですが例がちゃんと書いてあって。'2023-08-01 13:00:30+09:00' とかの「+09:00」のことなんですね。
- JSON_SCHEMA_VALID() について
- リファレンスの例がまたわかりやすいのだけど、エラーになった時「SHOW WARNINGS」で見れば理由をちゃんと教えてくれる。
- これ他でもあるよね?多分使えば出てくる所で使っていないかも。これも普段利用するようにしよう
- ON DUPLICATE KEY UPDATE について
短いですが、今日はここまで!
明日は「SQL 標準の明示的なテーブル句およびテーブル値コンストラクタ」から読みますよー!
MySQLのリファレンスを読む 1章の続き その⑦
まいえすきゅーえりたい ぽすぐれない おらくるってる(狂ってる)tomoです。
暑い日々が続いていますね。皆さんお元気ですか?
暑い日は川とか海とかで涼みたくなりますね。海…そう、海といえばイルカ!イルカといえばMySQL!!
さて、無理やり繋げた所で、今日のMySQLリファンレス読む時間です。
tomomo1015.hatenablog.com
はい、今日もこれの続きです。
一章長いけどくじけない。
今日読んだもの
1.3 MySQL 8.0 の新機能
ここのC API.の続きから。
- これはCのAPIですよね「ノンブロック通信用の非同期関数」というので、これはCのノンブロッキングソケットの話かな。というかCのAPIとかあるんだという薄い知識…いやぁRDBMSのほんの一部しか使えてないんだなぁと、反省。
- キャスト関数の話。CONVERT()は便利そうだなぁ。こうゆう小技があるのはMySQLすごいなと思う
- time_zone を使用したオプティマイザヒントはちょっと使い所がわからない
- redo ログのアーカイブは、Oracle感でていい感じだと思われ。大事ですよredoを長期保存、ロスなくできるようにするのって
- クローンプラグインは初めて聞いた!クローニングできるようになるのはすごいなぁ。
- ハッシュ結合の最適化については、詳細はさておきハッシュ結合を優先的に使うようになったこと(もちろん最適な場合)。きをつけないといけないのはjoin_buffer_sizeがハッシュ結合時に使用するメモリサイズになるので、join_buffer_sizeの値はちょっと多めに取ったほうが良さそう。 8.0.20 以降ではブロックのNESTED LOOPは使用されなくなったというのが驚き。かなり実行計画周り変わりそう。エグゼキューターのところはオプティマイザの後のInnoDBとやりとりするあれだとおもうんだけど、イテレータっていうどうゆうこと?集合体というか塊で処理するとか?いやぁここわからないな
- 分からないとTwitterで呟いたら教えて頂きました…嬉しい!!
- イテレータエグゼキューターはIterator Model(RDBMSのクエリ実行の概念の一つ)であり、個別でエグゼキューターが実施していた処理をまとめたもの。それがMySQLだと「Volcano iterator design」のことなんですね。API化したのかな、ほえええすごい!
- 複数値インデックスの話は、
JSON周りにも対応したと認識した