うっかりエンジニアのメモ

未来の自分に宛てたメモ

twitterでエアコンの電源on/off

日本の夏。暑すぎる。

帰ってきてドア開けた時の「もわっ」っていう熱気。しんどい。

このしんどさを減らすべく、帰宅30分前くらいにエアコン付けられたらいいなぁということで、Twitterで自分宛てに命令をつぶやいて自宅のエアコンをon/offするような仕組みを作りました。

つくったもの

  • 赤外線を送受信する電子回路
  • 赤外線を受信して記憶するArduinoスケッチ
  • Twitterのつぶやきに応じて赤外線を送信するArduinoスケッチ

ソースコードはgistに公開しています。(記事下部に埋め込んであります)

必要なもの

Arduino UNO R3 (参考市価: 3,000円)

Ethernetシールド(参考市価: 4,700円)

各種電子部品

  • 赤外線LED OSI5LA5113A
  • 赤外線受信モジュール OSRB38C9AA
  • OSRB38C9AA
  • 抵抗 47Ω
  • ジャンパワイヤ沢山
  • USBケーブル
  • LANケーブル

開発用マシン: MacBook Pro(Mac OS X 10.9.3)

エディタはsublime text 2です。
StinoというArduino開発向けプラグインが最高です。

配線

配線図は後ほど公開します。
以下のように部品とピン番号が対応しています。特にこの番号である必要はないです。

  • 赤外線LED 2番
  • 赤色LED 5番
  • タクトスイッチ(デバッグ用に赤外線を送信するため) 6番
  • 赤外線受信モジュール 7番

ただし、公式ドキュメントにあるようにArduino UNOでは10-13pinは使えないので注意。

Arduino communicates with the shield using the SPI bus. This is on digital pins 11, 12, and 13 on the Uno and pins 50, 51, and 52 on the Mega. On both boards, pin 10 is used as SS.

Arduinoスケッチ

マイコンに書き込むプログラムは以下の2つです。

  • リモコンの赤外線を受信して記憶するArduinoスケッチ
  • Twitterのつぶやきを一定時間おきに確認して、テキストに応じて赤外線を送信するArduinoスケッチ

ほんとは同時にできるといいのですが。

リモコンの赤外線を受信して記憶するArduinoスケッチ

橋本商会で有名な橋本翔さんのこちらのソースコードを若干改変させていただきました。

全然関係ない話ですみません、橋本翔という同姓同名の小学生の同級生がいるので驚きました。彼は地元で居酒屋を開業しているはずなので、タンジブルテバイスの研究に携わっているはずがなかった。

Twitterのつぶやきに応じて赤外線を送信するArduinoスケッチ

以下の記事を参考にしました。

赤外線リモコンの通信フォーマット

参考記事によると、フォーマットは3種類あります。

  • NECフォーマット
  • 家製協フォーマット
  • SONYフォーマット

うちのエアコンは日立製なのでSONYフォーマットはありえないとして、NECフォーマットか家製協フォーマットかは、実際にリモコンが送信するコードを調べてみないことにはわかりません。

赤外線を受信するスケッチをArduinoに書き込んで、エアコンの起動ボタンを押して受信モジュールで記録したコードを見てみます。

340,161,48,120,48,35,48,35,48,36,48,36,48,36,...(省略)

送っているコードのonの長さとoffの長さが交互に並んでいます。単位は10μsです。

どちらのフォーマットであっても最初の2つのコードはleader code(これからデータを送信しますよ、というのを受信側に伝えるためのコード)なので、この送信時間をもとに判別することにします。

**** NECフォーマット 家製協フォーマット
T 562μs 425μs
リーダコードのonの長さ 16T (= 8992μs) 8T (= 3400μs)
リーダコードのoffの長さ 8T 4T

一目瞭然。onの長さがぴたり一致したので、エアコンは家製協フォーマットでした。

赤外線でデータを送信する

赤外線リモコンの通信フォーマットはよく考えられていて、単純に赤外線が飛んできたら1、飛んでない時は0という表現ではありません。 というのも、赤外線はリモコンだけでなく様々な物体が発しているため、すべての赤外線に反応した場合、リモコンから送信される赤外線はそれらノイズに埋もれてしまい、正常に動作することはかないません。

そこでノイズ対策として、38kHzで赤外線を高速に点滅させてデータを表現する方式になっているようです。まず38kHzで一定時間送信された場合がon、一定時間何も送信されない場合がoffと定義されています。そして家製協フォーマットの場合、1T(=425μs)のonのあと1Tのoffが連続した時1、1Tのonのあと3T(=1275μs)のoffが連続した時0という具合です。

