macportsからHomebrewに乗り換えてMySQL5.1をインストールするまでの流れ

macportsで入れたアプリをlaunchctlからアンロードする

$ sudo launchctl list | grep port
79      -       org.macports.mysql5
80      -       org.macports.apache2


macportsで作られたplistは以下のディレクトリにある

  • /opt/local/etc/LaunchDaemons


アンロード

$ sudo launchctl unload ./org.macports.apache2/org.macports.apache2.plist
$ sudo launchctl unload ./org.macports.mysql5/org.macports.mysql5.plist

macportsディレクトリをmvする


完全削除はなんか怖いので、一旦ディレクトリをmvする

$ sudo mv /opt/local /opt/local_bak

Homebrewのインストール

$ ruby -e "$(curl -fsSkL https://raw.github.com/mxcl/homebrew/go)"

Xcodeコマンドラインツールのインストール


インストール時に入れろと警告が出たのでインストール

Xcodeのアップデート


その後こっちも警告が出たのでAppStoreから最新のを取得

/etc/path の修正


ローカルに入っているアプリの方が、Homebrewより優先されるので、/etc/pathを以下のように修正

  • Homebrewの/usr/local/binを先頭にする
$ cat /etc/paths
/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin


.zshrcを修正してもできる

gitのインストール


Homebrewのシステムでgitを使っているので絶対に必要

$ brew install git

Homebrewの更新

$ brew update

MySQL5.1のインストール

#標準だと5.5系がインストールされるので、他のバージョンを入れるformulaを追加
$ brew tap homebrew/versions
# MySQL5.1をインストール
$ brew install mysql51


この後、インストール時の説明にしたがって、launchctlをロードするが
そのままだと、手動でMySQLを終了できなくなるので、.plistファイルを編集する必要がある

# 
$ mkdir -p ~/Library/LaunchAgents
$ cp /usr/local/Cellar/mysql51/5.1.67/com.mysql.mysqld.plist ~/Library/LaunchAgents/
$ launchctl load -w ~/Library/LaunchAgents/com.mysql.mysqld.plist
$ launchctl list | grep sql
-       1       com.mysql.mysqld
# 初回設定
$ mysql_install_db --user=mysql --tmpdir=/tmp
# ルートパスワードの設定
$ /usr/local/Cellar/mysql51/5.1.67/bin/mysqladmin -u root password '*****'
# ログインしてみる
$ mysql -u root -p
  • tapは、formulaを追加するコマンド
  • いろんなサイトで、MySQL5.1を入れるときは、「brew install https://raw.github.com/adamv/homebrew-alt/master/versions/mysql51.rb」とあるけど、今は404になっているので使えない
    • オフィシャル側で用意してくれているのをtapする
    • brew versionsで出てくるMySQL5.1.56は、取得先にファイルが無いためにエラーになるので使わない

my.cnfの設定


クライアント、サーバそれぞれでutf8を使うように設定

ログを出すようにする

$ mkdir /var/log/mysql
$ sudo chown kasei_san /var/log/mysql
$ vim /usr/local/var/mysql/my.cnf
[mysqld]
log-error=/var/log/mysql/error.log
log=/var/log/mysql/query.log


ログローテーションは、newsyslogでできるらしい。そのうちやる?

その他メモ

再起動
mysql.server restart
my.cnfの場所
/usr/local/var/mysql/my.cnf

クライアント起動時に特定のデータベースにアクセス

$ mysql -u root -p -D test

jsのプロトタイプについてまとめてみた


なんか理解がぼんやりしていたのでまとめてみました

JavaScriptはプロトタイプベースっていうけど、それって何?


プロトタイプベースとは、全てのオブジェクトは他のオブジェクトのクローンを派生させたものという設計のオブジェクト指向。派生元をプロトタイプという
プロトタイプベースでは、自身が定義していない要素にアクセスした場合、プロトタイプをたどって要素を探しに行く。これをプロトタイプチェーンと言う

Javaみたいなクラスベースとの違いは?


