株式会社インフィニットループ PHPとスマホアプリ開発を行う札幌のシステム会社

技術ブログ

  1. トップ>
  2. 技術ブログ>
  3. 2013 8月の記事一覧

2013年08月27日 (火)

著者 : 

OS Xで作業効率を5%上げるSlateの紹介

こんにちは。今回は過去に勉強会で発表したSlateについて紹介します。

Slateとは

jigish/slate – Github

Slateとは、OS X上で動作し、ウィンドウのフォーカス移動・リサイズ・移動などをキーボードから楽に行えるようにするアプリケーションです。ウィンドウ操作を高速に、マウスから手を離さずに行えるようになります。

slate_resize_example

実際にSlateでウィンドウのリサイズ/移動を行っているところのgifアニメです。このような操作の他、

  • ウィンドウの配置の記録/復元
  • 特定のアプリケーションや、指定した方向にフォーカスを移す
  • スクリーン間でウィンドウを飛ばす
  • ウィンドウにヒントを表示してフォーカス移動する

などの操作をキーボードから行うことができます。

Slateの設定

slate_menu

Slate自体はステータスバーに常駐する簡素なアプリケーションで、細かな設定を行うGUIなどは用意されていません。Slateの設定は独自の書式の.slateかJavaScriptで.slate.jsに記述します。ある程度の操作は.slateで記述できますが、より複雑な操作を行いたくなったら.slate.jsを書くと良いでしょう。

詳細な設定方法はgithub上によくまとめられています。
README.mdには使用できるoperationなどが, WikiのJavaScript-ConfigsにはJavaScriptから扱える各種APIが記述されています。

例えばvim-likeにalt + hjklでフォーカス移動がしたい場合は次のように記述できます。

// .slate
bind l:alt focus right
bind h:alt focus left
bind k:alt focus up
bind j:alt focus down

少々面倒ですが、慣れると快適なので自分好みに設定していくことをおすすめします。
参考に私の設定ファイルの一部を貼っておきます。私は.slate.jsに全て書いていますが、.slate.slate.jsは併用することができます。

// .slate.js
var util = {
  // alt + ..
  key: function(k, mod) {
    return k + ':alt' + (mod ? ',' + mod : '');
  },
  focusWindow: function(f) {
    var hit = false;
    slate.eachApp(function(app) {
      if (hit) return;
      app.eachWindow(function(win) {
        if (hit) return;
        if (f(win)) {
          win.focus();
          hit = true;
        }
      });
    });
  },
  nextScreen: function(screen) {
    return slate.screenForRef(String( (screen.id()+1)%slate.screenCount() ));
  }
};

// return     .. iTermを起動
slate.bind(util.key('return'), function(win) {
  slate.shell('/usr/bin/open -n -a iTerm');
});

// r+ctrl     .. Slateの再起動
slate.bind(util.key('r', 'ctrl'), function(win) {
  slate.operation('relaunch').run();
});

// hjkl       .. その方向へフォーカス移動
slate.bind(util.key('h'), slate.operation('focus', { direction: 'left' }));
slate.bind(util.key('j'), slate.operation('focus', { direction: 'down' }));
slate.bind(util.key('k'), slate.operation('focus', { direction: 'up' }));
slate.bind(util.key('l'), slate.operation('focus', { direction: 'right' }));

// i          .. 下に隠れているウィンドウをフォーカス
slate.bind(util.key('i'), slate.operation('focus', { direction: 'behind' }));

// o          .. スクリーン間でフォーカスを移動
slate.bind(util.key('o'), function(win) {
  var next = util.nextScreen(slate.screen());

  util.focusWindow(function(win) {
    return win.screen().id() == next.id();
  });
});

// o+shift    .. 次のスクリーンへ飛ばす
slate.bind(util.key('o', 'shift'), function(win) {
  if (!win) return;
  var next = util.nextScreen(win.screen());

  win.move(next.visibleRect());
});

