Xmarksが2011年1月でサービス終了

人に教えてもらってから、愛用していたブックマーク同期サービスXmarks。
Gmailと同じくらいなくては困るものになっていたんですが、来年頭でサービス停止という知らせがきました。こんな便利なサービスなのに残念です。

会社で2台のPCを利用しているので、それらのブックマークを同期させていたのです。
同じような同期サービスがないか調べてみたところないようです。
firefox同士、chrome同士なら同期できるよう。
http://mozilla.jp/blog/entry/5633/(firefox)
http://tokuna.blog40.fc2.com/blog-entry-1862.html(chrome)
http://googlejapan.blogspot.com/2010/01/google-chrome-40-web.html(chrome)

safariは現在のところブックマークを同期する方法は、MobileMeを利用する(有料)のみのよう。
http://plaza.rakuten.co.jp/macevan/diary/20080915/

異なるブラウザ間で同期させる方法はないようなので、これを機にメインブラウザを何にするかを考える人も増えそうです。気分で今日はこっちーってやっても、ブックマークがないのですから。

Macならこういうツールもありました。
ツールを通して、異なるブラウザのブックマークを一度に参照でき、ブックマークを選択すると、ブックーマクデータをもっているブラウザが起動されるそうです。
http://www.moongift.jp/2007/12/allbookmarks/
http://agilewebsolutions.com/

Xmarksと同等の機能を持つサービスが早くでてきてほしいですね。

MySQL複合INDEXの威力

予想以上の効果がでたので記事に残します。
DBの情報を元に統計を行うSQLとプログラムの性能改善で、複合INDEXを試してみました。
MySQLは、4.2です。

対象となったSQLは複数ありますが、そのうちほとんどは4つのテーブルをJOINしています。取得条件を変えながら情報を取得し、最後に目的別に合算するっていうことをやっています。4つのテーブルのうち3つはデータ数200万オーバー、1つは100万オーバー。

最初は、こんな状態

+----+-------------+-------+--------+----------------------------------------------+
| id | select_type | table | type   | Extra                                        |
+----+-------------+-------+--------+----------------------------------------------+
|  1 | SIMPLE      | tb    | range  | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | tc    | ref    | Using where                                  |
|  1 | SIMPLE      | tp    | ref    | Using where                                  |
|  1 | SIMPLE      | td    | eq_ref | Using where                                  |
+----+-------------+-------+--------+----------------------------------------------+

まずは、一番最初に検索されている tb に複合インデックス(検索条件カラム、取得カラム全部、合計11カラム)を設定してみるも結果変わらず。カラムを検索条件だけにしてみたり、更に減らしたりと試すも結果変わらず。

とりあえず、最初に設定した複合インデックスを設定して、tcにも複合インデックス(全8カラム)を設定してみる。

すると変化が現れた。

+----+-------------+-------+--------+----------------------------------------------------------+
| id | select_type | table | type   | Extra                                                    |
+----+-------------+-------+--------+----------------------------------------------------------+
|  1 | SIMPLE      | tc    | ref    | Using where; Using index Using temporary; Using filesort |
|  1 | SIMPLE      | tb    | ref    | Using where                                              |
|  1 | SIMPLE      | tp    | ref    | Using where                                              |
|  1 | SIMPLE      | td    | eq_ref | Using where                                              |
+----+-------------+-------+--------+----------------------------------------------------------+

tc に設定したINDEXが利用されてuse indexが出てきたのはわかるが、テーブルをJOINする順番が変わっている!!気になって、tb に設定した複合INDEXを削除すると、またtb からJOINされるようになった、possible key(利用可能index)にはいて、key(利用index)にはでてこないけど、なんらかの影響は及ぼしているようだ。

td tpにも同じ要領で検索対象、取得対象のカラムに複合INDEXを設定する。その結果、td tpにもuse indexが表示されるようになった。

