前回の話を受けて、Railsの場合はどうなのかを調べてみたいと思います。ターゲットはRails 2.3.5です。
ActiveRecordに潜る RailsのO/R MapperはActiveRecordです。今回はActiveRecordでMySQLの場合について調べます。
まずはアクティブチェックしている部分を探します。ざっと見ると、connection_adaptors/mysql_adaptor.rbの275行目にactive?メソッドがありました。
def active? if @connection.respond_to?(:stat) @connection.stat else @connection.query 'select 1' end # mysql-ruby doesn't raise an exception when stat fails. if @connection.respond_to?(:errno) @connection.errno.zero? else true end rescue Mysql::Error false end うぉ、SELECT 1とかそのままじゃないか。とりあえず次はこれを呼び出しているところを探します。するとconnection_adaptors/abstract_adaptor.rbの149行目にverify!(*ignored)メソッドがありました。
# Checks whether the connection to the database is still active (i.e. not stale). # This is done under the hood by calling active?. If the connection # is no longer active, then this method will reconnect to the database.
例によって気になったので意訳。
Checking for a live database connection considered harmful on MySQL Performance Blog
顧客のデータベースでよく見かけて注意するのだが、クエリーを送信する前にデータベース接続がアクティブかどうかをチェックするのは大きなオーバーヘッドになる。これは、次のような擬似コードで書かれるデザインパターンに由来する。
function query_database(connection, sql) if !connection.is_alive() and !connection.reconnect() then throw exception end return connection.execute(sql) end 多くの開発環境やフレームワークで、こういうコードになっている。これには、実際には期待したとおりには動かないと言うことと、大きなパフォーマンスのオーバーヘッドがあると言う2点で間違っている。
実はちゃんと動かない このコードはレースコンディション(競合状態)によって動作しない。もしチェックしたときに接続がアクティブだとしても、connection.execute(sql)を実行するときにアクティブだとは保証されない。さらにもし非アクティブで再接続したとしても、同様にアクティブであるとは保証されない。
チェックして実行するのは実用的ではない。代わりに、次のように書き換えるべきだ。
function query_database(connection, sql, retries=1) while true try result=connection.execute(sql) return result catch InactiveConnectionException e if retries> 0 then retries = retries - 1 connection.reconnect() else throw e end end end end is_active()が無くなったのに気付いただろうか。接続がアクティブであればクエリーが実行され、そうでなければ失敗して再接続し、再度実行しようとする。
このコードだと、必要に応じてロック待ちのタイムアウトやデッドロックのときに再試行することができるようになっている。私の経験上、多くのアプリケーションで有効である。ほとんどのアプリケーションでは、こう言うときには単純に再試行するだけで、ちゃんと扱おうとはしていない。
パフォーマンスのオーバーヘッド アクティブチェックには大抵の場合、MySQLのプロトコルレベルのコマンドであるpingかstatisticsの呼び出しか、SELECT 1のような自明なクエリーが実行される。前者のコマンドはSHOW GLOBAL STATUSで表示されるCom_admin_commandsをインクリメントし、後者のクエリーは診断を難しくする。これは多くのアプリケーションで非常に高コストとなる。ここには2つのコストがある。1つはネットワーク通信とクエリー実行時間のアプリケーションへのコストで、もう1つはデータベースサーバの負荷上昇。このデータベースサーバへの負荷はかなり大きい。何日か前に、「管理コマンドのstatistics」を使うRuby on Railsアプリケーションを見たが、全クエリー実行時間の40%がこのコマンドだった。この不必要な接続チェックを削除したところ、データベースの負荷が半分程度に削減できた。これは普通じゃない!
マイミク日記にあったのですが、ずっと脳内ループ中です。タスケテ!
Standardプランの契約ができたので、早速設定。
/etc/ssh/sshd_config ```console Port xxxxx # ポート番号変更 PasswordAuthentication no # パスワードログインの禁止 PermitRootLogin no # rootログインの禁止 ``` 必要なパッケージの導入 ```console $ sudo su - # yum update # yum install zsh lv # chsh -s /bin/zsh # useradd hoge # passwd hoge # su - hoge $ chsh -s /bin/zsh $ exit # visudo ``` 公開鍵の登録とsshサーバの再起動、確認 パッケージのインストールと設定 ```console % sudo yum install mysql-server php-mysql % sudo vim /etc/httpd/conf/httpd.conf ``` iptablesの設定 PHPのインストール ```console $ sudo yum install php-pear-Net-Socket php-pear php-common php-gd php-devel php php-mbstring php-pear-Mail php-cli php-imap php-snmp php-pdo php-xml php-pear-Auth-SASL php-ldap php-pear-Net-SMTP php-mysql $ sudo vim /var/www/html/index.
Ruby on Rails携帯サイト開発技法 著者: 伊藤 祐策, 富田 陽介, 三上 喜之
出版日: 2010-04-30
出版社/メーカー: ソフトバンククリエイティブ
カテゴリ: Book
@yoshukiさんに献本していただきました。ありがとうございます。と言うわけで、書評を。
またもや端末識別番号問題 まず最初に残念な点を。最後の章でjpmobileを使わないで自前で機能を実装しているのですが、セッション管理に端末識別番号を利用してしまっています。これは高木さんがよく言われている由々しき問題で、エンジニアとしてはやってはいけないことのはずです。
ここまで破綻しているケータイID認証(簡単ログイン) on 高木浩光@自宅の日記 せっかくその前の章でSession Fixationの話がでたのに、最後にそれを台無しにしてしまう内容は、少し残念でした。Railsで携帯サイト開発の初の本なのでよけいに。書評でこんなこと書いてしまうのはどうかとも思ったのですが、携帯サービス開発をしている身としては、書かざるを得ませんでした。
あと途中から、あまりRubyのコードを書かない人なのかな?と思わせる部分が見受けられました。if 〜 thenだったり、文中のメソッドがget_mobile_id()と括弧がついていたり。気になったのはその辺りでしょうか。
Railsで携帯サイト開発する人は一通り目を通すべき とは言え、端末識別番号のこと以外ではお勧めできる本となっています。jpmobileの使い方からPCで絵文字を出す方法まで、かなり実践的な内容です。特にPCで絵文字は携帯/PC両対応のサイトを作るときには必ず出てくる問題で、それが詳しく書かれています。Railsで携帯サイト開発をする人は、一度は目を通しておいて損はしないでしょう。
そしてまとめ これはもうjpmobile on Rails3という本を書くしかないですよね(多少違う)。先を越された感をぬぐい去れないので、@yoshukiさんとはどこかで飲み明かしたいと思いました。さてjpmobileがんばろう。
気になったので、How fast is FLUSH TABLES WITH READ LOCK?の意訳。
FLUSH TABLES WITH READ LOCKってどれだけ時間がかかる? ちょっとまえのMySQLカンファレンスでバックアップソフトのベンダーのとこに行ったんだ。で、バックアップソフトについて話をし始めたんだけど、それはデータベース全体のロックを取得する、FLUSH TABLES WITH READ LOCKを使ってるということだった。でも彼は、ロックは「数ミリ秒」しかかからないと誇らしげに語ってた。バックアップソフトベンダーはそう思っているようだが、これは大きな勘違いだ。
実際にはこのコマンドがロックする時間はわからない。研究室のテスト環境ではそりゃミリ秒で終わるかも知れないが、実際にはもっと時間がかかる。数分から下手すれば数時間もかかることがあるだろう。そしてこの間、サーバは完全にロックされる(readonlyでもないんだ!)。なぜそうなのか、このコマンドが何をしてるか見てみよう。このコマンドには、いくつか重要な処理が含まれている。
ロックのリクエスト FLUSH TABLES WITH READ LOCKコマンドは、すぐにグローバルロックを要求する。すると、そのロックが許可される前に、システムの中で動作している全てのプロセスがロックアウトされる。理論上、結局は読み出しのロックを取得するだけなのだから、このことは悪いようには見えない。他の読み出しロックだけが必要なコマンドとは、共存できるはずだ。
でも実際には、多くのテーブルは読み書きされている。最初の書き込みクエリーがグローバルロックによってブロックされると、それに続く読み出しクエリーは、その前の書き込みクエリーが要求したテーブルロックによってブロックされる。結局、実質的にテーブルは排他ロックされてしまい、新しいリクエストは全てブロックされてしまう。読み出しのクエリーでさえも!
ロックを待つ FLUSH TABLES WITH READ LOCKがロックを取得する前に、ロックを持っている実行中のものは全て終わらなければならない。要するに、SELECTも含めて、全てのクエリーが終わる必要がある。もしすごい時間のかかるクエリーが動いていたり、テーブルロックするトランザクションがあったりすると、FLUSH TABLES WITH READ LOCKは全ての処理が終わってロックが解放されるまでブロックされることになる。これには結構な時間がかかるんじゃないかと思う。僕にとっては顧客のサーバにログインして走ってるクエリーを見ることなんて珍しくないこと。FLUSH TABLES WITH READ LOCKの前に、こういうクエリーが走っていたりすると、結果はさんざんなことになる。
このプロセスが動いているときにシステムがどう見えるかについて、例を示そう。
mysql> SHOW processlist; +----+------+-----------+------+------------+------+-------------------+----------------------------------------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+------+-----------+------+------------+------+-------------------+----------------------------------------------------------------------+ | 4 | root | localhost | test | Query | 80 | Sending DATA | SELECT count(*) FROM t t1 JOIN t t2 JOIN t t3 JOIN t t4 WHERE t1.
いろいろ準備が必要なので順番に。
rootになれるようにする 基本的にはblogSetomits : GDD Phone の標準 1.6 ROM で Rootedを参考にしてます。まずAndroid SDKをインストールしてtoolsディレクトリにパスを通しておきます。あとHTCのsigned-jdd-ota-14721.zipとAuto-sign.zipは別途ダウンロードしておきましょう。
% cd ~/Works/android % mkdir signed-jdd-ota-14721 % cd signed-jdd-ota-14721 % unzip ~/Downloads/signed-jdd-ota-14721.zip % cd signed-jdd-ota-14721/META-INF/com/google/android/ % rm update-script updater-script % wget http://dl.dropbox.com/u/227579/gddphone/1.6/update-script % wget http://dl.dropbox.com/u/227579/gddphone/1.6/updater-script % cd ~/Works/android % wget http://upld.komugi.net/Android/ota1.6su_patch.zip % mkdir ota1.6su_patch % cd ota1.6su_patch % unzip ../ota1.6su_patch.zip % cp -rf system ../signed-jdd-ota-14721 % cd ../signed-jdd-ota-14721 % zip -r update.zip META-INF boot.img radio.img system % cd .
型をt.binaryなどとすると、デフォルトではBLOBとなってしまい、64KiBまでしか保存できなくなってしまいます。そこで:limit => 1.megabyteとオプションで制限を大きめに書いてやると、Railsが勝手に拡張してMEDIUMBLOBにしてくれます。
class CreateImages < ActiveRecord::Migration def self.up create_table :images do |t| t.binary :data, :limit => 1.megabyte t.integer :stat t.timestamps end end def self.down drop_table :images end end mysql> desc imags; +----------------+------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------------+------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | data | mediumblob | YES | | NULL | | | stat | int(11) | YES | | NULL | | | created_at | datetime | YES | | NULL | | | updated_at | datetime | YES | | NULL | | +----------------+------------+------+-----+---------+----------------+ 5 rows in set (0.
やはりユーザに優しくないから。あと記事を書くことが目的で、ブログシステムとかCMSを作るのが目的じゃなかったから。
ユーザに優しいWordPress MovableTypeと並んでCMSの双対をなしていると思われるWordPressですが、ユーザ数が多いだけあって情報もプラグインも豊富でした。また公式サイトにしっかりしたインストールマニュアルがあることもポイントの一つです。こういった「ユーザに優しい」姿勢というのが、世に広く受け入れられる事につながるのでしょう。
じゃあ、tdiaryがユーザに優しくないかというと、Rubyistにはそうではないでしょう。だってコード読めばわかるし。必要な機能がないなら自分で実装してしまえばいいですからね。でもその方向に行ってしまうと、カスタマイズに注力してしまって、なんか本末転倒な気がしてしまうのです。一時期はtdiaryで環境を作り始めたのですが、「なんかコード書いているのがメインになってきている」と感じたので諦めました。
Railsのものにしなかったのも、それと同じような理由からです。「なければ作ればいいじゃないか」と言うのもいいのですが、「なかったから別のにしました」でもいいんじゃないかと思うわけです。と言うわけで、しばらくはWordPressでPHPと戯れていたいと思います。
来るGo!肉の日(5月29日(土))に、TokyuRuby会議02が開催されます。それに伴い、LT発表者の募集が始まっています。
TokyuRuby会議02 一見するとすごい敷居が高そうですが、何を隠そう、単なる飲み会です(違。みんなが飲んでいる間に、好き勝手喋ることができる、LTデビューにはもってこいのイベントです。内容もRubyと言う単語が入ってさえいれば、内容は問いません。プレゼンのタイトルに「TokyuRuby会議02」とか書くと、あとは何でもいいってことですね、わかります。
ちなみに今回も司会をすることになっているので、打ち合わせさえしてもらえれば合いの手を入れることも可能です。むしろコント化してもいいかもしれません。無限に可能性が広がるLT、それがTokyuRuby会議です(言い過ぎ。
と言うわけで、肉と酒が好きな方は(そうでない方も)、是非ご応募ください。