// j+shift    .. 4隅に飛ばす
var corners = slate.bind(util.key('j', 'shift'), slate.operation('chain', {
  operations: _.map(['top-right', 'bottom-right', 'bottom-left', 'top-left'], function(d) {
    return slate.operation('corner', {
      direction: d,
      width: 'screenSizeX/2',
      height: 'screenSizeY/2'
    });
  })
}));

// k+shift    .. 左右に飛ばす
slate.bind(util.key('k', 'shift'), slate.operation('chain', {
  operations: _.map(['left', 'right'], function(d) {
    return slate.operation('push', {
      direction: d,
      style: 'bar-resize:screenSizeX/2'
    });
  })
}));

// h+shift   .. ウィンドウが左にあるなら縮小, 右にあるなら拡大
slate.bind(util.key('h', 'shift'), function(win) {
  if (!win) return;
  var rect = win.rect();
  var bounds = win.screen().visibleRect();
  if (bounds.x + bounds.width - 30 < rect.x + rect.width) {
    rect.x -= bounds.width * 0.05;
    rect.width = bounds.x + bounds.width - rect.x;
  } else {
    rect.width -= bounds.width * 0.05;
  }
  win.doOperation('move', rect);
});

// l+shift   .. ウィンドウが右にあるなら縮小, 左にあるなら拡大
slate.bind(util.key('l', 'shift'), function(win) {
  if (!win) return;
  var rect = win.rect();
  var bounds = win.screen().visibleRect();
  if (rect.x < bounds.x + 30) {
    rect.x = bounds.x;
    rect.width += bounds.width * 0.05;
  } else {
    rect.x += bounds.width * 0.05;
    rect.width -= bounds.width * 0.05;
  }
  win.doOperation('move', rect);
});

// m          .. 最大化
slate.bind(util.key('m'), function(win) {
  if (!win) return;
  var bounds = win.screen().visibleRect();
  win.doOperation('move', bounds);
});

最後に

勉強会で使用したスライドに手を加えたものを貼っておきます。
この記事内ではSlateの基礎に視野を絞りましたが、スライド内では併用したいアプリケーションやSlateそのものの拡張についても言及しています。

スライドを開く

2013年08月20日 (火)

著者 : 

これだけ見れば大丈夫!ーMySQLパフォーマンス監視のツボ(システム編)

こんにちは nob です。
前編 これだけ見れば大丈夫!ーMySQLパフォーマンス監視のツボ(クエリ編) の記事から1年半が経過してしまいました。ちょっと長いお休みでしたが、その間に蓄えた MySQL パフォーマンス監視の実戦経験を(システム編)としてお届けいたします!

今回の(システム編)で紹介するツボは 4 つです。(クエリ編)のツボに加えて、この4つに注目して頂ければ MySQL のパフォーマンス監視もバッチリです。

  • (ツボ1)Load Average < (1 + (cpu数-1)/3) 
  • (ツボ2)Checkpoint Age が水平線になったら要注意
  • (ツボ3)MyISAM は無いよね監視
  • (ツボ4)万能選手スローログ

なお前編と同様この記事では監視ツールとして CactiPercona MySQL Monitoring Template for Cacti (前編で紹介した better-cacti-templates の後継プロジェクト)を前提にしていますが、Munin やコマンドラインの mysql など、他のツールでも見るべき点は同じですので応用してください。

(ツボ1)Load Average < (1 + (cpu数-1)/3) 

右側の式は後ほど説明するとして、Load Average 監視って当たり前?ですよね。でも実際には Load Average にもいろいろなケースがあります。

  • Disk I/O が増えた場合に Linux カーネルのフラッシュのプロセスが CPU を使ったり、あるいはコンテキストスイッチでLA増加
  • アクセスしているテーブルのデータが殆どバッファープールに乗っている場合には相対的に I/O 負荷が低くなるため、クエリが増えた場合にその分ロック機構で使われるスピンロックなどの排他制御やソート処理で LA 増加
  • インデックスが使われず広範囲に読み込みスキャンするようなクエリを誤ってリリースしたような場合にも LA増加
  • クラウドのインスタンスがホスト側の障害で不良だった場合にも、アクセス数は変わって無いのに LA増加