38kHzで赤外線を点滅させる

これが辛い。1/38kHz ≒ 26.3μsということはおおよそ半分の13μsごとにon/offを変えればいいと思うのですが

digitalWrite(PIN_IR_OUT, HIGH);
delayMicroseconds(13);
digitalWrite(PIN_IR_OUT, LOW);
delayMicroseconds(13);

とかやっても、エアコンはぴくりとも反応しません。
なんでかなーとwebを彷徨うとこんな記事が。

Arduinoの高速化 | なんでも独り言

digitalWrite命令は実行に44サイクル掛かるらしいです。Arduino UNOのCPUクロックは16MHzであることを考えると、44/16MHz = 2.75μsなので、2回のdigitalWriteで5.5μsのずれということになります。なかなか無視できない数値だと思います。

そこで、同じ記事中で紹介されていたレジスタ操作を利用したピンの状態変更を採用しました。この方法ならピンのon/offに掛かる時間は3クロックに大幅に短縮されます。実際、13μsずつのdelayでうまくいきました。

twitterのつぶやきを取得する

赤外線送信部分が実装できたら、あとはtwitterのつぶやきを取得する部分です。
TwitterREST APIを公開しているので、HTTPでレスポンスを受け取ることができれば何とかなりそうです。

ここでTwitter APIを利用するためにOAuth認証というものが必要になりますが、 このOAuth認証はなかなかリソースを食うのでArduinoの貧弱なハードには荷が重いです。

そこで、どこか外部に丸投げしようと思っていたら、素晴らしいサービスがありました。

StewGate Uはひとことで言うと、Twitter APIのプロキシサービスです。アプリ登録時にStewGateがトークンを発行してくれるので、以後そのトークンとTwitterへの命令をセットにしてStewGateのサーバにリクエストを送ると、StewGateが代わりにTwitter APIからデータを取得してこちらに返してくれます。

そしてStewitterは@arms22さん作の、Arduino向けにTwitterアクセス機能を提供するライブラリです。もちろんStewGate Uを内部で利用しています。

さて、Twitterからtweetを取得しましょう。
実はStewGate Uの制約で、2014/8現在実行できるアクションは「ツイートを投稿する」か「自分宛ての最新のmentionを取得する」の2つのみとなっています。おうふ。

ということで自分宛ての最新のmentionを取得して、自分が自分宛てに「on」とつぶやいていたら、エアコンを起動するように実装します。この辺りはソースコードを見てください。フォロワーが自分宛てに「on」とつぶやいてエアコンを起動できることがないように注意。

また、Twitter APIはrate limitといって、時間あたりのAPI問い合わせ回数に制限を設けているのでこちらも注意します。StewGate Uの内部実装は公開されていませんが、おそらくmentionの取得はTwitter APIGET statuses/mentions_timelineで行われているので、15分辺り15回が制限値です。つまり、mentionを確認する頻度が毎分1回を超えないようにします。

このスケッチのソースコード

ということで実装したソースコードがこちらです。
Stewitterのトークン(Stewitter twitter("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");)は適宜変更してください。

ハマったこと

赤外線送信時に終端コードの送信を忘れない

こんなのハマるの私だけかもしれませんが。。
家製協フォーマットは終端コードが定義されているんですね。なので、全てのデータを送信した直後に '1'を1回送信する必要があります。実装中にこれを忘れてしまい、一向にエアコンが反応しないことがありました。

赤外線LEDの角度が意外とシビア

ほんのちょっとでもエアコンの受光部から外れると信号が届きません。どうやら許容角度が5度くらいらしいです。
こういうLEDキャップ買うと許容角度が拡がるらしいので今度アキバ行ったら秋月で調達しておこう…

現状の課題

帰宅前にtwitterでつぶやくのを忘れる。

人間が一番信頼性低いっていう。
iPhoneGPSと連動させるくらいしないとダメですね。

おつかれさまでした。

Mac OS X 10.9.4にpython2.7開発環境+scikit-learn実行環境構築

ばりばり言語処理&機械学習しちゃうぞ

以下をインストールします

  • XCode 5.1.3
    • Command Line Tools
  • Homebrew 0.9.5
  • gcc 4.2.1
  • Python 2.7.8
    • numpy 1.8.2
    • scipy 0.14.0
    • scikit-learn 0.15.1
      • nose 1.3.3
  • mecab 0.996

