エンジニアがtouchbar付きのMacを1ヶ月使った結果…

出オチのまとめ

touchbarがないと絶対に嫌だとはいえない。
まだまだ荒削りだなーと思う部分も多いが、たしかに未来を感じるが、ぶっちゃけなくてもいい。でも、touchbarがあるから嫌だってほどではない。

環境は以下

日常で使用しているアプリ

いいとこ悪いとこ

いいところ

Control stripは便利なところもある。

一発でスクリーンロック、ただしHighSierraからはショートカットキーあり 一発でスクリーンセーバー

touch IDはやっぱり便利

触るとロック解除されるのは便利。
間違ってロックしちゃった場合でも、指を使ってロック解除できる。 認識スピードはiPhoneと同じぐらいかな。とくに不満はない。

時たま便利な機能がある

SafariYoutubeを再生していると、Control stripに再生コントロールが展開される。シークバーの扱いとかは、ポインティングデバイスよりもtouchbarの方が上手なので、これは良い機能だと思う。
あんまり使ったことないけど、写真やフォトショなんかの明度や彩度の調整とかで使えるのも便利だと思う。

絵文字を使いやすい

gitのコミットログや、ちゃっとなんかに絵文字を使う際に、touchbarに絵文字が展開してくれるので選びやすい。

ちなみに…

Fnキーの段がないのは意外と慣れる

みんな気にしているFnキーがない(正確に言うとソフトウェアキーボードになる)って部分なんだけど、
意外となれます。touchbarがあろうがなかろうが、キーピッチは変わらないので、 指がF7とかF10の位置は覚えていてブラインドタッチも可能です。
なお、表示されているescキーの幅は狭かったりするんだけど、打鍵可能範囲はけっこう広めなので、適当に左端を叩けば(触れば)escの動作をしてくれます。

悪いところ

Control stripは便利だけど、Fnキーを常に表示にすると出てこなくなる

Touch Barショット 2018-02-03 17.44.23.png

Touch Barショット 2018-02-03 17.43.40.png

Touch Barショット 2018-02-03 17.48.06.png

Touch Barショット 2018-02-03 17.43.19.png

エンジニアが使うアプリでは恩恵を受けづらい

touchbar対応アプリケーションのほとんどが、映像、画像、音楽などのアプリケーションのようで、エンジニアがよく使うような、エディタやらツールやらのアプリケーションで対応しているものが少ない印象。
その場合は、touchbarの部分はOSと同じやつを展開するか、Fnを表示しておくので、その状態であれば、touchbarなし(物理Fnキー)の方が良いという気持ちもわかる。

バッテリーの持ちが悪い(気がする)

touchbarは専用のディスプレイ、専用のCPUで動作している。
そのために、touchbarなしのMacに比べてバッテリーの持ちが悪い(ベンチ取ってないんで気のせいかもしれないけど)体感では、10~20%ぐらい悪くなってそうな印象。 また、
バッテリーの持ちはともかく、冬でも本体がけっこう熱くなるのが微妙。 や、設計上問題ないのかもしれないけどけっこう発熱するので、心理的にw

mongoDB -製品に組み込むための覚え書き

MongoDBを使って見る

普段リレーショナルデータベースしかいじったことのない我々が、MongoDBを扱う必要がでてきました。MongoDBについて触れた記事が世の中にたくさんある中で、現在進行形で我々がどの様にキャッチアップしているかまとめて見ます。 MySQL技術者が5分でMongoDBを把握するために役立つとうれしいです。

MongoDBを扱う必要って?

開発の中でチャットボットを作ろう!となったときに、目をつけたフリーのフレームワークがMongoDBを作っていたのがそもそも。

いずれ、このフレームワークについても記事を書くと思いますが https://github.com/alfredfrancis/ai-chatbot-framework になります。

ユーザーのフリーテキスト入力文から必要なパラメーターを抜き出し、パラメーターが足りなかったらその入力を促すものです

とりあえずさわってみる。ではなく、製品に組み込むためには?

フレームワークを拡張して試作品公開をするに当たって、必要と思った情報は次の通りです。

  • インストールはどの様に行えばいいのか?
  • 本番と開発環境はどの様にわけるのか?
  • 起動・シャットダウン・再起動はどの様に行えばいいのか?
  • 接続をして、現在入力されている中身を見てみる

--- 今回はここまで

  • 初期ユーザーをどの様に作ればいいの?
  • ユーザーを増やすにはどうしたらいいのか?
  • パスワードはどうやって管理されているのか?
  • バックアップはどの様にとるのか?そもそもどんなバックアップの種類があるのか
  • リストアをするとしたらどうすればいいのか

じゃあ、リレーショナルデータベースとMongoDBの違いは?

本格的にソースコードを書いたりする様になると、便利なのが以下の資料。 どうしても、「MySQLでいうところのInsertをするにはどうしたらいいの?」とか、なれている言語で一旦やりたいことを置き換えてしまいます・・ 日本人が英語をしゃべる際も、なれないうちはまず日本語で文章を考えてから英語に訳すのに近いですね。

ということで、知りたかったことの回答をまとめていきます

基本的にはMongoDBのマニュアルを参考にまとめました。

https://docs.mongodb.com/manual

1.インストールはどの様に行えばいいのか?