などがあります。このように CPU, Disk I/O, クエリ負荷、クラウドの不良インスタンスまで、あらゆる状況を総合的に把握出来るのがやっぱり CPU のビジーで、その代表的なグラフが  Load Average です。

load_average_example

このグラフはあるシステムの2日間の Load Average のグラフです。左のグラフの 13:00 付近から Load Average が急上昇しました。これは新しく使われ初めたある機能により、実行回数も多いクエリのジョインやソートの行数が急に増加したため Load Average が上昇したものです。探索行数が多いと I/O が増加するイメージがありますが、実際には OS のキャッシュや MySQL のバッファプールに乗るため I/O は増えずに CPU だけ負荷が増える事があります。この場合も Load Average の上昇で兆候を発見し、まだ問題ない程度のうちに原因クエリを特定し改善することで、夜のピーク時間帯には通常ピーク値(青線)に戻す事が出来ました。

このように監視項目として有効な Load Average ですが、さてしきい値はいくつにしたらよいでしょうか? Linux カーネルのマルチコアでは

  • キャッシュのヒット率やメモリ一貫性維持のオーバーヘッドを考慮し、プロセッサのコアはある程度偏らせて使う
  • プロセス起動時は空いているコアを選択するが、MySQL はプロセス起動はあまり発生しないのでこの分散の恩恵は受けない
  • かなり負荷が高くなるとプロセスを動かしたままコアを移動するマイグレーションがおきるが、滅多に発生しない

という CPU を偏って使う特徴があるので、ググってよく出てくる 「Load Average < CPU数」は適正値にはなりません。とは言えしきい値も必要です。そこで、完全な経験則だけで理論の裏付けはありませんが、MySQL のインスタンスの Load Average のしきい値を出しました。

  • MySQL では Load Average  <  (1 + (cpu数 -1) /3)

です!よくある8コアのマシンですと、LA < 3.3 までが適正値です。ハイ言っちゃいました!

(ツボ2)Checkpoint Age が水平線になったら要注意

Redis や MySQL Cluster など多くのデータ永続化インメモリDBはデータはメモリ上で、HDDのシーケンシャル書き込みは速いのでログはディスク上で、という構造になっています。InnoDB もデータをメモリ上だけに持てるサイズがあり、このサイズまではインメモリDBと同じようにログだけディスクに書いた状態で動作します。このメモリ上に持ったままでよいサイズというのが  innodb_log_file_size です。そしてまだディスクにフラッシュされていないデータのサイズが Checkpoint Age になります。漢のコンピュータ道 でも128M程度までというアドバイスがありますが、一方で、ログファイルサイズを大きくすることで、高い性能を引き出そうというチューニング手法もあります。

Checkpoing Age が成長すると緩やかにディスクへのランダム書き込み(チェックポイント)が始まります。チェックポイントと、シーケンシャルなログ書き込みの量のバランスが取れていれば、 Checkpoint Age もバランスが取れ、上がったり下がったりしつつアクセス量と同じようなグラフの形になります。

checkpoing_age_low

使用ディスクが RAID などでシーケンシャル書き込みがランダム書き込みに比べてかなり速い場合は、ログの書き込みの方が多くなりCheckpoing Age が成長します。そして一定の割合(バージョンによる。90%等)を超えると今度は強いチェックポイントが始まります。この強いランダム書き込みとログのシーケンシャル書き込みでバランスが採れていれば、Checkpoing Age は高止まりした波グラフ(下のグラフの青い部分)になります。全力でディスクを使っている状態です。

checkpoint_age_middle