クラスベースでは、全てのオブジェクトは静的なクラスから生成される。
その為ちょっとしたクラスの派生を作りたい場合にも、クラス新たに定義する必要があるし、また基本的には動的にクラスを定義できない
プロトタイプベースでは、オブジェクトに要素を自由に追加出来るので、柔軟なのが利点

JavaScriptではどうやって、オブジェクトを生成するの?


JavaScriptでは、オブジェクトの生成は基本的にはnew演算子で行う

  var a = new A();


new演算子がやることは、

  • 引数に渡した関数オブジェクト(コンストラクタ)が持つプロトタイプを引き継いだオブジェクトを生成する事
  • それをthisとして関数オブジェクトを実行すること


あと、newで生成されたオブジェクトをインスタンスと呼ぶ。

  • instanceof演算子で、どの関数オブジェクトから生成されたのか調べる事が可能

newしたり、生成されるオブジェクトがインスタンスだったり、クラスベースっぽいね


newやinstanceof演算子の為に、クラスベースっぽく見えるけど、JavaScriptはれっきとしたプロトタイプベースの言語。
クラスベースっぽく見えるのは、ネットスケープJavaScriptを公開する時にJavaサーブレットも同時に公開する為に、JavaScriptJavaっぽくしたかったというマーケティング的な理由があったからと思われる

プロトタイプっぽいところは?


JavaScriptでは、prototype要素にプロトタイプの情報を格納する。
prototype要素は関数オブジェクト生成時に自動的に、constructor要素にその関数オブジェクトを入れたものが生成される
ちなみに、newを実行した時には、そのconstructor要素が呼ばれるので、中身を差し替える事で、呼ばれるコンストラクタを動的に変更できる。キモい!

var A=function(name){
  this.name = name;
}
A.prototype.say = 'hello';

var a = new A('Bob');
a.name; // Bob
a.say; // hello


プロトタイプはインスタンスが個別に持つのではなく、new した関数オブジェクトのprototypeを参照するので、newした後の変更も反映される

A.prototype.say = 'hi!';
a.say; // hi!

プロトタイプチェーンを使って継承っぽいことができるらしいけど?


web上ではこんなやり方が多い

var Asub = function(){...}
Asub.prototype = new A();
Asub.prototype.constructor = Asub; // prototypeを上書きしたので、コンストラクタを再定義
Asub.prototype.add = function{ ... } // 追加したい要素を定義していく


継承元の関数オブジェクトをnewしたインスタンスをプロトタイプに入れる
Aの持つ要素を全てAsubのプロトタイプに入れるイメージ
コンストラクタで定義された要素もプロトタイプに入るから漏れがないように感じる


だけど、色々欠点がある

  • コンストラクタに引数がある場合に動作出来ない
  • コンストラクタを呼びだす毎に内容が変わる場合に対応できない
  • コンストラクタで定義された要素と、prototypeがごっちゃになる


例えば、こんなことしたい場合

// 面積
var A = function(width, height){
  this.width = width;
  this.height = height;
}
A.prototype.area = function(){ return this.width*this.height; }

// 体積
var Asub = function(width, height, depth){...}
Asub.prototype.volume = function{ return this.width*this.height*this.depth; }

// これだと、A側のコンストラクタを引数付きで実行できない!
Asub.prototype = new A();    


なので、サイ本とかだと以下の方法をおすすめしている

// applyで、継承元のコンストラクタをコンストラクタで実行する
var Asub = function(width, height, depth){
  A.apply(this, [width, height]);
}
// Object.createは、引数のプロトタイプを持つオブジェクトを生成するメソッド
Asub.prototype = Object.create(A.prototype);
Asub.prototype.constructor = ASub;


コンストラクタ側で、継承元のコンストラクタも呼ぶ
んで、newするのではなく、Object.createでprototypeを定義する
これで、継承元のコンストラクタと、prototypeをごっちゃにせずに引き継ぎすることが可能


そんな感じです

画像のレスポンスコードが304の場合、onloadイベントが発生しない件と対策