手順

XCode

Command Line Toolsが後々、ライブラリのコンパイルで必要になる。
Xcode本体はAppStoreで検索するとすぐに入手できるので割愛します。
本体をインストール後、ターミナルで以下のコードを実行する。

$ xcode-select --install

参考:MacにHomebrewをインストールする

homebrew

おなじみのパッケージマネージャ。
homebrew公式サイトの最下部にあるワンライナーをターミナルで実行する。

2014/08/21時点では以下の通り。

$ ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"

Python本体

systemのpythonとは別に実行環境を作成したいのでpyenvでインストールする。

$ brew update
$ brew install pyenv
$ brew install pyenv-virtualenv # これは任意

$ pyenv install 2.7.8

numpy

$ pip install numpy

scipy

このままpip install scipyしてもインストールは失敗する。

そこでgfortranを入れようとすると、

$ brew install gfortran
Error: No available formula for gfortran
GNU Fortran is now provided as part of GCC, and can be installed with:
brew install gcc

gfortranはgccに組み込まれたよ!と言われる。なので、おとなしくgccを入れる。

$ brew install gcc

その後インストールするとうまくいく。

$ pip install scipy

nose

$ pip install nose

noseをインストールする理由

バージョン0.15.1のscikit-learnの依存性解決に不具合があるらしく、scikit-learnのインストール後にいざscikit-learnを使おうとすると

ImportError: No module named nose.tools

というエラーで起こられる。そのため、先にnoseをインストールしておく。

参考:Import errors after upgrading to sklearn 0.15

scikit-learn

機械学習用ライブラリ。

$ pip install scikit-learn

mecab関連

形態素解析ライブラリのmecabをインストールする。

$ brew install mecab
$ brew install mecab-ipadic

mecabpythonから利用するためのバインディングを導入する。

$ pip install https://mecab.googlecode.com/files/mecab-python-0.996.tar.gz

なお、mecab本体とバインディングのバージョンは一致させる必要がある。

1ヶ月でデータベーススペシャリスト試験に合格する方法

受験者のスペック

  • SIer新卒2年目エンジニア
  • 担当業務は脆弱性検査、コーディング(Java)
  • DBはOracle RACの調査で触る程度。業務でのDB設計経験は無し
  • 大学は情報系専攻

倒すべき敵を知る

試験は以下の4つの小試験に分けられており、全ての試験で合格点に達する必要があります。

**** 午前Ⅰ 午前Ⅱ 午後Ⅰ 午後Ⅱ
試験時間 9:30~10:20(50分) 10:50~11:30(40分) 12:30~14:00(90分) 14:30~16:30(120分)
出題形式 多肢選択式(四肢択一) 多肢選択式(四肢択一) 記述式 記述式
解答数/出題数 30/30 25/25 2/3 1/2
合格点 60% 60% 60% 60%

ただし、過去2年以内に以下の条件を満たしていれば、午前Iは免除されます。

午前Iが免除でないと応用情報の範囲もカバーする必要があり、短期間で合格するには辛いので、できればこの条件を満たした上で受けたいところです。私は2012年秋にセキュリティスペシャリスト試験に合格していたので、午前I免除でした。

午前II

データベースに関連した知識を問う4択問題が出題されます。25問中15問正解で通過です。 過去問からの出題が非常に多いです。感覚的には10問くらい過去問からなので、5問を取り切ることが課題になります。 25問中数問はセキュリティなど他の高度試験の試験範囲から出題されます。対策するにもコスパが悪いので、当たればラッキーの精神が良いと思います。

午後I

大問が3つ出題されます。

  • 大問1: データベース基礎理論
  • 大問2: データベース設計
  • 大問3: SQL(流動的)

おすすめは1,2選択です。 1,2は毎年この分野で出題され、問題形式もほとんど変化がありません。ですので、過去問対策が得点力に直結します。一方3はSQLをメインに扱うものの、パフォーマンスチューニング系の問題だったり設計と混ざった問題だったりと問題形式が流動的で、対策が辛いです。 1,2の組み合わせは、2chのスレを見ても定番のようです。 業務でSQLのパフォーマンスチューニングしまくってるレベルであれば、SQL選択で良いと思います。

H26春は傾向が変わった