+----+-------------+-------+------+----------------------------------------------------------+
| id | select_type | table | type | Extra                                                    |
+----+-------------+-------+------+----------------------------------------------------------+
|  1 | SIMPLE      | tc    | ref  | Using where; Using index Using temporary; Using filesort |
|  1 | SIMPLE      | tb    | ref  | Using where                                              |
|  1 | SIMPLE      | tp    | ref  | Using where; Using index                                 |
|  1 | SIMPLE      | td    | ref  | Using where; Using index                                 |
+----+-------------+-------+------+----------------------------------------------------------+

だが、肝心のusing temporary、using filesortが消えない。using filesortって、今回のSQLにはgroup by は入っているけど、order byは入っていない。いくつかサイトを見たけど、order by には言及してあるものが多かった。そこで、order by をgroup by に読み替えて、実験してみることにした。
http://www.mysqlpracticewiki.com/index.php/Extra_field
上記のサイトと、リンク先を読んでいて今回のケースに当てはまりそうなものは、最初にJOINされるテーブルではないカラムで、group by を掛けていること。tbのカラムでかけていた。
幸いtc tbは1対1対応のテーブルでで、group by をtc のカラムでかけても取得結果は変わらないので、試してみることに。

その結果がこれ

+----+-------------+-------+------+--------------------------+
| id | select_type | table | type | Extra                    |
+----+-------------+-------+------+--------------------------+
|  1 | SIMPLE      | tc    | ref  | Using where; Using index |
|  1 | SIMPLE      | tb    | ref  | Using where              |
|  1 | SIMPLE      | tp    | ref  | Using where; Using index |
|  1 | SIMPLE      | td    | ref  | Using where; Using index |
+----+-------------+-------+------+--------------------------+

消えた!やった!
ってことで、実行されるSQL(20ちょいくらい)にexplainをかけてみた。サブクエリ以外でexplainをとってみると、ほとんどのものでusing temporary、using filesortが消えていた。消えなかったのは、取得したいデータの量的にどうしようもなさそうなもの(データの半分以上を一旦取得するもの)くらい。

これで統計プログラムを実行したところ、実行時間が40分オーバーから5分前後へ!
かなり早くなった。予想以上の結果にびっくりしたので、メモ!

複合INDEXについては、以下のサイトも参考にさせていただきました。
http://d.hatena.ne.jp/k_yamamot/20100831/1283262245
http://txqz.net/blog/2006/12/13/0943
http://dev.seesaa.net/article/238633.html

Macで使えるメモツール

会社ではWin、家ではMacをメインで使っていますが、Winで使っている「紙copi」のようなメモツールが必要になったので、探してみたところ見つけたのが「JustNote」。

「紙copi」のようにフォルダは作れないけれど、あとは同じような使い方ができます。
もともとがほんとにメモ用で、TipsとかSQLの例とか、仕事の思いつきのまとめとかに使っていたので、その範囲内(テキストメモ)での使い勝手が似たようなもんってことです。
Webページの貼り付けとかにはつかってなかったので、そのあたりはよくわかりません。

紙copi Netの利用も考えたのですが、パスワードが分からないと閲覧されないはずとはいえ、ネット上に置いてていいのか?と思える内容もあるので、今回は対象外としました。とはいえ、なんかで使えるかもしれないと思って、アカウントだけは作成しました。

ネット上のメモツールは、Quill.toを使ってたんですが、特にここが不便って思ったりしないまま、あまり使わなくなり、紙copi に全部書くようになっていったんです。iPhoneもAndroid端末も持っていない今だと、家でも会社でも常に更新したいような事柄がない限り、ネット上に保存して共有できるメリットよりも、ブラウザ立ち上げて認証するっていう手間のデメリットが大きく感じる気がします。プログラムの仕様をまとめるときとかには重宝してました。

なので、JustNoteもネット上のメモサービスであるSimpleNoteと同期をとるっていう機能がついていますが、SimpleNoteとは連携せずに使っていくつもりです。