以下のようなコードを書くと、画像が304を返した場合に、onloadイベントが発生しない

var img = new Image();
img.onload = function(){ console.log("loaded!"); }
img.src = "./01.jpg"; // 01.jpg が304を返すと、onloadが発生しない!!


当然と言えば当然なのですが、少しハマったので対策をメモ

先に対策


setIntervalで img.complete をチェックし続ける
img.completeは、画像が読み込み完了したら true を返す

  • キャッシュであれ、webから取ってくるのであれ、読み込みが終わったらtrueになるので、304でも読み込み完了を知ることができる

onloadと、img.completeの動き


MacのFirefox16, Safari5.1, chrome23で動作確認した結果はこんな感じ

  • onloadは200の時にのみイベントが発生
  • img.completeはなんであれ、サーバから結果を受け取ったらtrueがかえってくるっぽい
  • ブラウザのキャッシュの場合は未調査
    • 後でやる

サンプルコード


rack でこんなの書いて実験した

def self.call(env)
  data = nil
  File.open("./01.jpg", "rb") do |f|
    data = f.read
  end
  case env['PATH_INFO']
  when '/200.jpg'
    [200, {"Content-Type" => "image/jpeg"}, [data]]
  when '/304.jpg'
    [304, {}, [data]]
  when '/500.jpg'
    [500, {}, [data]]
  when '/404.jpg'
    [404, {"Content-Type" => "image/jpeg"}, []]
  when '/'
    [200, {"Content-Type" => "text/html"}, [(<<-EOS)]]
      <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <script>
          var img_check = function(path){
            var img = new Image();
            img.onload = function(){console.log(path + ":onload");}
            img.src = path;
            var id = setInterval(function(){
              console.log(path + ":img.complete:" + img.complete);
              if(img.complete){
                clearInterval(id);
              }
            }, 100);
          };
          img_check("/200.jpg"); // onloadイベントが発生して、complete:true
          img_check("/304.jpg"); // onloadイベントが発生しなくて、complete:true
          img_check("/500.jpg"); // onloadイベントが発生しなくて、complete:true
          img_check("/404.jpg"); // onloadイベントが発生しなくて、complete:true
        </script>
      </head>
      <body>
      </body>
      </html>
    EOS
  end
end
run self


そんな感じ

追記


onerrorも試してみたら、304もonerrorが発生してた

MacPortsで、mecabと、mecab-rubyのインストール


色々手間だったので記録

mecab のインストール

$ sudo port install mecab


入った

$ port installed | grep mecab
  mecab @0.994_0+ipadic (active)
  mecab-base @0.994_0 (active)
  mecab-ipadic @2.7.0-20070801_0 (active)
$ mecab -v
mecab of 0.994

mecab-ruby のインストール


mecab-rubyは、ruby にバインドする必要があるらしいので、
gem install だとできないし、rvmなので、macportsからも
インストールできない


参考 → rvmで入れた各rubyにmecab rubyバインディングをインストールする - 橋本詳解


上記を参考にインストール


まずは、mecab-ruby を取得

$ wget http://mecab.googlecode.com/files/mecab-ruby-0.994.tar.gz .


解凍

$ tar -zxvf mecab-ruby-0.994.tar.gz
$ cd mecab-ruby-0.994/
$ rvm list

rvm rubies

=> ruby-1.8.7-p174 [ x86_64 ]
   ruby-1.9.2-p180 [ x86_64 ]


とりあえず、1.8.7 に入れる


READMEにやり方が書いてあったので、続きはそちらを参照

$ cat README 
MeCab ruby module

$Id: README,v 1.1.1.1 2005/12/03 14:18:52 taku-ku Exp $;

1. Installation

   % ruby extconf.rb
   % make
   % su
   # make install

2. How to use?

   See 'test.rb' as a sample program.