ここからさらにログ書き込み量が上回りCheckpoint Age が上限に達すると、全てのトランザクションを停止してチェックポイントを行うようになります。そして Checkpoint Age が下がりトランザクションが再開されますが、更新が多いのでまたすぐ上限に達します。この動作を繰り返し続けるので Checkpoint Age は下のグラフの様に上限値でほぼ水平線になります。このグラフの形でも全トランザクション停止時間が1秒未満など微視的であれば、上の例と同じく全力運転状態と言えます。

checkpoint_age_high

一方、高速動作を狙って innodb_log_file_size を〜GBというようにバッファプール並みに大きくしてると、全トランザクションをブロックして書き込む量も多くなりますので、停止時間が数分間にも達し大きな問題となる事があります。こうしたトランザクション停止時間は Checkpoint Age のグラフでは読み取ることが難しいですから、Checkpoing Age が水平線になったらトランザクション全停止が発生していないかアプリのログなどで確認するようにしましょう。

ドリコムさんで体験された「ちゅどる」もこの現象になります。(ソーシャルゲーム スケールアウトの歴史 p81 参照)またPercona の Adaptive Checkpointing  はこの「ちゅどる」にならないようにチェックポイントの強さを自動的に調節する機構です。

(ツボ3)MyISAM は無いよね監視

現在は MySQL = InnoDB といっても過言ではないと思います。MyISAM のテーブルは作成していません。それなのに突然 MyISAM Indexes のグラフが急上昇することがあります。大抵はシステムも高負荷になっています。

myisam_indexes

これはジョインやソートのために MySQL 内部に作成されるテンポラリテーブルが、メモリ上には乗り切らず MyISAM テーブルとして作成され使用されている為発生します。MyISAM Indexes のグラフを監視して、発見したら

  • max_heap_table_size  と tmp_table_size をさらに大きな値に変更してメモリから溢れない様にする
  • クエリを見直してテンポラリテーブルの使用量を下げる

などの対応が必要です。

(ツボ4)万能選手スローログ

MySQL パフォーマンスチューニングや監視で、もっとも有効なのがスローログです。理由は単純明快で、あらゆるDBの問題は「遅い」という症状につながるからです。そんな万能選手のスローログですが、完全無欠ではなく注意すべき点が2点あります。

  1. 完了したクエリだけが記録される。デッドロックなどロールバックされたクエリは完了していないため記録されません。記録されているスローログは被害を受け遅延したクエリで、原因となった加害者のクエリが最終的にはロールバックされ記録されていないというケースもあります。あくまでも完了したクエリなんだという前提で読みましょう。
  2. クエリが原因だと先入観を持たない。あるクエリの実行時間が想定以上に長い場合、ついそのクエリの書き方に注意が向きがちですが、先入観を持たずに調査しましょう。例をあげますと、 SELECT MASTER_POS_WAIT(logname, position, 2) という、本来2秒で応答すべきスレーブのマスター追いつき確認関数 MASTER_POS_WAIT の実行に何十秒もかかっていて、 実はインスタンス自体がハングアップしていたのが原因でクエリも MySQL も原因では無かった、というケースなどがあります。

最後に

MySQL パフォーマンス監視のツボ(システム編)としてサーバー運用管理での経験を共有させて頂きました。是非ご活用ください!

2013年08月19日 (月)

著者 : 

Raspberry Pi上でゆっくりボイスを音声合成する「AquesTalk Pi」がβ公開されたようなので試してみました

matsuiです。
半年ほど前、Raspberry Pi上でゆっくりボイスをしゃべらせるラズベリーゆっくりの構築という記事を書きました。
 
最近、公式からRaspberry Pi上でゆっくりボイスを音声合成する「AquesTalk Pi」というソフトがβ公開されたようなので、試してみました。
→ N.Yamazaki’s blog 【β版公開】Raspberry Piで音声合成する「AquesTalk Pi」
 
 

使い方

使い方は簡単で、Raspberry Pi上に落としてきたtar.gzを展開して実行するだけです。

現在はまだベータ版で、商用利用も禁止とのことなので、とりあえず家のラズベリーパイで試してみました。