iPhoneとかもっていて、PCでもiPhoneでも、iPadでも全部からアクセスしたいって方にはいいかもしれません。クライアントも充実しているようです。
http://simplenoteapp.com/downloads/

Zendを使ってZip解凍

以前、Zendを使ってZip圧縮という記事を書きましたが、今回は逆のZendを使ってZip解凍についてです。

前回は、Zend_Filter_Compressというクラスを使いましたが、今回はZend_Filter_Decompressというクラスを使います。
使い方は以下です。

$filter = new Zend_Filter_Decompress('Zip');
$filter->setArchive('圧縮ファイルのパス付ファイル名')
    ->setTarget('圧縮ファイルの解凍先ディレクトリパス');
$filter->filter('hoge');   // hogeはnullじゃなければ何でもいいです。

または

$filter = new Zend_Filter_Decompress('Zip');
$filter->setTarget('圧縮ファイルの解凍先ディレクトリパス');
$filter->filter('圧縮ファイルのパス付ファイル名');

または

$filter = new Zend_Filter_Decompress(array(
            'adapter' => 'Zip', 
            'options' => array(
                            'archive' => '圧縮ファイルの解凍先ディレクトリパス', 
                            'target'   => '圧縮ファイルの解凍先ディレクトリパス'
                         )
));
$filter->filter('hoge');   // hogeはnullじゃなければ何でもいいです。

または

$filter = new Zend_Filter_Decompress(array(
            'adapter' => 'Zip', 
            'options' => array('target'   => '圧縮ファイルの解凍先ディレクトリパス')
));
$filter->filter('圧縮ファイルのパス付ファイル名');

または

$filter = new Zend_Filter_Decompress();
$filter->setAdapter('Zip')
       ->setAdapterOptions(array(
             'archive' => '圧縮ファイルの解凍先ディレクトリパス', 
             'target'   => '圧縮ファイルの解凍先ディレクトリパス'))
       ->filter('hoge');    // hogeはnullじゃなければ何でもいいです。

解凍、圧縮共に Zip の部分を Gz, Lzf, Rar, Tar, Bz2 に変更するとそれぞれの形式で圧縮解凍ができます。
今回疑問に思ったのは(何で前回思わなかったのか。。)、なんで、こんな感じでかけないのかということ。

$filter = new Zend_Filter_Decompress('Zip');
$filter->setArchive('圧縮ファイルのパス付ファイル名')
    ->setTarget('圧縮ファイルの解凍先ディレクトリパス');
       ->filter();

または

$filter = new Zend_Filter_Decompress('Zip');
$filter->setTarget('圧縮ファイルの解凍先ディレクトリパス');
       ->filter('圧縮ファイルのパス付ファイル名');

不思議に思ってプログラムを観てみると、setTargetとかsetArchiveから返ってきているオブジェクトが、Zend_Filter_Decompressではなくて、Zend_Filter_Decompress内部で持っているZip処理のアダプターZend_Filter_Compress_Zipでした。
Zend_Filter_Compress_Zipにはfilterっていうメソッドはないので、エラーがでてたんですね。

Zend_Filter_Decompressの中で、

    public function __call($method, $options)
    {
        $adapter = $this->getAdapter();
        if (!method_exists($adapter, $method)) {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception("Unknown method '{$method}'");
        }

        return call_user_func_array(array($adapter, $method), $options);
    }

ってなっている部分を

    public function __call($method, $options)
    {
        $adapter = $this->getAdapter();
        if (!method_exists($adapter, $method)) {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception("Unknown method '{$method}'");
        }

        call_user_func_array(array($adapter, $method), $options);
        return $this;
    }

ってしたら、やりたい書き方ができそうだけど、どんな弊害があるのかは細かく調べてないのでわかりません。ざっとみたところなさそう。アダプター使いたいなら、getAdapterで取得できるので、不具合もなさそう。なんで、アダプターを返すような作りにしたんだろう?読み取れてない意図があるのかな。