$ ruby extconf.rb
checking for main() in -lmecab... yes
checking for main() in -lstdc++... yes
checking for mecab.h... yes
creating Makefile
$ make
g++ -I. -I. -I/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/1.8/i686-darwin10.7.0 -I. -DHAVE_MECAB_H  -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE   -fno-common -g -O2  -fno-common -pipe -fno-common  -I/opt/local/include   -c MeCab_wrap.cpp
cc -dynamic -bundle -undefined suppress -flat_namespace -o MeCab.bundle MeCab_wrap.o -L. -L/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib -L.     -lruby -lstdc++ -lmecab  -ldl -lobjc  
$ sudo make install
Password:
/usr/bin/install -c -m 0755 MeCab.bundle /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0


なんかあっさり終わった。
できたのかね?


サンプルコード動かしてみる

$KCODE = 'u'
require 'MeCab'
m = MeCab::Tagger.new("-Ochasen")
print m.parse("今日もしないとね")
dyld: lazy symbol binding failed: Symbol not found: __ZN5MeCab12getLastErrorEv
  Referenced from: /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0/MeCab.bundle
  Expected in: flat namespace


dyld: Symbol not found: __ZN5MeCab12getLastErrorEv
  Referenced from: /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0/MeCab.bundle
  Expected in: flat namespace


んー???

mecab-rubymacportsのライブラリにリンクするように修正


otool -L でリンクしているライブラリを調査できるらしい

otool -L  MeCab.bundle
MeCab.bundle:
        /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/libruby.dylib (compatibility version 1.8.0, current version 1.8.7)
        /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
        /usr/lib/libmecab.1.dylib (compatibility version 2.0.0, current version 2.0.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)
        /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)


MacPortsで入れたなら、/opt/local/lib/libmecab.2.dylib とかになるはず…
元々あるライブラリにリンクされてる??


事例があった → mecab-rubyをMacにインストールするメモ | インサイドフラッギング


extconf.rb をいじるらしい

require 'mkmf'

mecab_config = with_config('mecab-config', 'mecab-config')
use_mecab_config = enable_config('mecab-config')

`mecab-config --libs-only-l`.chomp.split.each { | lib |
  have_library(lib)
}

$CFLAGS += ' ' + `#{mecab_config} --cflags`.chomp

have_header('mecab.h') && create_makefile('MeCab')


使用するライブラリのディレクトリを指定するために、$LDFLAGSを追加

require 'mkmf'

mecab_config = with_config('mecab-config', 'mecab-config')
use_mecab_config = enable_config('mecab-config')

$LDFLAGS += ' -L' + `#{mecab_config} --libs-only-L`.chomp

`mecab-config --libs-only-l`.chomp.split.each { | lib |
  have_library(lib)
}

$CFLAGS += ' ' + `#{mecab_config} --cflags`.chomp

have_header('mecab.h') && create_makefile('MeCab')

もっかいインストール

$ make clean
$ ruby extconf.rb
checking for main() in -lmecab... yes
checking for main() in -lstdc++... yes
checking for mecab.h... yes
creating Makefile
$ make
g++ -I. -I. -I/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/1.8/i686-darwin10.7.0 -I. -DHAVE_MECAB_H  -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE   -fno-common -g -O2  -fno-common -pipe -fno-common  -I/opt/local/include   -c MeCab_wrap.cpp
cc -dynamic -bundle -undefined suppress -flat_namespace -o MeCab.bundle MeCab_wrap.o -L. -L/Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib -L.  -L/opt/local/lib    -lruby -lstdc++ -lmecab  -ldl -lobjc  
$ sudo make install
Password:
/usr/bin/install -c -m 0755 MeCab.bundle /Users/kobayashi/.rvm/rubies/ruby-1.8.7-p174/lib/ruby/site_ruby/1.8/i686-darwin10.7.0


もっかいサンプルコード実行したところ、激しく文字化け