長らく上記の大問構成が続いていたのですが、今回受験したH26春で傾向が変わりました。

  • 大問1: データベース基礎理論 + 設計
  • 大問2: SQL
  • 大問3: データベース設計 + SQL

SQLを捨てていた受験生はIPAに殺されました。。。 ちなみに大問2の会議室予約システムの問題は、ミック本の「重複予約を見つける」項がそのまま出題されたのでやってた人は「これ進研ゼミでやったやつだ…!」状態になったことと思います。やりましたね。

午後II

大問が2つ出題されます。どちらも10ページ超の長文を読んで、事例を分析します。

  • 大問1: データベース設計を中心に複合問題(流動的)
  • 大問2: 概念データモデル設計

おすすめは2選択です。 大問1はやはり出題範囲が流動的で、対策が辛いです。 大問2の概念データモデル設計は理解に時間が掛かるものの、理解度に応じてどんどん解けるようになっていくので、徒労になる心配がなくおすすめです。

おすすめ参考書

情報処理教科書

素晴らしいです。著者がDB試験に愛着あり過ぎで、過去問十数年分の解説が手に入ります。

情報処理教科書 データベーススペシャリスト 2014年版

情報処理教科書 データベーススペシャリスト 2014年版

過去問

無料。国家試験素晴らしいです。ある意味最強の教材です。

過去問題(問題冊子・配点割合・解答例・採点講評)

iPhoneアプリ

午前IIに不安がある場合はこのアプリで通勤通学の行き帰りにやっちゃいましょう。

データベーススペシャリスト試験 午前 精選予想問題集 500題 https://itunes.apple.com/jp/app/detabesusupesharisuto-shi/id480946632?mt=8

勉強法

DBは知識暗記系の試験ではありません。状況設定を理解し、比較的少ない原理・原則を適用して解く問題が多いです。そのため、長期にわたって勉強するとかえって中弛みがあってコスパが悪いです。 60点でも100点でも合格です。ここでは、合格できるレベルの原理・原則を理解し、時間内に答案用紙に表現できる状態で、「自分は解ける」という高まりを感じているピークの時期をちょうど試験日に合わせることを目的にします。

1ヶ月前

iPhoneアプリを使って午前II対策を始めます。通勤通学に進める程度でOKです。 今の知識でも解ける問題は本番も解けるのでスルーして、間違えた問題について、意識的に記憶を修正するようにします。原理・原則を覚えるべき問題は参考書やgoogle先生で理解しておきます。 並行して、ぼけ〜っと参考書を眺めます。パラパラ読む程度でOKです。「この内容の8割位が頭に入れば当日受かるんだな」というあたりをつけます。

3週間前

脳内に試験範囲の全体像がぼんやり形成されてきた頃です。ここで、直近の試験を印刷して午前IIから午後IIまで通しで本番と同じ形式で解いてみます。土日どちらかは潰れます。 その後、IPAのサイトにある模範解答を見て答え合わせをして(なんとなく)採点します。午後IIとか2時間もあるのに全然解答できなくて凹むこと請け合いですが、私も最初は2割くらいしか埋まらなかったです。 ここで「DBやべぇな…」という緊張感を得ておきます。

3週間前〜3日前

ひたすら過去問演習します。午前IIから午後IIまで通しでやる必要はないです。「今日は早く帰れたから午後II解こう」「なんか気分乗らないから午前IIだけやって寝よう」OKです。日を空けたりせず、毎日やって徐々にDB脳にしていきます。ただし、直近の一つ前の試験は解かずに取っておきます。 どのくらいの量を演習すればよいのか人それぞれですが、時間的制約で私は平成21年春まで消化しました。それより前は出題形式が異なるので参考にならないと思い、避けました。

はじめは制限時間を気にせず、ただし模範解答は見ないで、じっくり解きます。 配点は非公開なので点数を計算してもあまり意味がないと思います。○、△、×の3種類でつけていました。記述式の設問については「文意は合っているから丸」とかせず、文の構成や使用単語がIPAの模範解答と異なる場合は△にします。ケアレスミスは即修正して気をつけようで終わりです。「概念を根本的に誤って理解しているな」と感じたら、過去問を解く作業を中断して、参考書を読んで概念を再構築します。

終盤になったら、制限時間内に模範解答に近い解答を作成できるように時間も意識します。

前日

この日は丸一日空けます。 そして、解かずに取っておいた直近の一つ前の試験を午前IIから午後IIまで通しで解きます。 社会人の方、できれば有休取りましょう。