ZendでDbのMeta情報をキャッシュする

なんのこっちゃ?って感じですよね。僕もそう思います。

ZendFrameworkを利用する場合、Zend_Db_Table(Row、Rowsetも)を利用することが多いと思います。Zend_Db_Table_Abstractを継承して、テーブル名と他テーブルとのリレーションを定義するだけで、DBの登録、更新、削除ができてしまう優れものです。個人的にはこれだけ、ZendFrameworkを使ってみようってきっかけになりました。

これらがテーブル名とリレーションだけしか定義してないのに、存在しないカラム名を指定して処理しようとすると、”そんなカラムはないよ!”ってエラーを出してくれます。賢いです。これができるのって、テーブル関連の処理をするたびに”describe”が実行されて、テーブルの情報を取得しているからなのです。気がきいてます。

ただ1点、”describe”が実行される量が多すぎるんです。これが処理が遅くなる原因になることがあります。ここででてくるのがDbのMeta情報のキャッシュ。DbのMeta情報というのは、”describe”の実行結果です。なので、これをキャッシュとして保持しておくと、”describe”をしなくても、情報が手に入るため余計なSQLをDBに実行しなくてすみます。

たかが”describe”ですが、実行回数がほんとに多いので、だいぶ変わります。
Zend_Db_Table使うときは、セットで設定するくらいでいいのではと思っています。

Bootstrap.phpの中にこんな感じで書いておくと使えます。

    protected function _initDbCache()
    {
        $frontendOptions = array('automatic_serialization' => true);
        $backendOptions = array('cache_dir' => APPLICATION_PATH . 'cacheディレクトリへの相対パス');
        $cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
        Pb_Db_Table_Abstract::setDefaultMetadataCache($cache);
    }

恵比寿の焼肉店-喜福世

前回書いたのは、行きたいお肉の店でしたが、今回は行って気に入ったところを。
恵比寿の韓国焼肉のお店。喜福世。キッポヨと読みます。
http://r.gnavi.co.jp/g753700/

ここは以前、恵比寿が地元の友達に近所の中華屋へ北京ダックを食べにきた際に、まあまあの値段するけど、美味しいところだよと紹介されたお店でです。いつもの通り、近くに来たりすると思い出すけど、いつもは忘れてるっていうパターンで、実際に行くまでにだいぶ間があいてしまいました。

7月に大阪から大学時代の友達がきて恵比寿でご飯ってことになったので、ここに行ってみることに。結果は今焼肉食べたくなったらここだなーと思うくらい気に入りました。

肉の霜降り加減も好みで(肉はとろけない方が好きです)、チヂミも、キムチも美味しい。感動するとかではないですが、安心館があるというか、美味しさの代わりに何かを代償としなくていいくらいの味がとても印象がよかったです。

男性なら一人8000円くらいでお腹いっぱい食べて、2、3杯お酒飲めるくらいだったと思います。

気になるお店の名は鉢山

大げさなタイトルかもしれませんが、お店の外観からうけるイメージは、タイトルに決して負けていません。大人の余裕が感じられる外観です。

とりあえず先に書いておきますが、まだ食べに行ってません。
いつも通る道沿いになるので、気になり始めて数カ月。やっとPCの前にいるときに思い出しました。

焼肉 鉢山
http://www.hachiyama.jp/

一人1万くらいあれば食べれそうです。食べログみてると。
叙々苑と同じくらいか少し高いくらいですか?
恵比寿のチャンピオン別館と同じくらいかもしれません。
恵比寿のチャンピオンは肉がよすぎて(霜降り過ぎて)あまり好みではなかったので、こちらにはとても期待しています。韓国料理ベースで、野菜も美味しいってレビューがあったので、さらに期待が高まります。

行く機会があったら、また書きます。