$ uname -a
Linux raspberrypi 3.6.11+ #474 PREEMPT Thu Jun 13 17:14:42 BST 2013 armv6l GNU/Linux

OSはRaspbianなのですが、上記の環境で問題無く動きました。
 
 
UTF8で引数を渡すと標準出力でwavファイルが出力されますので、保存するかパイプでそのまま食わせてやればOKです。

$ ./AquesTalkPi "ゆっくりしていってね。漢字も読めるよ。" | aplay

 
スピードをやや遅めると、よりゆっくりっぽさが出ます。

$ ./AquesTalkPi -s 80 "スピードやボリューム調整もできるよ" | aplay

 
その他のオプションはヘルプで確認できます。

$ ./AquesTalkPi -h

 
 

まとめ

自宅では、ゴミの日をしゃべらせたり、Googleカレンダー上にある1日の予定を喋らせたりしているのですが、これがラズベリーパイだけで完結できるようになるのは嬉しいです。
セットアップや使い方も凄く簡単で素晴らしいです。

しかも本体から買いそろえてもトータル数千円で済んで、なおかつコンパクトというのがいいですね。
家にテキストを読み上げる機器があるというのは、思いのほか便利なものですので、ぜひ試してみてください。

ビジネス用途にも色々と使えそうなので、はやく正式版になってライセンス販売もして欲しいものです。

raspbery_ukkuri

2013年08月06日 (火)

著者 : 

はじめてAnsibleを使う人が知っておきたい7つのモジュール

こんにちは。makiです。
先月札幌で開催したPHP Matsuri 2013 では、Ansibleについて LT 発表してきました。

発表スライドはSlideshareにおいてあります。

Ansibleとは

  • 構成管理ツール(ChefやPuppet等と同じ)
  • playbookという脚本ファイル(Chefでいうrecipe)にやりたいことをyaml形式で書いて実行するだけ
  • 別にVagrantとの関係が必須じゃない
  • vagrant provisionよりもansible-playbookを単体実行のほうが便利なことも多い

READMEには設計思想としてこのように書かれています。

  • とってもシンプルなセットアップ
  • デフォルトで超速くて並列処理ができる
  • サーバーもデーモンもいらない。sshdだけ
  • クライアントのboxに追加で必要なものなどない
  • どんな言語でもモジュールを書くことができる
  • スクリプト作成のために用意されたステキなapi
  • rootユーザでなくても使える
  • とっても簡単な設定管理で利用できるよ

難しいことは何もなく、我々がやることはyaml形式の設定ファイルを書いて実行するだけです。
今回は、120以上ある標準モジュールの中から、特によく使うものを7つ厳選してご紹介します。

(続きを読む…)

2013年08月01日 (木)

著者 : 

PHPのコレクション処理ライブラリUnderbar.phpの紹介

こんにちわちわ。
以前に便利なVimのOperatorをマスターするを書きましたemonkakです。

今回は私が個人的に開発を進めていて以前に社内勉強会で発表しました、PHPのコレクション処理ライブラリのUnderbar.phpの紹介をしたいと思います。

PHPのarrayなんとか関数は使い勝手が悪い

PHPには配列処理のための関数がたくさんありますが、array_map()の引数の順番が変だったりarrayがそもそもオブジェクトではないのでメソッドチェインができなかったり使い勝手が良くありません。

$xs = range(1, 10);
$twice = function($n) { return $n * 2; };

array_slice(array_map($twice, $xs), 0, 3);
// [2, 4, 6]

また、これらの関数はarray専用なので、IteratorのようなTraversableなオブジェクトを引数に取ることはできませんし、処理を遅延してIteratorを返すなんてことも当然できません。

そこでarrayTraversableなオブジェクトを同じように扱えて、Iteratorを利用して遅延リストを返すことができるライブラリが欲しいですよね。
さらにPHP5.5から導入されたGeneratorも使えるといいですね!

遅延リストの恩恵