おそらく6割あたり、解ける力がついています。 「合格点行けるのでは」「全くわからない設問は無かったぞ」 3週間前とは全く違う手応えを感じながら、自分の中の成功イメージを高めます。

DBは詰め込み系の試験ではないので、前日にできることはほとんどありません。勉強はほどほどに早めに就寝して、翌日のパフォーマンスを良くすることに集中します。 私は美味しい夕飯を食べて、湯船にじっくり浸かって疲れを癒してから寝ました。 (が、実は緊張して4.5時間くらいしか眠れませんでした…)

当日

健闘を祈ります。

総学習時間

65時間。ざっくりと内訳↓

  • 午前II対策 0.5h(iPhoneアプリ)×20日 = 10h
  • 参考書読む 1.0h×20日 = 20h
  • 過去問 {5h(解答)+2h(答え合わせ)}×5年分 = 35h

結果

f:id:entropiajp:20140627125912p:plain

S3上の静的サイトにさくらで管理中のドメインのサブドメインでアクセス

今日、さくらのレンタルサーバから、さくらのVPS+さくらのメールボックスへのお引越しが完了しました。はじめはすべてVPSで済ませようと考えていたものの、メールサーバの構築難易度(セキュリティ面含む)の高さに恐れおののき、このような変則構成になりました。

その引っ越しの最中に、中学時代に書いてたオリジナルガンダム小説発表サイトなど、暗黒時代の産物が多数発掘されて、面白くなってきてこれはこれで公開しとこうと思い立ちました。でも普通にVPSのwebサーバ上に置くのも技術的に面白く無いなぁということで、そういえば昔AWSのS3に月額10円でサイトをホストうんちゃらというような広告を見たことがあったような気がして、ちょうどいい機会だと思って試してみました。

公開したいガンダム小説サイトはこちら http://0322.entropia.jp

条件

  • さくらのドメインentropia.jpを管理している
  • デプロイしたいサイトは静的サイト

ゴール

0322.entropia.jpで、S3上にデプロイした静的サイトにアクセスできる

手順

AWSでの設定

バケットの作成

[Create Bucket]から0322.entropia.jpという名前のバケットを作成します。

静的サイト公開設定

バケットを静的サイトのデプロイ先として利用できるように設定します。 Static Website Hosting > Enable website hostingを選択
Index Documentにindex.htmlError Documentにerror.htmlと入力します。

ファイルアップロード

普通にFTPする感覚で左上のUploadからファイルをアップロードします。
バケットのルートディレクトリがURLのルートと対応することに注意します。

パーミッション設定