&#228;&#187;	\&#191;\&#171;\&#224;\&#233;	&#228;&#187;	&#204;&#190;&#187;&#236;-°&#236;&#200;&#204;		
&#138;&#230;&#151;\&#227;&#130;&#130;&#227;&#129;&#151;&#227;&#129;	&#138;&#230;&#151;\&#227;&#130;&#130;&#227;&#129;&#151;&#227;&#129;	&#138;&#230;&#151;\&#227;&#130;&#130;&#227;&#129;&#151;&#227;&#129;	&#181;&#173;&#185;&#230;-°&#236;&#200;&#204;		
&#170;&#227;	&#170;&#227;	&#170;&#227;	&#204;&#190;&#187;&#236;-&#184;&#199;&#205;&#173;&#204;&#190;&#187;&#236;-&#193;&#200;&#191;\		
&#129;&#132;&#227;&#129;¨&#227;&#129;&#173;	&#129;&#132;&#227;&#129;¨&#227;&#129;&#173;	&#129;&#132;&#227;&#129;¨&#227;&#129;&#173;	&#181;&#173;&#185;&#230;-°&#236;&#200;&#204;		
EOS

辞書ライブラリをutf-8のものを参照するように修正


辞書ライブラリが原因らしい


/opt/local/etc/mecabrc

;
; Configuration file of MeCab
;
; $Id: mecabrc.in,v 1.3 2006/05/29 15:36:08 taku-ku Exp $;
;
dicdir =  /opt/local/lib/mecab/dic/sysdic

; userdic = /home/foo/bar/user.dic

; output-format-type = wakati
; input-buffer-size = 8192

; node-format = %m\n
; bos-format = %S\n
; eos-format = EOS\n


/opt/local/lib/mecab/dic/sysdic なる辞書を使っているらしいので見てみると…

$ ll /opt/local/lib/mecab/dic                                                          
total 8
drwxr-xr-x  11 root  admin  374 12 12 12:10 ipadic-eucjp/
lrwxr-xr-x   1 root  admin   12  6  9  2012 sysdic@ -> ipadic-eucjp


なんかeucとか言ってる
そら化ける


本当は、mecabのvariantsにutf-8があるから、それを入れるのが正解だったっぽい


仕方ないので以下を参考に、utf-8の辞書を別途インストール

$ sudo port install mecab-ipadic-utf8


入った

port installed | grep mecab
  mecab @0.994_0+ipadic (active)
  mecab-base @0.994_0 (active)
  mecab-ipadic @2.7.0-20070801_0 (active)
  mecab-ipadic-utf8 @2.7.0-20070801_0 (active)


辞書が追加されたことを確認

 ll /opt/local/lib/mecab/dic
total 8
drwxr-xr-x  11 root  admin  374 12 12 12:10 ipadic-eucjp/
drwxr-xr-x  11 root  admin  374 12 12 13:38 ipadic-utf8/
lrwxr-xr-x   1 root  admin   12  6  9  2012 sysdic@ -> ipadic-eucjp


シンボリックリンクを貼り直す

$ sudo rm sysdic
$ sudo ln -s ipadic-utf8/ sysdic
$ ll
total 8
drwxr-xr-x  11 root  admin  374 12 12 12:10 ipadic-eucjp/
drwxr-xr-x  11 root  admin  374 12 12 13:38 ipadic-utf8/
lrwxr-xr-x   1 root  admin   12 12 12 13:44 sysdic@ -> ipadic-utf8/


もう一度サンプルコード実行

今日	キョウ	今日	名詞-副詞可能		
も	モ	も	助詞-係助詞		
し	シ	する	動詞-自立	サ変・スル	未然形
ない	ナイ	ない	助動詞	特殊・ナイ	基本形
と	ト	と	助詞-接続助詞		
ね	ネ	ね	助詞-終助詞		
EOS


おおお、動いた!

形態素解析結果の取得方法
$KCODE = 'u'
require 'MeCab'
m = MeCab::Tagger.new("-Ochasen")
node = m.parseToNode("今日もしないとね")
while node do
  puts "#{node.surface} : #{node.feature}"
  node = node.next
end


インクリメントに取得していくのには、parseToNode で取得して、nextで次を取得
surfaceで、分割された単語を返して、featureで情報を取得
あと、最初と最後にヘッダとフッタみたいのが付く


こんな感じ

 : BOS/EOS,*,*,*,*,*,*,*,*