エディションについて

  • MongoDB Community Edition : これが商用版ではないほう。GNU AGPL v3.0ライセンス。なので、商業利用は可能。ソースコードの改変をしたら公開が義務づけられる(参考:http://ounziw.com/2013/05/17/agpl/
  • MongoDB Enterprise (Advanced) : 商業向け。In-memory Storageでスループットが向上していたり、暗号化サポートもあったり、セキュリティも向上
  • MongoDB Compass : MongoDBのためのGUI. MongoDB Enterprise Advanced, mongoDB Professionalの一部として提供されているとのこと
  • MongoDB Compass Community : こちらはフリーのGUIツール。Compassの機能制限版
  • MongoDB professional が有償サポート付きの契約 ということで、スモールスタートで始めるに当たっては、MongoDB Community Edition + MngoDB Compass Communityを用意することにしました。

動作環境について

セットアップについては説明ドキュメントが数多くあるし、インストーラーに従えばインストールはハードルは低いです。

2.本番と開発環境をどの様にわけるの?

MySQLの対応表を見ると、MySQLのDatabaseに相当するものはMongoDBでもDatabaseと呼ぶとのこと。MySQLのtableに当たるものは MongoDBではCollectionという概念になります。

今回使うフレームワークも(当然のように)データはCollectionという集合に対して読み書きされるので、本番用のDatabaseと開発用のDatabaseという分け方で大丈夫そう

すなわち、セットアップについては本番用と開発用の二つのmongodプロセスをインストールする必要がないという理解です。まあ、試作品の段階ということもあり。

とはいえ、バックアップとか起動・再起動の単位、そしてユーザーはプロセス毎なので、製品版については、開発と本番はわけたいと考えています。

3. 起動・シャットダウン・再起動はどの様に行えばいいのか?

参考: https://docs.mongodb.com/manual/tutorial/manage-mongodb-processes/

起動の仕方

サーバー上で、

mongod 

でデフォルト状態で起動します

シャットダウンのしかた

データベースへの接続が必要なので、サーバー上で

mongo

で接続する(デフォルトだとユーザー名・パスワード省略でログインができる。できてしまう。。。)

その上で、以下を実行

> use admin;
> db.shutdownServer();
> quit();

4. 接続をして中身を見てみる?

まずは接続をしてみる

(もちろんサーバーにログインした上で)

 $ mongo 

(ローカルホストでユーザー認証を有効化していない場合)

サーバーに接続する場合

$ mongo [サーバー名] 

ユーザーを指定する場合

$ mongo [サーバー名] -u [ユーザー名] -p [パスワード]

現在存在するDBの一覧を表示する

> show dbs; 

DBに接続する

> use [DB名] 

コレクション (テーブルと同等の概念)の一覧を確認する

> show collections; 

とりあえずコレクションを覗いてみる

> db.[コレクション名].find({}); 

特定のフィールド値を持つものだけを抽出する

> db.message.find({user_id:”test”}) 

以下、messageというコレクションを例にします

さらに、特定のフィールド (例:user_id, bot_message) の値のみを抽出する

> db.message.find({},{user_id:1, bot_message:1, _id:0})

そして、重複しないフィールド値を取得する (distinct)

> db.message.distinct(“user_id”) 

2重引用符が必要なので注意

以下、次回に続きます

5. 初期ユーザーをどの様に作ればいいの?

6. ユーザーを増やすにはどうしたらいいのか?

7. パスワードはどうやって管理されているのか?

8. バックアップはどの様にとるのか?そもそもどんなバックアップの種類があるのか

9. リストアをするとしたらどうすればいいのか

ひとまず入れるべし!!オススメのWordpressプラグイン6選

Hello. スマイルです。日本人です。

今回はWordpressでこれからサイト作ってみたいなーという人向けに、 とりあえずこれ入れとけば良いっしょ!っていうプラグインを紹介します。

プラグインをうまく使えば、0から構築するよりもかなり簡単で早く、良い感じのサイトが出来ちゃいます♫

ちなみにプラグインのインストール方法についてはここで詳しく説明しませんが、 管理画面に入って 左のメニュー「プラグイン」クリック ↓ 左上の「新規追加」クリック ↓ プラグインの検索画面になるので見つけたいプラグイン名を検索すると出て来ます (インストール後に「有効化」ボタンのクリックも忘れずに)

※以下で紹介するプラグインの詳しい設定方法などはプラグイン名で検索するとたくさん出てくるので、それについてもここでは説明しません!

All In One SEO Pack

検索エンジンに「このサイトはこんなサイトですよー」と教えるためにソース内で色々やらないといけないことがあるんですが、 このプラグインを入れて、プラグインの設定画面で適当に入力するだけでうまくその辺やってくれます!

まずは最初これ入れるっしょ!みたいなレベルで有名なプラグインです。

https://ja.wordpress.org/plugins/all-in-one-seo-pack/

Contact Form 7

お問い合わせフォームを超簡単に実装できるプラグインです。

ショートコードという、このコード入れたらその部分にこれ出すよーというコードが生成されて、それをフォームを入れたい場所に入れるだけでお問い合わせフォームが出来ちゃう優れものです。  

[contact-form-7 id="" title="コンタクトフォーム 1"]

フォームの見た目も数パターン用意されていて、選べるので自分のサイトにあったパターンを入れてみてください!

ちなみに余談ですがこのプラグインを開発したのは日本人なのでなんだか親近感も湧いちゃいます♫

https://ja.wordpress.org/plugins/contact-form-7/

EWWW Image Optimizer

これは画像とかをうまく圧縮(800KB→100kbみたいに容量減らすこと)できるプラグインです。

サイトの読み込みスピードが遅いと、せっかくサイトに訪問しようとしてくれたのに、途中で諦めてしまう人が増えてしまいます。

いろんな改善事項はありますが、まずは画像を軽くするというのが一番手軽で効果が高いです! 過去にアップした画像も含めて自動で圧縮してくれる機能もあるので簡単で便利です!

Google XML Sitemaps(少し中級向け)

Googleにこのサイトはこんな構造ですよーと教えるために Search ConsoleというGoogleのツールにサイト構造を示しているsitemap.xmlを送信して認識させる必要があります。

このツールは、例えばブログの記事を更新とかした際に自動でsitemap.xmlを更新してGoogleへ送信してくれます。

意外とそこまで手動で都度やるのは面倒臭いのですがこれだとインストールして最初に設定しておくだけなので超楽です!

https://ja.wordpress.org/plugins/ewww-image-optimizer/

BackWPup

これはWordpressを定期的にバックアップしてくれるプラグインです。

何をバックアップとるか、いつバックアップをとるか、 どこにバックアップをとるか、など細かく設定出来ます。

Wordpress修正したけど何かやっちゃってサイトが見れなくなった・・・」 「バージョンアップでサイトが崩れちゃった・・・」 「記事データ消しちゃった・・・」

というトラブルがあっても、バックアップを取って入れば復元できるので保険として入れておいた方がいいでしょう。

https://ja.wordpress.org/plugins/backwpup/

【番外編】Pz-LinkCard

記事中に関連記事のリンクを出すと、 読んでて、あまり面白くないなーという人もそのままサイトを閉じずに関連記事に行ってくれる場合があります。

そのために、よくブログ記事だと記事中に関連記事のリンクを出していますが このプラグインを使うとその関連リンクを、簡単に、そして良い感じに出してくれます!

これもショートコードが生成されるので それを入れたい記事で設定して入れたいところに入れるだけです。

[ url="http://xxx" title="xxxxxx" content="xxxxxx"]

出したい記事URLを設定するだけで、自動でアイキャッチ画像とか記事タイトルとか抽出してカード風に出してくれます♫

https://ja.wordpress.org/plugins/pz-linkcard/

まとめ

以上、ひとまず入れるべシリーズでした!

他にもたくさんオススメのプラグインがあるので機会があれば紹介します! プラグインをうまく活用すると、それ以外の執筆とかサイト作りに集中出来ますね!

ではでは!

OPTIMIZER_TRACEでMySQLの気持ちを理解する

こんにちは。マイケルです。 前回、MySQLのEXPLAINコマンドの結果を見やすく整形してくれる pt-visual-explain というツールの紹介をしました。 Percona Took Kitでビジュアルに実行計画を確認しよう

ツリー構造である実行計画を、ちゃんとツリー構造で表示してれるため、処理やJOINの順番が明確にわかるというものでした。

pt-visual-explain でもわからないこと

上記の記事のように、どのテーブルからどの順番でテーブルがJOINされていくのかを確認していると、"なぜその順番で処理しようとしたのか" がわからない場合があります。 具体的には以下のようなSQLの場合です。 (前回の記事と同じサンプルデータを使用しています。また★マークは私が書き加えたコメントです)

SQL

EXPLAIN SELECT
    e.emp_no,
    e.emp_name,
    e.dept_no,
    d.dept_name,
    d.location_no,
    l.location_name
FROM
    emp e
INNER JOIN
    dept d
ON
    e.dept_no = d.dept_no
INNER JOIN
    location l
ON
    d.location_no = l.location_no
WHERE
    e.dept_no = 10;

pt-visual-explain の結果

JOIN
+- Bookmark lookup
|  +- Table
|  |  table          e
|  |  possible_keys  emp_dept_no_idx
|  +- Index lookup
|     key            e-&>emp_dept_no_idx
|     possible_keys  emp_dept_no_idx
|     key_len        5
|     ref            const
|     rows           5
+- JOIN
   +- Bookmark lookup
   |  +- Table
   |  |  table          l
   |  |  possible_keys  PRIMARY
   |  +- Constant index lookup
   |     key            l-&>PRIMARY
   |     possible_keys  PRIMARY
   |     key_len        4
   |     ref            const
   |     rows           1
   +- Bookmark lookup       -- ★1番最初にアクセスされたテーブル ここから
      +- Table
      |  table          d   -- ★deptテーブル
      |  possible_keys  PRIMARY,dept_loc_no_idx
      +- Constant index lookup
         key            d-&>PRIMARY  -- ★dept_no列のINDEX
         possible_keys  PRIMARY,dept_loc_no_idx
         key_len        4
         ref            const
         rows           1   -- ★1行ヒット

まず、WHERE句の絞り込み条件を見ると、 e.dept_no = 10 とあります。 つまり、empテーブルからdept_noが10の行を取得しようとしています。 それに対して、 pt-visual-explain の結果をみると、一番最初にアクセスされているのはdeptテーブルになっています。 そして、使用されたINDEXとその結果の行数をみると、 dept_no列のINDEXを使用して1行のみヒットしています。

つまり、empテーブルのdept_noが10のものを探そうとしたのに、なぜかdeptテーブルのdept_noが10のものを取り出しているわけです。 SQLで命令したのとは異なる動きをしています。

なぜこんなことをしているのか。 MySQLさんの気持ち、さっぱりわからないわー。 もうマジMySQLさんキマグレ。

というようなことがたまに起こります。 特にINDEXチューニングしようとしていると、想定とは異なるJOINの順番になっていて、なんでやー!となったりします。

OPTIMIZER_TRACE

上記のように、「なんでそんな風にしようと思ったん?」と感じたときに非常に便利なのが、 OPTIMIZER_TRACE 機能です。 これは、MySQLのOPTIMIZERが、どんな順番で何を考えたのかの思考の手順をトレースしてくれる機能です。

使い方は以下の簡単3ステップ

  1. set optimizer_trace="enabled=on";
  2. SQL文実行
  3. SELECT * FROM information_schema.optimizer_trace\G

関連するパラメータや詳細な見方は、以下のエントリーが詳しいので合わせて御覧ください。 http://nippondanji.blogspot.jp/2015/12/blog-post.html

さっそくやってみる

先程のSQLの OPTIMIZER_TRACEを早速確認してみます。 結果は以下の通り。 (長いんですけど、いったん全部出します)

mysql> SELECT * FROM information_schema.optimizer_trace\G
*************************** 1. row ***************************
                            QUERY: SELECT e.emp_no, e.emp_name, e.dept_no, d.dept_name, d.location_no, l.location_name FROM emp e INNER JOIN dept d ON e.dept_no = d.dept_no INNER JOIN location l ON d.location_no = l.location_no WHERE e.dept_no = 10        -- ★(1)
                            TRACE: {
  "steps": [
    {
      "join_preparation": {
        "select#": 1,
        "steps": [
          {
            "expanded_query": "/* select#1 */ select `e`.`emp_no` AS `emp_no`,`e`.`emp_name` AS `emp_name`,`e`.`dept_no` AS `dept_no`,`d`.`dept_name` AS `dept_name`,`d`.`location_no` AS `location_no`,`l`.`location_name` AS `location_name` from ((`emp` `e` join `dept` `d` on((`e`.`dept_no` = `d`.`dept_no`))) join `location` `l` on((`d`.`location_no` = `l`.`location_no`))) where (`e`.`dept_no` = 10)"   -- ★(2)
          }
        ]
      }
    },
    {
      "join_optimization": {
        "select#": 1,
        "steps": [
          {
            "transformations_to_nested_joins": {    -- ★(3)
              "transformations": [
                "JOIN_condition_to_WHERE",
                "parenthesis_removal"
              ],
              "expanded_query": "/* select#1 */ select `e`.`emp_no` AS `emp_no`,`e`.`emp_name` AS `emp_name`,`e`.`dept_no` AS `dept_no`,`d`.`dept_name` AS `dept_name`,`d`.`location_no` AS `location_no`,`l`.`location_name` AS `location_name` from `emp` `e` join `dept` `d` join `location` `l` where ((`e`.`dept_no` = 10) and (`d`.`location_no` = `l`.`location_no`) and (`e`.`dept_no` = `d`.`dept_no`))"
            }
          },
          {
            "condition_processing": {       -- ★(4)
              "condition": "WHERE",
              "original_condition": "((`e`.`dept_no` = 10) and (`d`.`location_no` = `l`.`location_no`) and (`e`.`dept_no` = `d`.`dept_no`))",
              "steps": [
                {
                  "transformation": "equality_propagation",
                  "resulting_condition": "(multiple equal(10, `e`.`dept_no`, `d`.`dept_no`) and multiple equal(`d`.`location_no`, `l`.`location_no`))"
                },
                {
                  "transformation": "constant_propagation",
                  "resulting_condition": "(multiple equal(10, `e`.`dept_no`, `d`.`dept_no`) and multiple equal(`d`.`location_no`, `l`.`location_no`))"
                },
                {
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "(multiple equal(10, `e`.`dept_no`, `d`.`dept_no`) and multiple equal(`d`.`location_no`, `l`.`location_no`))"
                }
              ]
            }
          },
          {
            "table_dependencies": [
              {
                "table": "`emp` `e`",
                "row_may_be_null": false,
                "map_bit": 0,
                "depends_on_map_bits": [
                ]
              },
              {
                "table": "`dept` `d`",
                "row_may_be_null": false,
                "map_bit": 1,
                "depends_on_map_bits": [
                ]
              },
              {
                "table": "`location` `l`",
                "row_may_be_null": false,
                "map_bit": 2,
                "depends_on_map_bits": [
                ]
              }
            ]
          },
          {
            "ref_optimizer_key_uses": [
              {
                "table": "`emp` `e`",
                "field": "dept_no",
                "equals": "10",
                "null_rejecting": false
              },
              {
                "table": "`dept` `d`",
                "field": "dept_no",
                "equals": "10",
                "null_rejecting": false
              },
              {
                "table": "`dept` `d`",
                "field": "location_no",
                "equals": "`l`.`location_no`",
                "null_rejecting": false
              },
              {
                "table": "`dept` `d`",
                "field": "dept_no",
                "equals": "10",
                "null_rejecting": false
              },
              {
                "table": "`location` `l`",
                "field": "location_no",
                "equals": "`d`.`location_no`",
                "null_rejecting": true
              }
            ]
          },
          {
            "rows_estimation": [
              {
                "table": "`emp` `e`",
                "range_analysis": {
                  "table_scan": {
                    "rows": 24,
                    "cost": 7.9
                  },
                  "potential_range_indices": [
                    {
                      "index": "PRIMARY",
                      "usable": false,
                      "cause": "not_applicable"
                    },
                    {
                      "index": "emp_dept_no_idx",
                      "usable": true,
                      "key_parts": [
                        "dept_no",
                        "emp_no"
                      ]
                    },
                    {
                      "index": "emp_emp_name_idx",
                      "usable": false,
                      "cause": "not_applicable"
                    }
                  ],
                  "setup_range_conditions": [
                  ],
                  "group_index_range": {
                    "chosen": false,
                    "cause": "not_single_table"
                  },
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                      {
                        "index": "emp_dept_no_idx",
                        "ranges": [
                          "10 lt;= dept_no lt;= 10"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "rows": 5,
                        "cost": 7.01,
                        "chosen": true
                      }
                    ],
                    "analyzing_roworder_intersect": {
                      "usable": false,
                      "cause": "too_few_roworder_scans"
                    }
                  },
                  "chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "range_scan",
                      "index": "emp_dept_no_idx",
                      "rows": 5,
                      "ranges": [
                        "10 lt;= dept_no lt;= 10"
                      ]
                    },
                    "rows_for_plan": 5,
                    "cost_for_plan": 7.01,
                    "chosen": true
                  }
                }
              },
              {
                "table": "`dept` `d`",
                "rows": 1,
                "cost": 1,
                "table_type": "const",
                "empty": false
              },
              {
                "table": "`location` `l`",
                "rows": 1,
                "cost": 1,
                "table_type": "const",
                "empty": false
              }
            ]
          },
          {
            "considered_execution_plans": [
              {
                "plan_prefix": [
                  "`dept` `d`",
                  "`location` `l`"
                ],
                "table": "`emp` `e`",
                "best_access_path": {
                  "considered_access_paths": [
                    {
                      "access_type": "ref",
                      "index": "emp_dept_no_idx",
                      "rows": 5,
                      "cost": 3.4,
                      "chosen": true
                    },
                    {
                      "access_type": "range",
                      "cause": "heuristic_index_cheaper",
                      "chosen": false
                    }
                  ]
                },
                "cost_for_plan": 3.4,
                "rows_for_plan": 5,
                "chosen": true
              }
            ]
          },
          {
            "attaching_conditions_to_tables": {
              "original_condition": "((`e`.`dept_no` = 10))",
              "attached_conditions_computation": [
              ],
              "attached_conditions_summary": [
                {
                  "table": "`emp` `e`",
                  "attached": null
                }
              ]
            }
          },
          {
            "refine_plan": [
              {
                "table": "`emp` `e`"
              }
            ]
          }
        ]
      }
    },
    {
      "join_execution": {
        "select#": 1,
        "steps": [
        ]
      }
    }
  ]
}
MISSING_BYTES_BEYOND_MAX_MEM_SIZE: 0
          INSUFFICIENT_PRIVILEGES: 0
