NB Jogger's blog

NBが好きなのです

閉鎖のお知らせ

2014-02-12 08:26:02 | インポート
Broach(ブログ)サービス終了について
この度、「ぷらら」ではお客さま向けオプションサービスのひとつである、Broach(ブログ)サービスの提供を終了させていただくことになりました。 2013年5月30日から新規受付停止しておりますが、2014年6月30日をもってサービスの一切の機能が使えなくなります。

ということで、サービス自体が修了するので、このブログも閉鎖です。
ご閲覧いただいてきたみなさま、ありがとうございましたm(_ _)m

移転先ははてなブログかレンサバで放置しているWordPressにするか検討中です。

Catalyst Sample

2010-12-12 23:40:40 | インポート
Catalyst本を買ったので、Catalystのサンプルを作ってみました。
インストールについてはこちら
(インストールが最難関かもしれない・・・)

サンプル

以下は作業手順

1. アプリケーションの作成準備
適当なディレクトリで次のコマンドを実行する。
catalyst.pl Bbs

Bbsディレクトリが作成され、その中に必要なファイルが作成されます。
以下、作業はBbsディレクトリで行います。

2. とりあえず動かしてみる
以下のコマンドを実行して、サーバーを起動する。
./script/bbs_server.pl -r -p 3001

http://localhost:3001/ でアクセスするとテストの画面が表示されます。
-p オプションを指定しないときは3000になります。
Firewallでポートが開いているか注意してください。
-r オプションを立てておくとファイルを修正するたびに自動的にサーバーがリスタートします。

3. DBの準備
SQLiteを使います。DB、テーブルを準備します。
ファイルは db/bbs.db にします。
CREATE TABLE bbs (
  bbs_id INTEGER PRIMARY KEY AUTOINCREMENT,
  name VARCHAR(40),
  body VARCHAR(300),
  timestamp TIMESTAMP DEFAULT '00000000000000'
);
CREATE INDEX idx1 ON bbs(timestamp);

4. lib/Bbs.pm にプラグインを追加
lib/Bbs.pm の use Catalyst qw/ ... /; のqw//の中に以下のプラグインを追加します。
・FillInForm
・Charsets::Japanese
・Prototype

5. lib/Bbs/Schema.pm ファイルを作成
ファイルの内容は以下の通り
package Bbs::Schema;
use strict;
use warnings;

use base qw/
    DBIx::Class::Schema::Loader
/;

__PACKAGE__->loader_options(
    # debug => 1,
);

1;

6. Schemaファイルを作成
以下のコマンドを実行します。
./script/bbs_create.pl model DBIC DBIC::Schema Bbs::Schema

7. Viewの作成
以下のコマンドを実行します。
./script/bbs_create.pl view TT TT

8. tmpl の作成
tmplはroot/tmpl以下に作成します。
root/tmplはデフォルトではないので mkdir で作成しておきます。
ファイルの内容はサンプルを参考にしてください。

9. Controllerの作成
lib/Bbs/Controller/Root.pmを修正します。
今回、名前の通りBBSなので、BBSの表示とコメントの投稿を実装します。
メソッドはindex、addを容易します。
これも内容についてはサンプルを参考にしてください。

10. YAMLの設定
bbs.conf を bbs.yml にリネームして、内容を修正します。
---
name: Bbs

Charsets::Japanese:
    charsets: 'UTF-8'

View::TT:
    INCLUDE_PATH:
        - 'root/tmpl'
    TEMPLATE_EXTENSION: '.tt'

Model::DBIC:
    connect_info:
        - 'dbi:SQLite:dbname=db/bbs.db'
        - ''
        - ''
<iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=nbjogger-22&o=9&p=8&l=as1&asins=4844328654&fc1=000000&IS2=1&lt1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>

MySQL + memcached

2009-11-26 01:19:13 | インポート
MySQL(DB)とmemcachedを比較すると以下のような特徴があります。

MySQL
  • 複雑な条件の検索が可能
  • クエリの実行(Read/Write)が低速
  • データを永続的に保持できる

memcached
  • 複雑な条件の検索は難しい
  • クエリの実行が高速
  • メモリストレージなのでデータを永続的に保持できない

それぞれ長所短所があります。
これらを上手く組み合わせると、サイトの負荷を下げることが可能です。

使用例

例えば、芸能人などの人気ブログがあったとすると、データのアクセスについては以下のような特徴が考えられます
  • Write処理は1日に1~2回と少ない
  • たくさんの訪問者があるため、Read処理は非常に多い
  • 表示される内容はReadに対してそんなに変わらない
ピーク時にアクセスが集中したとき、Read処理が大量に走ったとき、DBへの問い合わせがボトルになる場合があります。
こんなとき、上手くmemcachedを使ってレスポンスを早くすることができます。


使い方の方針

次のように組み合わせて使います。
  • アクセスがあった場合、まずmemcachedにデータを取得しにいき、なければDBにアクセスする。DBにアクセスした場合、取得したデータをmemcahcedにsetする
  • エントリ作成時であれば、memcachedの更新に備えるため、memcachedの一覧データを削除
このようにすると、Readの大半はmemcachedに流れレスポンスは早くなります。
memcachedが落ちてデータがクリアされたり、expireでデータがなくなった場合でもデータはDBから取得することが可能です。
ただ、Write時にはexpireされるかどうかに関わらずすぐに更新したいので、memcachedのデータを一旦削除します。アクセスがあったとき、Readの処理が走り、再びmemcachedにデータがsetされます。