今日 : 名詞,副詞可能,*,*,*,*,今日,キョウ,キョー
も : 助詞,係助詞,*,*,*,*,も,モ,モ
し : 動詞,自立,*,*,サ変・スル,未然形,する,シ,シ
ない : 助動詞,*,*,*,特殊・ナイ,基本形,ない,ナイ,ナイ
と : 助詞,接続助詞,*,*,*,*,と,ト,ト
ね : 助詞,終助詞,*,*,*,*,ね,ネ,ネ
 : BOS/EOS,*,*,*,*,*,*,*,*


eachとかできないのに違和感があるので、eachできるようにしている人が居た

MySQLで、やたら重いクエリが発生したので、原因を究明するまでの話

今後の為に原因の追求方法をメモ

まずは、どのクエリが重いのか調査


SHOW PROCESSLIST


実行中のプロセスが表示される
処理時間がTimeに表示されたり、実行中のコマンドがInfoに表示されるので、重い処理がどれだか判る


ちなみに、KILLコマンドで、処理中のプロセスを殺すことも可能

EXPLAIN SELECT でどんな風に検索されているか確認


今回は、indexがきちんと使われているか、確認の為に使用

+----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys | key        | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+
|  1 | SIMPLE      | test_table | index | NULL          | test_index | 4       | NULL |   10 | Using where |
+----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+
  • typeがindexならば、indexが使われている
  • typeがALLならば、フルテーブルスキャン(重い)


MySQL :: MySQL 4.1 リファレンスマニュアル :: 5.2.1 EXPLAIN 構文(SELECT に関する情報の取得)


indexが使われているが、やたら重いのでボトルネックを確認する

SHOW PROFILE でクエリの処理毎の実行時間を見る


やたら重い箇所があったらそこがボトルネックなはず

mysql> SET profiling = 1;
mysql> SELECT ...
mysql> SHOW PROFILE;

+--------------------------------+------------+
| Status                         | Duration   |
+--------------------------------+------------+
| starting                       |   0.000081 | 
| checking query cache for query |   0.000484 | 
| Opening tables                 |   0.000052 | 
| System lock                    |   0.000020 | 
| Table lock                     |   0.000092 | 
| init                           |   0.000283 | 
| optimizing                     |   0.000137 | 
| statistics                     |   0.000052 | 
| preparing                      |   0.000089 | 
| executing                      |   0.000002 | 
| Sorting result                 |   0.000010 | 
| Sending data                   | 115.337027 | 
| end                            |   0.000066 | 
| query end                      |   0.000015 | 
| freeing items                  |   0.000201 | 
| storing result in query cache  |   0.000022 | 
| logging slow query             |   0.000001 | 
| logging slow query             |   0.033929 | 
| cleaning up                    |   0.000034 | 
+--------------------------------+------------+


Sending dataは、読み込みと絞りこみに掛かってる時間

This is quite a misleading status. It should be called "reading and filtering data".


MySQL :: MySQL 5.1 リファレンスマニュアル (オンラインヘルプ) :: 8.5.5.33 SHOW PROFILES 構文


普通に、検索結果をwhereするのに時間が掛かってる様子
この辺でMySQLのファイルが上手くキャッシュメモリに乗っていないのではないか? という疑惑が


vmstatコマンド で、disk ioを調査


disk io 等を調査できるlinuxのコマンド
詳しくはこっち → linuxで空きメモリがどんくらいあるか確認する方法 - かせいさんとこ


$ vmstat -SM 10
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0    361      5      1    267    0    0     0     2  987   85  0  0 100  0  0

〜 検索開始 〜

 0  1    361      5      1    267    0    0   348     0 1024   90  1  2 55 43  0
 0  1    361      5      1    267    0    0   610     3 1128  192  0  3  0 97  0


SELECT文発行直後に、ディスクioが大量に発生して、さらにcpuのio待ち率が97%とかになってる!


調べてみたら、テーブル用のファイルがOSのディスクキャッシュより大きかったために、SELECT文を発行する度にHDDにアクセスしていたのが原因だった
今回は、テスト用の環境なので、データ量を減らす事で解決
ハマりました...