1 row in set (0.00 sec)

大量の出力で拒絶反応がでちゃいますか? それともワクワクしますか? 私はこういうトレース結果を読み解いたりするのが大好きなので、めっちゃテンションあがります。

結果の確認

とりあえず今回注目して頂きたい点だけ抜粋します。

(1) 元SQL トレース結果の1行目にあるのが、我々が発行した元のSQLです。

(2) 整形後のSQL 決められたフォーマットにSQLを整形しています。 ここではあくまでも整形しているだけで、どんな順でJOINするかなどはまだ考慮していません。

(3) transformations_to_nested_joins ON句に記述されていた結合条件を、全てWHERE句に移動させる形にSQLを変形しています。 結合条件はONでもWHEREでも書けるので、SQLの意味に変更はありません。

(4) condition_processing 所謂「述語の推移」を行っている部分です。 推移というのが数学の話なので、こういう言葉を使うと拒絶反応を起こす人もいると思いますが、中身を見ていけばなんとくやっていることがわかると思います。 該当部分だけ抜粋します。

            "condition_processing": {       -- ★(4)
              "condition": "WHERE",
              "original_condition": "((`e`.`dept_no` = 10) and (`d`.`location_no` = `l`.`location_no`) and (`e`.`dept_no` = `d`.`dept_no`))",   -- ★(4)-1
              "steps": [
                {
                  "transformation": "equality_propagation",
                  "resulting_condition": "(multiple equal(10, `e`.`dept_no`, `d`.`dept_no`) and multiple equal(`d`.`location_no`, `l`.`location_no`))"  -- ★(4)-2
                },
                {
                  "transformation": "constant_propagation",
                  "resulting_condition": "(multiple equal(10, `e`.`dept_no`, `d`.`dept_no`) and multiple equal(`d`.`location_no`, `l`.`location_no`))"
                },
                {
                  "transformation": "trivial_condition_removal",
                  "resulting_condition": "(multiple equal(10, `e`.`dept_no`, `d`.`dept_no`) and multiple equal(`d`.`location_no`, `l`.`location_no`))"
                }
              ]
            }