memcachedに保持するのは以下のようなデータです。
  • 最新エントリ一覧(エントリID、タイトル、日時の配列)
  • 最新n件のエントリ(タイトル、本文、日時のハッシュ)
それぞれ"list"、"entry:$num" という形式のkeyでデータを保持します。

MySQLのテーブル

日記用のテーブル
CREATE TABLE `diary` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `title` varchar(255) NOT NULL,
  `body` text NOT NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
データのdump
INSERT INTO `diary` VALUES
(1,'First entry','This is first entry.','2009-11-01 03:00:00'),
(2,'Second entry','This is second entry.','2009-11-02 03:00:00'),
(3,'Third entry','This is third entry.','2009-11-03 03:00:00'),
(4,'Fourth entry','This is fourth entry.','2009-11-04 03:00:00'),
(5,'Fifth entry','This is fifth entry.','2009-11-05 03:00:00');
サンプル

Perlのコード
#!/usr/bin/perl
package Diary;
use strict;
use warnings;

use DBI;
use Cache::Memcached::Fast;

use constant {
    DSN         => 'DBI:mysql:diary',
    USER        => 'perl',
    PASSWORD    => undef,
    EXPIRE      => 60 * 60,
};

sub new {
    my $class = shift;

    my $dbh = DBI->connect( DSN(), USER(), PASSWORD() );
    my $cache = Cache::Memcached::Fast->new({servers=>["localhost:11211"]});

    bless {
        dbh     => $dbh,
        cache   => $cache,
    }, $class;
}

sub get_diary_list {
    my $self = shift;

    my $key = 'list';
    my $list = $self->{cache}->get($key);
    if ( $list ) {
        warn "Read from cache";
        return @$list;
    }

    my $sth = $self->{dbh}->prepare(qq{
        SELECT id, title, timestamp FROM diary
    });
    $sth->execute;

    my @result;
    while ( my $row = $sth->fetchrow_hashref ) {
        push @result, $row;
    }

    $self->{cache}->set($key, \@result, EXPIRE());

    warn "Read from db";
    return @result;
}

sub get_diary_entry {
    my ($self, $id) = @_;

    my $key = "entry:$id";
    my $entry = $self->{cache}->get($key);
    if ( $entry ) {
        warn "Read from cache";
        return $entry;
    }

    my $sth = $self->{dbh}->prepare(qq{
        SELECT title, body, timestamp FROM diary WHERE id = ?
    });
    $sth->execute($id);

    my $row = $sth->fetchrow_hashref();
    return if ( !$row );

    $self->{cache}->set($key, $row, EXPIRE());

    warn "Read from db";
    return $row;
}

sub insert_diary {
    my ($self, $title, $body) = @_;
    my $sth = $self->{dbh}->prepare(qq{
        INSERT IGNORE INTO diary SET title=?, body=?
    });

    $sth->execute( $title, $body );

    my $key = 'list';
    $self->{cache}->delete($key);
}

1;

package main;
use Data::Dumper;
my $diary = Diary->new();

##エントリ一覧
my @list = $diary->get_diary_list();
print "== Diary List ==\n";
print Dumper \@list;

## 各エントリの読み出し
print "== Diary Entry ==\n";
for my $id (1..5) {
    my $entry = $diary->get_diary_entry( $id );
    print Dumper $entry;
}

## エントリの追加時
$diary->insert_diary( "New Entry", "New Entry's Body" );

※ただ、ブログなどデータの更新が1日に1回あるかないかの低頻度であれば、HTMLなどの静的なフォーマットに吐き出すほうがいいです。当たり前ですが・・・。


Windows7 RC版をインストール - HDDに空きを作る

2009-05-30 00:54:24 | インポート
Windows7 RC版をインストールしてみました。
今回はインストール作業について。

■ISOを落としてDVDに焼く
↓からダウンロードしてDVDに焼いてください



結構時間かかります

■新規 or 上書き?
DVDを入れてPCを起動
新規パーティションにインストールするか、
既存パーティションのWindowsに上書きインストールか選択できます。
さすがにRC版で上書きはないので新規パーティションにインストールします。

■HDDを空ける
ただ、普通、HDDに空きパーティションなんてないですよね?
なので、既存パーティションのサイズを変更して、空きパーティションを作ります。
パーティションサイズ変更にGPartedを使います。
ISOを落としてCD-RWに焼きました。


Live USBもありますが、手持ちのUSBメモリでは上手くいかず。

GPartedのCDを入れてPC起動。
GUIを操作して既存のパーティションサイズを変更し空きを作ります。
RC版の検証だけなら20GBもあれば十分のようです。

■Windows7のインストール
Windows7のインストールDVDを入れてPC起動。
メッセージにしたがってインストール。
インストール自体は20分もかかりませんでした。



これからのものをレコメンド?

2008-11-10 01:36:46 | インポート
独断!レビログ=偏見:レコメンド機能への疑問

購買履歴を時系列で追跡していけば、そのユーザの購買傾向プロファイルみたいなものが取得できて、
それにマッチするような商品のレコメンドをすれば、人気になったものの後追いにはならず、これから人気の出るものをレコメンドできるんじゃないか?

・・・と思ったけど、結局ユーザのプロファイル情報と照らし合わせるための商品の属性情報は統計情報から得ることになるので、結局人気の後追いになるのか。
統計に依存しない新規商品の属性情報を得られれば、人気の後追いにならないかもだけど、
それは、最早レコメンドでない気がする・・・。
(パターンマッチだよね、たぶん)