ここで一度、遅延リストの恩恵について考えてみます。
0からの連番をたくさん返すIterator$ysがあります。
ここから偶数の要素を二乗して5つだけ取り出したいとします。
foreachを使って自前でループしてbreakするのが効率的なのは当然ですが、ここでは高階関数を使って書いてみます。

$even = function($n) { return $n % 2 === 0 };
$square = function($n) { return $n * $n; };

array_slice(array_map($square, array_filter(iterator_to_array($ys, false), $even)), 0, 5);
// [0, 4, 16, 36, 64]

このコードには効率上の問題が2つあります。

  • 要素5つ分のメモリ空間で十分なのにIteratorの長さ分だけ空間を必要とする
  • 今回必要としない5個目以降の要素についても$squareが呼びだされてしまう

自前でループを書いて処理すればいいのですが面倒ですね。
それなら以下のような感じでIteratorをチェインして遅延リストとして処理してしまいましょう。

$xs = new TakeIterator(new MapIterator(new FilterIterator($ys, $even), $square), 5);
iterator_to_array($xs);
// [0, 4, 16, 36, 64]

これでわざわざループを書かなくても済みますし、効率上の問題も解決しました。
Underbar.phpの実装もまさにこのようになっています。

Underbar.phpとは

Underbar.phpはこれまでに述べた問題を解決するために開発されたPHPのコレクション処理のためのライブラリです。
その名の通りUnderscore.jsにインスパイアされたライブラリですが、移植というわけではないのでそれほど互換性は重要視していません。

詳しい使い方は公式ドキュメントを読んで頂くとして、
遅延リストを利用してフィボナッチ数列を10個取り出す例を引用してみます。

use Underbar\IteratorImpl as _;
    
// Fibonacci sequence
echo _::chain([1, 1])
    ->iterate(function($pair) { return [$pair[1], _::sum($pair)]; })
    ->map(function($pair) { return $pair[0]; })
    ->take(10)
    ->join();  // 1,1,2,3,5,8,13,21,34,55

今回の例ではIteratorを返すIteratorImplを使っていますが、他にArrayImplGeneratorImplがあります。

その他のPHPのコレクション処理ライブラリ達

PHPのarrayが扱いづらい問題はPHPerの共通見解のようでUnderbar.php以外にもいくつかコレクション処理ライブラリが開発されています。
大きくUnderscore.js系とLINQ系に分けることができます。

Underscore.js系

自分も1つ増やしてしまったわけですが、ちょっと乱立気味ですね。
本来こういった機能は言語の組み込みにするなり、何か統一を図るべきなのかもしれません。
まあPHPなのでしょうがないですね!

この中だと本家のUnderscore.jsが即時実行する実装なのもあって、遅延実行できる実装はありません。
これがUnderbar.phpの最初の開発の動機でした。

LINQ系

LINQ系のライブラリも結構数がありますが、最後発のGinqが設計が洗練されていて非常に良くできています。このスライドで大体の雰囲気を掴めるかと思います。

LINQ好きはGinq使っておけば間違いないですね。
Underbar.phpにはないjoinによるデータの結合やソートの合成などもあるのでおすすめです。

まとめ

PHPの配列を扱う関数は非常に使い勝手が悪いので、様々なコレクション処理のためのライブラリが開発されています。
しかし、中でも遅延リストを扱えるものは少ないので、そういった意味ではUnderbar.phpが選択肢の一つになりうると思います。

Underbar.phpは今後社内でも利用しようという動きがありますが、まだ誕生したばかりのライブラリなので不具合や要望等あればpull requestIssuesの方にお願いします。

PHPのコレクション処理ライブラリUnderbar.phpの紹介

最後に社内勉強会でのUnderbar.php発表の様子とスライドです。

  • このブログについて

    このブログは、札幌市の「株式会社インフィニットループ」が運営する技術ブログです。
    お仕事で使えるITネタを社員たちが発信します!

    最新の記事