(4)-1 ではいったんWHERE句に移動させた条件がそのまま出力されています。 (4)-2 では、複数の条件式の中で、同じ意味(結果)になるものをひとまとめにしています。 具体的には

`e`.`dept_no` = 10

がある状態で、

`e`.`dept_no` = `d`.`deptno_no`

があるのなら、 e.dept_no = 10 でも、 d.dept_no = 10 で絞り込んでも、結局出てくるのは10番のデータだけということになります。 それを

(multiple equal(10, `e`.`dept_no`, `d`.`dept_no`)

のように表現します。 これによって、今回のSQLでは、empテーブルのdept_no列に対して絞り込み条件が書かれていましたが、これはdeptテーブルのdept_no列への絞り込に書き換えたとしても、結果は同じであるということが決定されます。

ここまでできれば、あとはどっちで絞り込んだほうが速いのかの判定です。 MySQLでは、SQLの各処理で必要となるリソース(ディスクI/O、メモリ使用量、CPU使用量、時間など)をコストという単位に数値化して内部で管理しています。 コストが高い = 遅い コストが低い = 速い と思って頂ければだいたい合っています。 OPTIMIZERは、どの順番でJOINして、どのINDEXを使えば最もコストが低くなるのかを見積もって、一番コストが低くなるやり方を採用します。 そこらへんの判断の過程もトレース結果には出ているのですが、長くなるので今回は省略します。 いずれ別のエントリーで説明ができればと思っています。

まとめ

今回のポイントは以下の3点です。

  • 元は empテーブルのdept_noに絞り込み条件を設定していた
  • MySQLのOPTIMIZERが、deptテーブルのdept_noへの絞り込みにしても結果が変わらないことを数学的に確定させた
  • empテーブルで絞り込むのがよいか、deptテーブルで絞り込むのがよいか考えて、よさそうな方を選択する

で、結果として、deptテーブルで絞り込むという選択をしたのが、EXPLAINの結果だったわけです。 このように、OPTIMIZER_TRACEの結果を読み解いていくことで、どんなふうにMySQLSQLを処理しているのか、つまりMySQLの気持ちが見えてきます。 (今回省略したコスト計算の辺りも理解すると、もっと深くMySQLの気持ちがわかるようになります)

なんか思うようにSQLのパフォーマンスが出ないなーとか、思った通りのINDEXを使ってくれないなーという時は、EXPLAINの結果と合わせて是非OPTIMIZER_TRACEの結果も眺めてみてください。 きっと、今よりももっと、MySQLと仲良しになれると思います。

おすすめタイピングフリーソフト

Keybord

今でもタイピングが苦手なキャサリンです。 システムエンジニアを目指すうえでタイピングが出来ないのは笑えないので ひたすら練習して初めの頃より早く打てるようになりました。 やはりタイピングが早いと業務効率化にもなりますし 皆さんも今から紹介するフリーソフトを使って 練習してみてはいかがでしょうか(#^^#)?

mytyping

  • https://typing.twi1.me/training/practice 初めに練習したのがこのソフトです。 ホームポジションでキーを打ていなかったので ひたすらキーを正しい指で打てるように練習しました。 押す指と打つキーに色がつくのでキーボードを見ずに 画面を見て練習をすることが出来ます。 a7e1c2f6a85301a6e64cd9ec9eff25d2.png

寿司打

  • http://typing.sakura.ne.jp/sushida/ システム部では10,000円コースをクリアするのが当たり前になっています。 スコアは、「何皿お皿が取れたか」ということで金額が決まります。 コツはタイピングミスをしないでいかに多く打つかです!! 映像が綺麗ですし楽しみながら練習できるのでお勧めです。 7a9d7f1c8988903a47741db61a6f2b5b.png

皿打

  • http://neutralx0.net/sarada/ 集まって来る可愛い小人さん取られないように タイピングをして追い出すゲームになっています。 凄いゲーム要素の高いタイピングソフトです。 色々なソフトで練習してやり飽きちゃった方に お勧めのソフトになっています。 04110f7ee30580cf8014e28010595d4345767672.png

楽しみながら練習をしてみて下さい(^^)/♪

こんなプルリクは嫌だ

Octicons-git-pull-request.svg.png

はじめに

アウローラでの開発はBitbucketで行われており、書いたコードをプロダクトへ取り込む際には基本的にプロダクト管理者のレビューが必須となっています。
コードレビューはコードの品質を上げたり、レビューイ(とレビュアー)のスタディにもなるしでいい事ずくめだけど。やーーーーーーーーーーーっぱり時間がかかるものです。
そこで、主に 私の開発速度を下げないため…もとい、チームの開発速度を下げないために、反面教師としてレビューしにくいPullRequestの紹介をします。

えー、これを書いたからにはこの内のどれかに引っかかるPullRequestが私に投げたら、ソッコーでrejectするからヨロシコ。
というわけでいってみよう。

マークダウン使ってない

一番最初に体裁のことを書くのもどうかと思うけど、githubやBitbucketではマークダウン形式で記載ができるので積極的に使う。
見出しや箇条書きで書くだけで、つらつら書くよりも相当見やすくなる。

私がよくやるのは、Macdownというマークダウンエディタを使って、バーっと書いた後に、PullRequestの画面に貼り付けるスタイル。 Bitbucketのテキストボックス部分はあまり使いやすいとは言えないので、手元のマークダウンエディタでやる形に落ち着いた。

なお、Macdownを使っているのはショートカットキーが使えるというただ一点なので、別にAtomでもPHPStormでもなんでも良いよ。

ちなみに個人的に使っているPullRequestのフォーマットは以下

タイトル

概要

やったことをさらっと完結に

詳細

以下を参照のこと。 課題(弊社ではBacklogへのリンク) - title

特記事項

実装上のコメントやBacklogで表現しきれないことを記載

コミットログ

PullRequest作成時、もしくはgit log で出力されたログを記載

マークダウンVer

# タイトル

## 概要
やったことをさらっと完結に

## 詳細
以下を参照のこと。

課題(弊社ではBacklogへのリンク)
- [title](url)

### 特記事項
実装上のコメントやBacklogで表現しきれないことを記載

## コミットログ
PullRequest作成時、もしくはgit log で出力されたログを記載

変更箇所多すぎ問題

1PullRequestに変更ファイルが1,000超えていたりするやつ。
通常の変更ではあんまりないけど、masterから空ブランチを切って、フレームワークをインストールしたりするとこうなる。

解決方法としては、フレームワークインストールは別にしてPullRequestを立てて、とっととマージしてしまう。(チームの方針にもよるけど、初期インストールとしてのフレームワークに関してはpushでも良いかもしれない) その後の開発については、マージ後のブランチから実施する。

今回いじってないけど変更に出ちゃいました問題

基本的に原因は2つ。

  1. PullRequestの投げ先の変更箇所が適切に自分のトピックブランチに反映されていない
  2. PullRequestの投げ先自体がトピックブランチの親ブランチで行われた変更が反映されていない。

タイミングによっては回避しづらいんだけど、以下の点は確認してほしいところ。

PullRequestの投げ先の変更箇所が適切に自分のトピックブランチに反映されていない

コミットログを確認して、自分がブランチを切ったよりも後に親ブランチが更新されていないか確認する。更新されていれば、トピックブランチに親の更新を取り込んで(マージして)必要ならpushしなおす。

PullRequestの投げ先自体がトピックブランチの親ブランチで行われた変更が反映されていない

こちらもコミットログを確認。トピックブランチの親ブランチと、PullRequestの宛先ブランチの差分を確認する。 ここに差分が出ている場合は、なんらかの理由(緊急対応、もしくは開発上の事情、または怠惰)があるので上司に確認しよう。 特にチーム開発で幾つものブランチが平行して走っていくような場合は、このようなことになりやすいので注意。

ignore系ファイルもプルリクに含まれている問題

本来はignoreすべきファイルがプルリクに含まれていて、かつデスクリプションで何も触れられていないパターン。
初期のリポジトリ構築時のミスにより含まれてしまったものなのか、それともなんらかの理由によりリポジトリ管理が必要になったものかわからない。
ぽけっとしていてmasterまでコミットしてしまうと、後でgitの管理下から逃れるのが面倒になるので適切にignoreされていない場合は適宜気がついたタイミングでignoreファイルの整備をすること。

ってこらそこ、ignoreの整備が面倒だからって言い訳して.ideaファイルをコミットすんじゃないよ、開くたびに変更検知されるじゃろがい。

放置されたコンフリクト問題

さすがにあんまりないと思うけど、プルリク投げてコンフリクトしてるけど放置するパターン。ただ、投げた当時はならなくてもしばらく経ってコンフリクトになるパターンもあるので、なかなか難しかったりもする。

コードフォーマット直しました系が含まれている問題

フォーマットを直した規模にもよるんだけど、ファイル全体のフォーマットがガッツリ直っていたりすると、プルリクが…というか確認すべきポイントが非常に見辛くなる。 歴史のあるプロダクトで途中でルールが変わったりすると、カッとなってやりたくなる事象。

個人的にはコードの変更をかける前にフォーマットをがっつり直したくなったら、それ専用のプルリクを作って欲しい。ソッコーマージするからw
それが難しい場合は、せめてコミットを分けること。フォーマットの変更と処理の変更のコミットがまとまっていると、bitbucketのプルリク確認画面での確認は困難になる。

本番リリース考えてません問題

安全にリリースするためのPullRequest、リポジトリ管理なのにリリースすることが考えられていないもの。
例えばデータベースの更新があるのに、スクリプトがないとか、あるけどたくさんあってしかも順番を考慮しないといけないようなイメージ。 この状態になっていると、レビュアーでできることはあんまりない(却下するぐらい、リリースできないから)

逆にどうなっていると嬉しいかって言うと、例えばデータベースの更新であれば…

  1. リリース時に実行するファイルが1まとまりになっており、上から実行すればよいもの。
  2. 複数ファイルに分かれているが、実行用のプロシージャが組まれているので、それを叩けば適切な順番で実行してくれるもの

1.2.どっちでもいいけど、このような感じになっていると非常に安心できる。

まとめ

と、いうわけでPullRequestを受け取る際にこんな形になっていると嫌なものをまとめました。程度の差こそあれ、どれも確認に手間取ったり、リリースの際に手間取ったり、そもそもリスキーだったりとプロダクトを安定して運営していくにおいて、小さな障害になりやすいものばかりです。
とは言え、どれもこれもPullRequestを投げる際に、ちょっと気をつけるだけでコミュニケーションが潤滑になるものも多いので、PullRequestを投げる際はぜひ、気をつけてみてください。

次回予告w

今回はPullRequestを受け取る側の目線ですが、次は逆です。題して…

こんなプルリクのコメントは嫌だ

PullRequest作成者から見て、(上司とは言え)こういうコメントされると腹立つわ〜というポイントを纏めていきます、ええ自戒も込めて。

乞うご期待。

新旧jQueryを共存させるときにハマっちまったポイント暴露大会

新旧jQueryを共存させるときにハマっちまったポイント暴露大会

こんにちは。最近は営業さんが受注申請に使ってる社内システムを メインに保守、運用改善をしている とりさん です。

そんな私がちょっとまえに 焦って恥ずかしながらどツボにハマった案件について記事にしてみました。 (なので同じ事例で頭を抱えた人に贈る、尖った記事に…)

この記事で書いてあること

  • 基本的な2つ以上の jQuery バージョンの共存方法
  • 共存させるときにハマってしまったポイント解説

この記事を書いたきっかけをさらに詳しく

  • 古(いにしえ)のソースで動く社内システム
  • jQyery古くて色々対応しにくい(色々使えない)
  • でも新しくするには影響範囲が…

ってことで、2バージョンの共存の道を選択したところ、 焦ってハマって、最速で動く状態にもってけなかったので 世界のみなさんが同じことで時間をムダにしないように 書いた記事になります。 (3行で説明してみたかったが無理だった…)

基本的な共存方法

jQuery 共存」でぐーぐるせんせいに聞くと、 だいたい、こんな情報が出てくるかと思います。 (バージョンは適当)


// 旧jQuery <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script> // 新jQuery <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.5/jquery.min.js"></script> <script type="text/javascript"> var $145 = $.noConflict(true); // ここがミソ </script> <script> // 新バージョンの使い方A (function($){ console.log($.fn.jquery); })($145) // 新バージョンの使い方B console.log($145.fn.jquery); </script>

ただ、古いバージョンを基本的に使いたい時、 ぐーぐるせんせに聞いてでてくる記事だと ちょっと気をつけないといけないところが… (特にポイント③)

どツボポイント① 「noConflict」で返り値につっこまれる順番

jQueryのバージョン変えて、 色々動かなくなったから焦って共存させようとしている君、落ち着こう。

リファレンスはやっぱりもちろん大事ですよね?

jQuery.noConflict() この関数を実行すると、$関数の動作が先に定義されている動作に戻る。

jQuery日本語リファレンスより抜粋 http://semooh.jp/jquery/api/core/jQuery.noConflict/_/

「先に定義されている動作に戻る」なので、 返り値で返ってくるのは「後に定義したバージョン」ですよ!

焦って先に定義したバージョンが返ってような錯覚に陥り、 「想定と違うバージョンになってしまう。なぜ?!」 と、パニックにならないように…

どツボポイント② リファレンスに引数書いてないよ…!

上記で紹介したリファレンスだと、引数がない。 ぐーぐるせんせいで調べると一番上に出てくるページが上記ページですが…

焦るな、君。 jQuery日本語リファレンスのいつものやつだ。 引数によってページが違う。

今回、本当に使ってるのはこっちです。

jQuery.noConflict(extreme) $関数のみならず、jQueryオブジェクトも含めて完全にグローバルの名前空間から除去する。運用は慎重に行うこと。 これは、上記のnoConflict()を更に極端にして$関数だけでなくjQueryオブジェクトも、先に定義された動作に戻してしまうものである。 これを使わなければいけないケースは極めて稀だと考えられるが、例えば複数のバージョンのjQueryを混在して使わなければならないような場合だとか。

jQuery日本語リファレンスより抜粋 http://semooh.jp/jquery/api/core/jQuery.noConflict/extreme/

リファレンスはやっぱりもちろん大事ですね。 (大事なことは何度でも言う)

どツボポイント③ ライブラリ(外部JSファイル)に対してのフォロー

さて、noConflictについても理解できたが、 いざ、書いて見るとまだうまく動かない君。

プラグインで入れたライブラリの中も 新しいバージョンじゃないと動かないことをわすれてませんか?

今回、とりさんも便利プラグインを使いたいが、 jQueryのバージョンが対応していなくてこの対応を行いました。

が、焦ってプラグイン内のライブラリに新バージョンをあてることを すっかり失念し、無駄な時間を使ってしまいました… (焦ってるって本当によくない…)

ので、プラグインのライブラリを直接修正して、 (相変わらずバージョンは適当)


(function($){ // ライブラリの中身 })($145)

と、するか、 noConflictのタイミングを調節して、


// 旧jQueryを定義 <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script> // 新jQueryを定義(↓ ここからは新jQueryで動く) <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.5/jquery.min.js"></script> // ここで読み込まれるライブラリは新jQueryで動く <script type="text/javascript" src="プラグインのライブラリ"></script> // ↑ ここまでは新jQueryで動く <script type="text/javascript"> var $145 = $.noConflict(true); </script> // ↓ ここからは旧jQueryで動く

としないといけません。

(ただし、 前者はライブラリを編集しなければならないし、 後者だと他の人が間違えて順番変えちゃうと大惨事…

なかなか、難しいところ…)

ここまでくれば、動くはず…!

それでも動かない場合は、 一度、席を立って煙草休憩なり カフェに行って宇治抹茶ラテなりを買ってきて 隣の人に相談してみましょう。

なにか、きっと見落としてますよ! (スペルミスしてたりとか)

ここまで読んで動いたみなさま、 この記事がお役に立てたなら何よりです。

これからはバージョンアップによる 影響範囲に怯えることなく、 便利なプラグインを使っていきましょう!

おわりに

とはいえ、最終的には 新バージョンへのリファクタが必要になるかと思います。

この対応のおかげで 部分的にリファクタができるようになった、 ということにもなりますので

みなさんも脱・旧jQuery を目指してみてください…!