おまけ


MySQLのtable_cacheは、テーブルそのもののキャッシュではなく、テーブルに必要なファイルのハンドラをキャッシュする領域
テーブルそのもののキャッシュと勘違いしてました...

linuxで空きメモリがどんくらいあるか確認する方法


freeコマンドを見る

$ free -m
             total       used       free     shared    buffers     cached
Mem:           497        380        116          0          2        145
-/+ buffers/cache:        233        264
Swap:          511        349        162


-mオプションは、容量をメガバイト単位で表示するオプション
Memが実メモリで、Swapが仮想メモリ

  • total : メモリの総量
  • used : 使用したメモリ寮
  • shard : 共有メモリ → プロセス間で共有できる領域用のメモリらしい
  • buffers : バッファに使われているメモリ
  • cached : ページキャッシュに使われているメモリ

バッファって?


バッファ → 緩衝器、緩衝帯の意
I/Oアクセスする時に、直接I/Oに行くのではなく、キャッシュ経由でアクセスさせる為のメモリ
データが来ているのに受け手が処理できないので、使うはずのデータを貯めておくらしい

ページキャッシュって?


キャッシュ → 隠し場所、貯蔵品
ページキャッシュ → ファイルのデータを入れておいて、再度アクセスがあった時に素早く返す為の領域
1度アクセスがあったファイルは、再度使われる可能性が高いので保持している
また、linuxはメモリに空きがある限りページキャッシュを蓄えるので、Memのfreeが少ないからといって、メモリが足りない訳ではない
アプリにて実メモリが必要になったら、ページキャッシュは破棄されて、アプリにメモリが割り当てられるはず

実際の空き領域はどうやってみるの?


空き領域をチェックするのに必要なのは、上の "-/+ buffers/cache:"
これは、バッファ、キャッシュメモリを引いた値を出していて、使用可能な空き領域を示している

Swapが使われているってことは、メモリが足りないの?


一概にそうとは言えない
linux側で使用していないアプリのメモリをSwapに移して、ページキャッシュに割り当てる場合があるので

それじゃあメモリが足りてるかどうかはどうやって見るの?


まずは、"-/+ buffers/cache"の freeがあからさまに少ない場合
後は、スラッシングが発生しているかチェックする

スラッシング?


メモリ不足の為、Swapと実メモリの行き来が大量に発生して、処理が遅延する現象
vmstatコマンドでチェック可能

$ vmstat -SM 10
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0    361      5      1    267    0    0     0     2  987   85  0  0 100  0  0
 0  1    361      5      1    267    0    0   348     0 1024   90  1  2 55 43  0
 0  1    361      5      1    267    0    0   610     3 1128  192  0  3  0 97  0


-SMは、単位をメガバイトにしてるオプション
10 は、10秒毎に表示するオプション


各表示項目の意味は、以下を参照


ここで、si, so が多発していたらスラッシングが発生している

メモリが足りなかったら?


メモリを足すか、アプリがメモリを使わないように修正するか
頑張れ

Phantom.js+QUnitでjavascriptの自動テスト環境を作りたい

Phantom.jsって何?


PhantomJS is a headless WebKit with JavaScript API.

headless?


スクリーン表示の無い、CUIで動作するブラウザ

  • CUIで動作するので、自動テストに組みやすい
  • 中身はWebKitなので、jsやHTML5の動作が可能!

他に特徴は?

色んなテストフレームワークに対応

jasmine, capybara, QUnit, mocha, webdriver, yui test, busterjs, funcunit, robot framework

他のCUIブラウザと比べて軽い

ブラウザの動作をエミュレートしている訳ではない為

本当にHedless(LinuxX11を使ってない)

なので、AmazonEC2や、Herokuでも使える

インストールが簡単
  • ダウンロードしてすぐ使える
  • Macなら、Homebrewから取得可能
  • MacPortsは更新が止まってるらしい

導入


自分の環境は、MacPortsなので、手動でインストール

