社内のISUCONに参加しました。
昨日のことですが社内のISUCONに参加しました。
ISUCON2に関しては以下のブログに書いてあります
http://blog.livedoor.jp/techblog/archives/67728751.html
http://github.com/tagomoris/isucon2
今回はPHPを選択しました。
だって一番慣れてるしー
ホントだったらRubyでやりたいんだけどねー
実を言うと自分は業務がちょっと忙しくて
ソースを完全に見たのが当日の昼という状況でした。
まず基本的な設計指針
・PHP不要モジュールの削除
→ほぼ必須モジュールのみだったので手を加えず
・Apache不要モジュールの削除
→Auth系とかProxy系はOFF
・不要なサーバーのサービスのKill
→chkconfigで確認してPostfixとかOFF
・CSS,JS,画像ファイルの最小化
→JSはGoogleから引っ張ってきて画像は圧縮してBASE64エンコで内部に埋め込み
・Memcacheの導入
→別途記載(1)
・DBテーブルカラムの最適化
→別途記載(2)
・Apache等各種ログファイル出力のOFF
→httpd.confですべてOFF
・Keep Aliveの設定
→一回のリクエスト数が少ないので重くなりOFFにした
まず初期状態でのベンチスコアは110チケット前後
WebよりもDBのLoadAverageがいっぱいな感じ。
Memcacheの導入(1)
スロークエリーが出ていた場所は
SELECT stock.seat_id, variation.name AS v_name, ticket.name AS t_name, artist.name AS a_name FROM stock JOIN variation ON stock.variation_id = variation.id JOIN ticket ON variation.ticket_id = ticket.id JOIN artist ON ticket.artist_id = artist.id WHERE order_id IS NOT NULL ORDER BY order_id DESC LIMIT 10
最近購入されたチケットの10件を取得して表示する場所。
これは様々なページで呼ばれていたのでMemcacheに入れた。
チケットが売れた時に10件を超えていた場合はarray_popし古いのを1件削除して
array_unshiftで直近で売れたチケット情報を加えることにした。
Memcacheの導入(2)
他の部分でスロークエリーが出ていた場所は
SELECT COUNT(*) FROM variation INNER JOIN stock ON stock.variation_id = variation.id WHERE variation.ticket_id = :ticket_id AND stock.order_id IS NULL
残りのチケットの枚数を取得する場所。
はじめの枚数をMemcacheに入れて、
チケットが売れた時にそのvariationの枚数を-1する処理に変更。
ここで400前後のスコアまで改善した。
APCの導入+サーバーのチューンナップ
APCを入れたらスコアが一気に700前後まで改善した。
KeepAliveはスコアが落ちたのでOFFにした。
DBテーブルカラムの最適化
アーティストとチケットと開催場所をDBではなくソースに直接配列として記述した。
これらの値は基本固定だったので配列として処理した。
以下やらなかったこと
・nginxの導入
→時間が足りなかった
・Mysqlのエンジン変更
→Mysqlサーバはボトルネックになってなかったから
・フレームワークの変更
→時間が足りなかった
・Mysqlのテーブルカラムの構造変更
(stockのseat_idをvarchar255から2個にカラム分割してintに変換しindexを貼り直す)
→Mysqlサーバはボトルネックになってなかったから
以下反省点
・事前に時間を作ってちゃんとソースとサーバーを見ておくべきだった
・テストのソースをしっかりと確認してどこをテストしてるのかを確認すべきだった
・Webサーバっていう大まかな負荷を見るだけじゃなくて、
どの画面がボトルネックになっているのかっていうのをしっかりと把握すべきだった
・実際の業務じゃないので新しい技術に積極的にチャレンジしてもよかった
最後に
チームメンバーのお陰でなんと優勝することができました。
最小の処理でなかなかのパフォーマンスを引き出すことができ良かったかなと思います。
運営の皆様お疲れ様でした。次回ありましたら是非また参加したいです。
賞金5000円分の商品券もらったんで飲みにでも行きましょう(笑)
CakePHPでBlogを作る
某社の課題でBlogを作る。
基本設計編
次にデータベースの選択
・Mysql(ローカルにインストール済)
・PostgreSQL(ローカルに未インストール)
・Sqlite(環境が変わってもファイルなんで手軽)
ローカル環境なので何も考えずにMysqlで作ることにした。
ローカル環境編
ホストの設定
hostsファイルにローカルアクセスできるように以下を記述する。
sudo vi /etc/hosts 127.0.0.1 blog.localhost
Apacheの設定
バーチャルホストの設定する。
sudo vi /opt/local/apache2/conf/extra/httpd-vhosts.conf <VirtualHost *:80> DocumentRoot "/Users/kazuya/home/blog/app/webroot/" ServerName blog.localhost </VirtualHost>
サーバー再起動
sudo apachectl restart
データベース作成
本当ならユーザーも作成すべきだけどローカル環境なのでrootで全てやっちゃう。
mysql -u root -p # (パスワード入力) mysql > CREATE DATABASE `blog` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
CakePHPのインストール
http://cakephp.jp/ より最新版をゲットして/Users/kazuya/home/blogに設置する。
CakePHPの設定
データベースアクセスの設定
app/Config/databese.phpを作成する。
ファイル内容はこんな感じ。(パスワードとユーザー名は適宜変えてね)
またMacportsでインストールしたMysqlだとソケットを指定しないと動かないので注意する。
public $default = array( 'datasource' => 'Database/Mysql', 'persistent' => false, 'host' => 'localhost', 'login' => 'root', 'password' => 'XXXXXXXXX', 'database' => 'blog', 'prefix' => '', 'encoding' => 'utf8', 'unix_socket' => '/opt/local/var/run/mysql5/mysqld.sock' );
書き込みの権限を付与
キャッシュとかセッションを保存する一時フォルダーに書き込み権限をつける。
chmod -Rf 0777 app/tmp
http://blog.localhost/ にアクセスして問題なく動作していることを確認。
アプリケーション設計
データベース設計
データベースはERMasterで作った。
Blogで最低限記事とタグ付けが出来ればいいとの課題なので、
記事とタグと関連付けテーブル。あと認証を使いたいのでユーザーテーブルを作成する。
ER図はこんな感じ。
ERMasterでSQLをエクスポートしてコマンドラインからテーブルを作成する。
> mysql -u root -p blog < blog.sql
Bakeでモデル、コントローラー、ビューを作成する
CakePHPにはBakeっていうインタラクティブなコマンドラインツールがあるんだけど、
2.2で久しぶりに使ってみたらかなり使えるツールになってた。
php -f ./app/Console/cake.php bake --------------------------------------------------------------- Interactive Bake Shell --------------------------------------------------------------- [D]atabase Configuration [M]odel [V]iew [C]ontroller [P]roject [F]ixture [T]est case [Q]uit What would you like to Bake? (D/M/V/C/P/F/T/Q)
と表示されるのでMを選択してModelを作成する。
Use Database Config: (default/test) [default] > Possible Models based on your current database: 1. ArticleTag 2. Article 3. Tag 4. User
と4テーブル分のモデルをチュートリアルに従って作成する。
バリデーターとかも事細かにそれぞれのフィールドごとに指定できる。
ここで大切なのは
ArticleとTagはArticleTagにhasAndBelongsToManyであり、
ArticleTagはArticleとTagにbelongsToしていると関連付けしておく。
ControllerとViewも同様にして作成する。
アプリケーション実装
ルーティング処理
articlesのindexがblog.localhostにアクセスした際にデフォルトの表示になるように
app/Config/routes.phpに以下を記述する。
Router::connect('/', array('controller' => 'articles', 'action' => 'index'));
またコントローラーのadmin_の関数を有効化する為に
app/Config/core.phpに以下を記述
Configure::write('Routing.prefixes', array('admin'));
ページング処理
ArticlesControllerにページング処理の設定。
記事の公開日時順に並び替えたいのとデフォルトで5件表示に変更。
クラス変数に以下を追記
public $paginate = array( 'Article' => array( 'maxLimit' => 5, 'order' => array('published' => 'desc'), ));
あとはタグIDが指定されている場合にそのタグの記事だけが表示されるように、
ArticlesControllerのページング処理に絞込みの条件を指定。
function index ()内に以下を追記した。
if(isset($this->params['named']['tag'])){ $cond = "ArticleTag.tag_id = {$this->params['named']['tag']} and "; $this->paginate['Article']['joins'] = array(array('type' => 'LEFT', 'alias' => 'ArticleTag', 'table' => 'article_tags','conditions' => 'Article.id = ArticleTag.article_id')); }
ログイン処理
LoginControllerとAppControllerにadminのログイン処理を記述。
今回はAuthコンポーネントを使って処理する。
AppControllerにコンポーネントの有効化を記述することにより、
基本すべてのコントローラーがAppControllerを継承しているので全てに反映される。
クラス変数に以下を追記
public $components = array( 'Session', 'Auth' => array( 'loginRedirect' => Array('controller' => 'admin', 'action' => 'articles'), 'logoutRedirect' => Array('controller' => 'login', 'action' => 'index'), 'loginAction' => Array('controller' => 'login', 'action' => 'index')), );
ただ、Blogのトップと記事詳細はログインしてなくてもアクセスできるように、
AppControllerのbeforeFilterに以下を記述。
function beforeFilter() { $this->Auth->authError = __('Login Error'); $this->Auth->allow('article','index'); $this->Auth->allow('article','view'); }
日本語化対応
デフォルトでエラーメッセージやデータベースのカラムとかは全て英語表示。
一応日本語化しておく。
コマンドで全ファイル内の文字出力関数__()内の翻訳ファイルを作る。
php -f ./app/Console/cake.php i18n
これでapp/Locale/以下にdefault.potファイルが出来る。
mkdir -R app/Locale/jpn/LC_MESSAGES/ cp app/Locale/default.pot app/Locale/jpn/LC_MESSAGES/default.po
日本語の翻訳メッセージを入力しておく。
デフォルトで英語環境で使っているので日本語のFirefoxをインストールして確認した。
こんな感じで最低限ひと通りの機能は完成。
MyWi4.0+B-Mobileの組み合わせ最強説
以前の記事の続き。
MyWiがアップデートしてiOS4に対応しました。
3GをiOS4にすると電池のもちが悪くなったり、もっさりしたので、3.1.3に戻しました。
以下新しいフィーチャーの日本語訳。
http://www.rockyourphone.com/index.php/mywi-4.0.html
・より早く起動
・より消費バッテリーを少なく
・公式のテザリングバーとステータスバーに表示を選べる
・ステータスバー表示にすれば10-15%のバッテリーの持ちが良くなる
・Wifiの電波強度を30-100%の間で調整できる
・USBブリッジモードを搭載
個人的にはWifiの電波強度を調節できるのがいい。
基本的にすぐ近くに置いて使うので強度は殆どいらなく、
バッテリーの持ちと発熱を抑えられるのがいい。
あと起動がかなり早くなって使わないときはすぐOFFにできる。
5日くらい使いましたが、
たまに繋がらなくなったり不安定なことはあるものの、
基本的にiPhone4の3Gデータ通信の電波はOffのままで十分運用可能。
ドコモの広範囲+安定したネットワークで3Gにつながるのはすごい便利だ。
またMacbookProでUSB給電しながらのテザリングだと充電しがらテザリングできる。
E-MobileのPocketWifiなんかいらない
導入
昨日iPhone4を買いました。
今まではiPhone3Gを発売日に表参道で並んで買って丸々2年近く使ってきました。
昨年3GSが販売されて、
周りがiPhone3GSに機種変更していく中、我慢して3Gを使い続けていました。
最近外で仕事をする機会が多くなりiPhone4の購入と同時に、
E-MobileのPocketWifiを契約しよっかなーって思っていました。
iOS4には標準でAPNDisabler(3Gのデータ通信をOFFにする機能)が入っているのもあって、
iPhoneの最低パケットを維持すればトータルの差額1000円くらいで
PocketWifiを導入できるんじゃないかと考えていました。
でも個人的に回線契約を複数するのは嫌だし、
E-Mobileの2年縛りはあんまりいい話を聞かない。
どこでもネットはしたい、でもE-Mobileとは契約したくない。
その二つを解決してくれるのがBMobileとMyWiでした。
BMobile導入
BMobileは300kbpsとあんまり高速じゃないけど、
DocomoのFomaエリアなのでかなりの広範囲で使えるし、
もちろんテザリングOKだし、SIMフリー端末なら差し替えできるし、
半年で14900円(月あたり2500円)とかなり安いし変な縛りもない。
しかもビックカメラで買ったので10%のポイントが付いたので、
実質月に2235円と超お得だった。
開通手続きも電話するだけで簡単に開通。
MyWiはiPhoneをPocketWifiみたいにできるアプリケーションで10ドル。
SIMロックのiPhoneで
BMobileで使用するにはiPhone3Gを脱獄してSIMロックを解除しないといけない。
そこら辺の情報はGoogleさんに聞いてください。
SIMアンロック後に以下のようにAPNを設定してiPhone3単体でもネットできるようになった。
MyWi導入
んでMyWiでiPhone3GをPocketWifi化した感想。
300kbpsだけどかなり快適で、速度チェックしてみても安定して250-280kbpsの速度は出てる。
山手線でSoftBankだと絶対圏外になっていた新大久保->新宿、原宿->渋谷間も途切れない。
昨日の夜に試したところ、
2時間程度のテザリング状態で3Gの電池は半分以上残っていたので4時間くらいなら持ちそう。
(最近iPhone3Gを自分で分解して電池を交換したおかげもあると思うが。)
同時に3台Wifiで繋いでみたけど、
速度がちょっとイラつくぐらいで大丈夫そうだった。
3Gがスリープ状態でもテザリング状態になってしまうので、
テザリングが不要な場合はAirPlaneモードにするのがいいみたい。
必要になったらAirPlaneモードを解除してMywiのテザリングをOnにすればすぐに繋がる。
(1分かからないくらいでiPhone4で通信できた)
もちろんiPhone3G単体でもネットできるし
MacbookProの場合はUSB・Bluetoothでテザリング可能
感想
満足点
・iPhone3G,iPhone4,iPad,MacbookProでいつでもどこでもネットできる
・月々の支払金額が安くなりそう(下の表を参照)
・Softbankが圏外でWifiがない場所でもネットに繋がる
不満点
・ちょっと初期設定がめんどくさい
・やっぱり速度がちょっと遅い
・MMSが使えない(元々使ってないので影響はない)
料金比較表
項目 | iPhone4 | iPhone4 +PocketWifi |
iPhone4 +BMobile |
---|---|---|---|
初期金額 | 0円 | 1円 | 10ドル(MyWi) +13410円(BMobile) = 14400円 |
月額(ソフトバンク) | 980+315 +4200(パケ放) +1920(端末代金) -1920(月々割)= 5495円 |
980+315 +1029(パケ放) +1920(端末代金) -1440(月々割)= 2804円 |
980+315 +1029(パケ放) +1920(端末代金) -1440(月々割)= 2804円 |
月額(その他) | 0円 | 4980円(E-Mobile) | 0円 |
6ヶ月合計 | 32970円 | 46704円 | 31224円 |
1ヶ月あたり | 5495円 | 7784円 | 5204円 |
PC・iPadとのテザリング | × | ○ | ○ |
めんどくささ | - | あんまりない | 多少 |
持ち歩く物 | iPhone4 KBC-L2S iPhone充電ケーブル |
iPhone4 KBC-L2S PocketWifi iPhone充電ケーブル PocketWifi ケーブル |
iPhone4 iPhone3G KBC-L2S iPhone充電ケーブル |
※iPhone4は16Gを想定。
※パケ放題フラットは4200円固定だが、月々割が1920円になる。普通のパケ放題の場合は1029円〜4200円で月々割が1440円となる。
なんと、普通にソフトバンクに契約するよりも安くなってしまった。
自分的には変な縛りのある回線契約しなくていいのが嬉しい。
まぁJailbreakしてiPhoneが壊れても一切責任を負わないので自己責任でおねがいしますね。
iPad iPhoneでGoogle Syncを使う
何回やっても忘れてしまうので自分的なmemo。
※自分は言語を英語にしているので、日本語の人は注意。
iPhoneやiPad側の設定方法
一度設定すればMailとかカレンダーをPushで受け取ることができる。
ただOS3.1ではひとつのExchnageサーバーしかSyncできない。
OS4では複数のExchangeサーバーとSync可能。
1.Settings->Mail,Contacts,CalendarsからAdd Accountを選択する。
2.Microsoft Exchangeを選択する。
3.UserNameに[xxx]@gmail.comを入力する。
4.PasswordにGoogleのパスワードを入力する。
5.Nextボタンを押すとServerっていう項目が表示されるのでm.google.comを設定する。
6.さらにNextボタンを押すとMail, Calendar, Contactsが表示されて自分がSyncしたい項目だけOnにして完了。
詳しくはここ。
http://www.google.com/support/mobile/bin/answer.py?hl=en&answer=138740
複数のカレンダーをSyncしたい場合の設定方法
デフォルトでは自分のカレンダーしかPushされないので、複数のカレンダーをPushしたい場合は以下の設定が必要。
1.Mac or Windows上にFirefoxのUserAgentSwitcherをインストールしてUserAgentを変更できる状態にする。
2.UserAgentに以下を追加する
Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10
3.UserAgentを変更した状態で以下のURLにアクセス
https://m.google.com/sync/settings/iconfig/welcome?source=mobileproducts&hl=en
4.設定したい機器を選んでSaveで終了