Permission > edit bucket policyで開いたウィンドウに以下をコピペしてsaveします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::0322.entropia.jp/*"
        }
    ]
}

この時点でAWSドメインで静的サイトにアクセスすることができます。もう一息です。 http://0322.entropia.jp.s3-website-ap-northeast-1.amazonaws.com

さくらでの設定

会員メニュー > ドメイン > ドメイン名一覧 から所望のドメインのゾーン編集をクリックして、下記のCNAMEレコードを追加します。
0322 CNAME 0322.entropia.jp.s3-website-ap-northeast-1.amazonaws.com.
最後のドットが非常に重要です。

以上です。お疲れ様でした。

nginx上でサブディレクトリのCakePHPアプリを動作させる

もう本当に大変で、1週間潰れました。
同じように貴重な土日を潰してしまう人が一人でも減ることを願って。

環境

目標

サブディレクトリにあるCakePHPアプリを動作させたい。

いいかえるとhttp://example.com/subdir/ にアクセスして、サーバー上の/usr/shared/nginx/example.com/subdirにあるCakePHPアプリにアクセスしたい。

stackoverflowなどに同様の質問は沢山挙がっているのですが、どのベストアンサーも私の環境では正常に動作しませんでした。

CakePHP公式サイトのnginx設定例じゃダメなの?

ダメなんです。。。

server {
    listen   80;
    server_name www.example.com;
    rewrite ^(.*) http://example.com$1 permanent;
}

server {
    listen   80;
    server_name example.com;

    # root directive should be global
    root   /var/www/example.com/public/app/webroot/;
    index  index.php;

    access_log /var/www/example.com/log/access.log;
    error_log /var/www/example.com/log/error.log;

    location / {
        try_files $uri $uri/ /index.php?$uri&$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass    127.0.0.1:9000;
        fastcgi_index   index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

残念ながら、これはサーバのルートディレクトリにCakePHPを設置した場合の設定例なのです。
サブディレクトリにCakePHPを配置した場合の設定を一から考える必要があります。

CakePHP公式サイトのnginx設定例は何をやっているのか

公式サイトの設定例はそのまま利用できないものの、nginxでどう設定すればよいのかヒントになるので、詳しく見ていきます。

まず、最初のserverコンテキストで設定されているものです。

server {
    listen   80;
    server_name www.example.com;
    rewrite ^(.*) http://example.com$1 permanent;
}

これはwww.有りでアクセスがあった際に、www.なしのhttp://example.com/にリダイレクトする設定です。これはCakePHPと関係がないので、以後無視します。

後半のserverコンテキストを見ます。

server {
    listen   80;
    server_name example.com;

    # root directive should be global
    root   /var/www/example.com/public/app/webroot/;
    index  index.php;

    access_log /var/www/example.com/log/access.log;
    error_log /var/www/example.com/log/error.log;

    location / {
        try_files $uri $uri/ /index.php?$uri&$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass    127.0.0.1:9000;
        fastcgi_index   index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

server_name example.com;
Hostがexample.comであるアクセスは、このコンテキストの設定で処理されることを宣言しています。

root /var/www/example.com/public/app/webroot/;
URL上のルートパスに対応するサーバ上のフルパスが設定されます。apacheにおけるDocumentRootと同じ。

locationコンテキストその1

    location / {
        try_files $uri $uri/ /index.php?$uri&$args;
    }

(先ほどのrootディレクティブの設定は{}の内側全域で有効であることに注意)
ここのlocationコンテキストの動作はやや複雑。

  1. /var/www/example.com/public/app/webroot/$uriという静的ファイルを探す
  2. (無ければ)/var/www/example.com/public/app/webroot/$uri/というディレクトリを探す
  3. (それも無ければ)/var/www/example.com/public/app/webroot/index.php?$uri&$argsに内部リダイレクトする

  4. $uriにはクライアントから要求のあったURLから、server_nameを取り除いた文字列が入る

    • 例: http://www.example.com/xxx/yyyにアクセスがあった場合、$uri = xxx/yyy
  5. $argsはURLパラメータを保持したまま内部リダイレクトするための変数

locationコンテキストその2

    location ~ \.php$ {
        try_files $uri =404;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass    127.0.0.1:9000;
        fastcgi_index   index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

locationコンテキストに~をつけると、設定を適用したいアクセスを正規表現で指定できます。
この場合、末尾が.phpで終わるファイルへのアクセスは、このlocationコンテキストの設定が適用されます。

nginx設定例

以上を踏まえて、こうなりました。

server {
    listen       80;
    server_name  example.com;

    root   /usr/share/nginx/example.com;

    location / {
        index  index.php index.html;
    }

    location ~ ^/subdir/(img|css|js|files)/(.+)$ {
        root /usr/share/nginx/example.com/subdir/app/webroot;
        try_files /$1/$2 =404;
    }

    location ~ ^/subdir/(.+\.php) {
        fastcgi_pass    127.0.0.1:9000;
        fastcgi_index   index.php;
        fastcgi_param   SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include         fastcgi_params;
    }

    location ~ ^/subdir/(.*)$ {
        index  index.php index.html;
        try_files /subdir/$1 /subdir/$1/ /subdir/index.php?$1&$args;
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

参考資料

Nginx Community
http://wiki.nginx.org/

Nginx はどのようにリクエストを処理するか
http://nginx.org/ja/docs/http/request_processing.html

Nginx で location の判定方法と優先順位を調べる
http://server-setting.info/centos/nginx-location-check.html

nginx連載5回目: nginxの設定、その3 - locationディレクティブ
http://heartbeats.jp/hbblog/2012/04/nginx05.html

新卒2年目がAWSソリューションアーキテクト アソシエイトレベルに合格するまで

2014年5月に受験して合格しました。

例によって、受験時の規約により問題内容などについて言及することができないので、おすすめの教材などをまとめます。

受験者について

  • SIer新卒2年目エンジニア
  • 担当業務は脆弱性検査、コーディング(Java)
  • AWSは無料利用枠で触ってるくらい
  • 大学は情報系専攻

倒すべき敵を知る

公式サイトと受験体験記から、どんな試験なのかあたりをつけます。

眺めた受験記

http://drawing-hisa.seesaa.net/article/384974315.html http://qiita.com/inouet/items/02e8ba866a30f0316eeb http://humangas.hatenablog.jp/entry/2014/03/31/174247 http://www.seeds-std.co.jp/seedsblog/1055.html http://pc-concierge.net/blogs/blog/archives/70

おすすめ教材

サンプル問題
http://aws.amazon.com/jp/certification/certification-levels/certified-solutions-architect-associate/
公式に公開されています。

AWS 認定ソリューションアーキテクト – アソシエイト レベル試験に合格する方法
http://dev.classmethod.jp/etc/how-to-get-a-certification-aws-architect-associate-level/
クラスメソッドさんのブログにはいろいろ助けられました。

Amazon Web Services クラウドデザインパターン 設計ガイド

Amazon Web Services クラウドデザインパターン 設計ガイド

AWSエバンジェリスト玉川憲氏らが執筆した、AWSデザインパターンのカタログのような本です。1つのデザインパターンを数ページにまとめているので、さくさく読みやすいです。

AWS クラウドサービス活用資料集 特に「AWS マイスターシリーズ」と題した一連のプレゼン資料は、よくある要件をもとにAWSの各種サービス適用例を説明しているので理解しやすいです。

サンプル問題にもあるように、暗記すれば解ける類の問題よりも、ある状況設定のもとで最善の方法を選ばせる問題に対応できると良いと思います(精一杯の言い方)

ひとこと

CCNAのような暗記ゲーでも無ければ、Oracleの白本(※)のようなチート本もないので、なかなかしんどい試験だと思います。 「実戦経験がない人は受けない方が良い」という意見も見かけましたが、AWSを使ったアーキテクチャ設計が未経験だったり、AWSの管理画面をばりばり利用したことがなくても、ドキュメントベースでAWSへの理解があれば合格できるように感じました。 実務に裏打ちされた深い技術理解の証明には、今ならソリューションアーキテクト – プロフェッショナルレベルが適していると思います。

【オラクル認定資格試験対策書】ORACLE MASTER Bronze[12c SQL基礎](試験番号:1Z0-061)完全詳解+精選問題集(オラクルマスタースタディガイド)

【オラクル認定資格試験対策書】ORACLE MASTER Bronze[12c SQL基礎](試験番号:1Z0-061)完全詳解+精選問題集(オラクルマスタースタディガイド)

  • 作者: エディフィストラーニング株式会社,西昭彦,飯室美紀,鈴木佐和,岡野友紀,矢島祐子
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2014/03/27
  • メディア: 単行本
  • この商品を含むブログを見る

CakePHPでやたら遅いSELECTクエリの改善

プライベートで作ったWebアプリで、一画面だけブラウザに表示されるまで3秒かかる激重画面があります。 この画面ではCakePHPが自動的にいろんなテーブルをjoinしたSQLを生成しているので、その辺りが原因だろうとは感づきました。 それにしても、たかだか20行程度のselectなので、なんか変だ。。。 ちょっとだけ分析と改善をしました。(はじめてのパフォーマンスチューニング…ドキドキ)

結論としては、1000倍早くなりました。 CakePHPのクエリ自動生成は楽ですが、パフォーマンス上の問題が発生した時にはやはりSQLを知らないとダメだなぁ…

環境

VirtualBoxVM(メモリ613MB)上に下記の環境があります。

テーブル構造

テーブル 内容 外部キー
orders 注文データ shipping_method_id, payment_method_id, prefecture_id, tracking_code_id
shipping_methods 発送手段データ なし
payment_methods 入金方法データ なし
prefectures 都道府県データ なし
tracking_codes 追跡番号データ order_id(この追跡番号に対応した注文のid)

全てのテーブルはid列を主キーとして持つ。

恐ろしく時間がかかるクエリは、ordersテーブルを基点に他の4つのテーブルをleft joinして、注文が新しい順にソートして上位20件だけselectするようなクエリです。

改善前

CakePHPが発行したクエリ

SELECT *
FROM
  orders 
LEFT JOIN shipping_methods ON orders.shipping_method_id = shipping_methods.id
LEFT JOIN payment_methods ON orders.payment_method_id = payment_methods.id
LEFT JOIN prefectures ON orders.prefecture_id = prefectures.id
LEFT JOIN tracking_codes ON orders.id = tracking_codes.order_id
  WHERE 1 = 1
  ORDER BY orders.created DESC
  LIMIT 20;

※見やすさのために手を加えています

WHERE 1 = 1が気になりますが速度に影響しません。
CakePHPが挿入するこのWHERE 1 = 1の意味はこちらの解説がわかりやすいです。
CROSSOVER SEplus : where 1 = 1

実行速度(5回実行平均)

1826ms
くっそ遅い

クエリのexplain結果

id select_type table type possible_keys key key_len ref row Extra
1 SIMPLE orders ALL NULL NULL NULL NULL 2604 Using temporary; Using filesort
1 SIMPLE shipping_methods eq_ref PRIMARY PRIMARY 4 ordersys.orders.shipping_method_id 1
1 SIMPLE payment_methods eq_ref PRIMARY PRIMARY 4 ordersys.orders.payment_method_id 1
1 SIMPLE prefectures eq_ref PRIMARY PRIMARY 1 ordersys.orders.prefecture_id 1
1 SIMPLE tracking_codes ALL NULL NULL NULL NULL 2233

で、でた〜ww
Using temporary; Using filesortはアカン。
なぜアカンのかはイケメンな漢さんの記事が大変参考になります。
漢(オトコ)のコンピュータ道: Using filesort

分析

怪しいのは2箇所。

  • LEFT JOIN tracking_codes ON orders.id = tracking_codes.order_idだけ、join先の列がidではなくorder_id
  • ORDER BY orders.created DESCでは、indexが生成されていないcreated列を使ってソートしている

それぞれ修正していきます。

CakePHPのテーブル関係定義

生成されるSQLの違いはCakePHPでの設定の違いから生まれるのは間違いないので、Orderモデルの定義をみていきます。

<?
class Order extends AppModel {
    public $hasMany = array(
        'ItemOrder'
    );
    
    public $hasOne = array(
        'TrackingCode',
    );

    public $belongsTo = array(
        'ShippingMethod',
        'PaymentMethod',
        'Prefecture'
    );
    ?>

SQLが怪しいTrackingCodeモデルだけ、関係がhasOneなのが気になります。

設計時に「OrderはひとつのTrackingCodeを持つ」という考えだったけれど、エンティティの主従関係を踏まえると、OrderがあろうがなかろうがTrackingCodeは存在するのだから、「OrderTrackingCodeに紐づく」(つまりOrder belongs to TrackingCode)としたほうが自然。

したがってOrderモデルを以下のように修正しました。

<?
class Order extends AppModel {
    public $hasMany = array(
        'ItemOrder'
    );

    public $belongsTo = array(
        'ShippingMethod',
        'PaymentMethod',
        'Prefecture',
        'TrackingCode'
    );

    // 中略
    ?>

ソート

これまで、注文日時が新しい順にデータが欲しかったのでorders.created(レコードの挿入日時)をもとにソートしていましたが

  • orders.created列にはindexが作成されていない
  • orders.id列で降順ソートしても注文日時順にデータが取得できる

ということで、orders.idをもとにソートするように修正します。

ORDER BY orders.id DESC

改善後

CakePHPが発行したクエリ

SELECT *
FROM
  orders 
LEFT JOIN shipping_methods ON orders.shipping_method_id = shipping_methods.id
LEFT JOIN payment_methods ON orders.payment_method_id = payment_methods.id
LEFT JOIN prefectures ON orders.prefecture_id = prefectures.id
LEFT JOIN tracking_codes ON orders.tracking_code_id = tracking_codes.id
  WHERE 1 = 1
  ORDER BY orders.id DESC
  LIMIT 20;

実行速度(5回実行平均)

1.3ms!!
当社比1400倍。くっそ速い(前が遅すぎた)

クエリのexplain結果

id select_type table type possible_keys key key_len ref row Extra
1 SIMPLE Order index NULL PRIMARY 4 NULL 20
1 SIMPLE ShippingMethod eq_ref PRIMARY PRIMARY 4 ordersys.Order.shipping_method_id 1
1 SIMPLE PaymentMethod eq_ref PRIMARY PRIMARY 4 ordersys.Order.payment_method_id 1
1 SIMPLE Prefecture eq_ref PRIMARY PRIMARY 1 ordersys.Order.prefecture_id 1
1 SIMPLE TrackingCode eq_ref PRIMARY PRIMARY 4 ordersys.Order.tracking_code_id 1

Using temporary; Using filesortが消えてる

まとめ

  • CakePHPhasOneアソシエーションを設定する場合は生成されるクエリに注意
  • ソート列にindexが作成されているか注意
  • クエリが遅かったらexplainで手がかりをつかむ