$ wget http://phantomjs.googlecode.com/files/phantomjs-1.7.0-macosx.zip
$ unzip phantomjs-1.7.0-macosx.zip
$ cd phantomjs-1.7.0-macosx
$ bin/phantomjs -v
1.7.0

簡単。

jsを使って動かす


まずは、hello world

console.log('hello, world!');
// メインスレッドが終わっても終了しないので、
// 明示的に終わらせる必要がある
phantom.exit();
$ bin/phantomjs test.js
Hello, world!

ページを読み込む

var page = require('webpage').create();
page.open('http://google.com', function () {
  // ページのキャプチャ
  page.render('google.png');
  phantom.exit();
});



標準だと、594x442px でレンダリングされてる
page.viewportSizeでサイズの変更が可能

page.viewportSize = { width: 480, height: 800 };

内部でjavascriptも動くの?


onloadで、div id="test" に、文字列を表示する、htmlを用意して実験

<html>
 <head>
  <meta charset="utf-8">
  <title>title</title>
  <script>
    window.onload = function(){
      document.getelementbyid("test").innerhtml='test';
    }
  </script>
 </head>
 <body>
   <div id="test1"></div>
 </body>
</html>


test.js にて、htmlを読み込んで、onloadで表示されるdiv id="test"の中の文字列を取得して表示する

var page = require('webpage').create();
page.open('01.html', function () { // ページアクセス
  setTimeout(function(){
    console.log(
      // ページ内でJSを動かすメソッド
      page.evaluate(function () {
        return document.getElementById("test").innerHTML;
      })
    );
    phantom.exit();
  }, 1000);
});
$ bin/phantomjs test.js
test


うごいた!

イベント送受信


今度はこんなHTMLを用意
div id="test" をクリックすると、confirmが出て分岐

// 〜 中略 〜
    window.onload = function(){
      document.getElementById("test").addEventListener("click", function(){
        if(confirm("click?")){
          alert("yes");
        }else{
          alert("no");
        }
      });
    }
  </script>
 </head>
 <body>
   <div id="test">click me!</div>
 </body>
</html>
var page = require('webpage').create();
page.open('01.html', function () { // ページアクセス
  page.onConfirm = function(msg){ // confirm発生時にトリガ
    console.log("confirm : "+msg);
    return true; // 真偽値でOK、キャンセルを決める
  }
  page.onAlert = function(msg){ // アラート発生時にトリガ
    console.log("alert : "+msg);
  }
  page.includeJs("jquery.min.js", function(){ // jQueryをinclude
    var offset = page.evaluate(function(){ // リンクのoffsetを取得
      return $("* test").offset();
    });
    page.sendEvent('click', offset.left+1, offset.top+1); // クリック
  });
});
$ bin/phantomjs test.js
alert : jquery.min.js *  includeJs完了時に、alertイベントが飛んでるっぽい
confirm : click?
alert : yes


イベントを受け取って、confirmでyesを選択している
ちなみに、confirmを受け取らない場合、デフォルトでは「いいえ」を選択している様子

QUnit連携


連携には、examples/run-QUnit.js を使う
試しに、QUnitのテストをPhantom.jsで実行してみる

$ examples/run-QUnit.js ../jquery-QUnit-4e03a4b/test/index.html; echo $1
'waitfor()' finished in 1901ms.
tests completed in 1771 milliseconds.
596 tests of 596 passed, 0 failed.bin/phantomjs
0
  • 成功の場合、戻り値は0
  • 失敗の場合は、1を返す
まとめ
  • 内部的には、QUnitをopenして、結果を出力するDOMをウォッチしているだけなので、ちょっといじれば、エラーが発生したテストを出力するとかできそう
  • 戻り値を返すので、Jenkinsへの組み込みも簡単にできそう
  • 全ブラウザのテストは無理だけど、CIに組み込めるのが良いと思った
  • デザイナさんがjavascript書いて、テストもQUnitで書くという状況ならば、Phantom.js と組み合わせて、CIに組み込むのがベター?