みんなが幸せになるサービス
最近、レアジョブという英会話学習サービスを知った。
http://www.rarejob.com/
サイトにあるように25分で129円からである。今までの英会話の金額と比較すると破壊的に安い。
試しにやってみたが、そんなに悪くない。
これは、英語教師はフィリピンのフィリピン大学に通う学生/卒業生だからである。
フィリピン大学とは、日本でいう東大みたいなもんだろうか。フィリピンにおける最高学府のようである。
嘘か本当か知りませんが、知り合いに聞いたところ、フィリピンでは大学を卒業しても就職先がコールセンターしか無いそうである。フィリピンでは公用語も英語だし、英語教育が進んでいるので、大学まで進学した人は英語が堪能に話せるのだろう。なので、アメリカ企業などが、アメリカでコールセンターを開設せずに、国際電話を使ってもフィリピンに開設した方が費用が安く済みそうなのはなんとなく想像できる。
なので、大卒の就職先がコールセンターしか無いとは言い過ぎだろうが、アメリカ企業がコールセンターを大量に作るので、そこの求人が多くなるだろうとは想像できる。
せっかく大学まで卒業したのだから、職業はもっと幅広く選択できてもいいだろうとは思う。
そこで、上記のレアジョブのような仕組みが出来上がると
- 日本のユーザは安くて英会話学習が出来る
- フィリピン人は職業の幅が広がる
- 運営は評判になれば儲かる?
と、誰も損しない、幸せな仕組みができあがるなと思ったりする。
皆がwin-winになれるサービスとか作れるといいよね。
MySpace
MySpaceというSNSを使ってみようと思い、会員登録してみた。
すると以下のようなメールが送られてきた。
こんにちは、XXXさん。 MySpaceにご登録いただき、ありがとうございます! ログインには以下のメールアドレスとパスワードをご利用ください。 メールアドレス: XXX@XXXX.com パスワード: XXXXX ※メールアドレスとパスワードは他人に知られないように大切に管理してください。
パスワードが平文で記録されてるとは!
wikipediaによると
2008年5月10日現在で2億320万人分のアカウントがあることが発表されている。 一日に23万アカウントずつ新規登録が増えている。
とのことなのですが、大丈夫なのか?
これほどの会員数を抱えるサービスがパスワードを平文で格納していることに驚きを感じる。
RailsのDB負荷分散プラグインを実装しよう。。。。かなw
最近、会社でもRailsとMySQLで負荷分散に取り込むことが多いのですが
これはやっぱり難しいですよね。
色々プラグインを入れてみたり、自前で実装してみたりと試しているのですが、どうもシックリこない。
何が面倒なのかというと、実装の中で明示的にMasterかSlaveにつなげるというのを意識しないといけない点だ。
それによってconnection先を変更してるんだけど、これがどうも馴染めない。
そこでRESTfulをベースにアクション単位で負荷分散する方針で新しいプラグインを作ってみようと思う。
- getはSlave
- post,put,deleteはMaster
もしくは
- showはSlave
- indexはSlave
- newはMaster
- createはMaster
- updateはMaster
- destroyはMaster
のように決め打ちでやればどうだろう?
もしくはfilterにように
class FooController < ApplicationController :master => [:create,:update] :slave => [:index,:show]
とかやるといいのだろうか?
まあ、この辺を組み合わせて作るのだろうけど、showにアクセスしたときに読んだ人の数をカウントしたりとか、このルールに乗っ取らない処理をする場合は諦めてくださいってことですかね。
というわけで、誰か実装してくださいw
その前にacts_as_tritonnをなんとかしたほうがいいすかねw
Railsに最適な開発環境を求めていたら・・・
会社でMacbook proが余ったので仕事ではMacを使うようになりました。それに伴い、最近メインのエディタをAptanaからemacsに乗り換えた。
操作も馴染み、なかなか快適な環境なので、自宅の自分のPCにも導入しようかと思い、VMWare上のCentOSに入れてみた。
そこからSSHでしばらく使ってたのですが、どうしてもキーのレスポンスが悪い。コンマ何秒か遅れるのがどうしても気になってしまう。
なので、WindowsでRailsが動く環境をつくり、Meadowをインストールするか、Cygwinの環境を作るかを考えてたのですが、
そのとき、vistaさんがまさかの
ブルースクリーン -> 強制終了 -> 起動しない -> 俺発狂
またしてもお亡くなりになったようです。
幸い(?)にも、この1年で1回お亡くなりになり、
前回はknoppixからCDブートしてデータをサルベージしてましたが、前回の教訓を活かして、バックアップソフトを導入して定期的にバックアップ(しかも昨日とった)を取ってました。
いやあ、備えあれば憂いなし!です。
いい加減にvistaとお別れしようかと悩んでます。
そこで
Windows XPのダウングレードオプションを行使
acts_as_tritonn
前回MySQL+Sennaを簡単に扱えるプラグインを作って公開するとか言っておきながら忙しくて放置してました^^;
このまま放置しようかと一瞬思ったのですが、はてなスターも付いてることだし、折角なので公開します
なお、全文検索クエリを簡単に使うことを目的としているため、細かくSenna演算子をつかったり、適合率でソートはできません^^;
インストール
※githubに引っ越しました
script/plugin install http://ryu.rubyforge.org/svn/acts_as_tritonn
git clone git://github.com/ryu00026/acts_as_tritonn.git
使い方ですが、
class Post < ActiveRecord::Base acts_as_tritonn end
普通の検索
Model.find_fulltext({:col1 => "hoge"}) => SELECT * FROM models WHERE MATCH(col1) AGAINST('+hoge' IN BOOLEAN MODE);
AND検索
Model.find_fulltext({:col1 => ["hoge foo"]}) => SELECT * FROM models WHERE MATCH(col1) AGAINST('+hoge +foo' IN BOOLEAN MODE);
OR検索
Model.find_fulltext({:col1 => ["hoge", "foo"]}) => SELECT * FROM models WHERE MATCH(col1) AGAINST('-hoge -foo' IN BOOLEAN MODE);
countをとる場合も同様です
Model.count_fulltext({:col1 => "hoge"})
一部、同様のプラグインであるacts_as_ludiaを参考に
:all=>true
オプションをつけると複数のカラムに対してORかAND検索できます。
cond = ["posts.user_id = ?", params[:user_id]] query = {:"posts.title" => "foo",:"posts.content" => "foo",:"comments.content" => "foo" joins = "LEFT JOIN users ON users.id = posts.user_id LEFT JOIN comments ON #posts.id = comments.post_id " @pages = Paginator.new(self, Post.count_fulltext(query, :select => "DISTINCT(posts.id)", :all=>true, :joins => joins, :conditions => cond), 10, params[:page]) @posts = Report.find_fulltext(query, :select => "DISTINCT(posts.id)", :all=>true, :joins => joins, :conditions => cond, :order => "posts.updated_at DESC", :offset => @pages.current.offset, :limit => @pages.items_per_page)
のようなSQLがかけます。
migrate
create_table :posts, :options => "ENGINE=MyISAM DEFAULT CHARSET=utf8" , :force => true do |t| t.column :title, :string t.column :content, :text end add_index :posts, [:title], :fulltext => "NGRAM" add_index :posts, [:content], :fulltext => "NGRAM"
※この部分はmasuidriveさんにご提供いただきました。ありがとうございます。
ここで問題です
MySQLとSennaはSNSのようなサイトで、特定の会員等に対して検索結果を非表示にしたりするのに非常に有効な手段だと思います。
ですが、この方法はInnoDBを使えないという大きな欠点を抱えます。
なので、通常はbackgroundrbなどで、バッチを走らせて定期的にインデックスを追加したりする方法を取ると思います。
が、そんなのめんどくさい!と思うじゃないですか!?(僕だけ?)。
そこで
mysql_replication_adapter
というのを試しています。
Slave DBのmy.cnfに
skip-innodb
とし、レプリケーション設定をして、slave側でCREATE INDEXしておけば、勝手にレプリケーションしてくれてインデックスが更新されていくかなと思い色々実験しています。
通常のマスタのInnoDBに対してINSERT/UPDATE文を実行し、スレイブでバイナリログを実行されるとき、slave側でインデックスが作成されます。
上の例だと
@posts = Report.find_fulltext(query, :select => "DISTINCT(posts.id)", :all=>true, :joins => joins, :conditions => cond, :order => "posts.updated_at DESC", :offset => @pages.current.offset, :limit => @pages.items_per_page, :use_slave => true)
とuse_slaveオプションを追加しておけばSlaveに接続し、全文検索クエリを投げます。
こうすることで、軽快なMyISAMとトランザクションが有効なInnoDBを使い、負荷分散もできるかなとか思ったり・・・・。
なお、右下のバックアップ用のSlaveはあってもなくてもいいけど、masterになにか障害が発生してもMyISAMのslaveをmasterには昇格できないので
あったほうがいいかと思います。まあ、mysqldumpするときに使ったりと、実際の運用では必要かもしれません。
HDD換装計画
僕のメインで使ってるノートPCですが、ReadyBoostしようが、メモリを最大の3Gまで積もうがvistaが全く快適になりません。
そこで最終手段として120G/5400rpm/キャッシュ8MBのHDDを200G/7200rpm/16MBのヤツに交換しようと考えてます。
vista businessなのでディスクイメージをComplete PCでバックアップを取ることにする。
このComplete PC、なんと保存先にNASを選択できません。
いきなり使えないにも程がある。
仕方がないので外付けUSBHDDに保存するとしてつなげると、フォーマットがFAT32・・・・
仕方がないので、NASにせっせとファイルをコピー。160Gもあると結構時間がかかる
その間に色々調べるとAcronis True Image 10 Homeがどうも簡単に換装できる模様。
[手順]
- HDDを新しいHDDに交換
- 元のHDDをUSBのHDDケース等に入れて接続
- True ImageをCDブート
でラクにできそうである
[懸念点]
- 元のHDDより容量の大きなHDDを買い、サイズの大きいHDDにクローンを作成するとAcronis True Image 10 Homeがリカバリ用のパーティションまで勝手に拡張する模様。なんと不便な。ま、Dドライブとかにしますか。
- パッケージ版は高いのでDL版にしたいんだけど、これってisoで焼けるのかなあ?
とか考えながら現在外付けHDDをNTFSでフォーマット中・・・・・
というかAcronis True Image 10 HomeはNASに接続できるんだった・・・・
acts_as_tritonn
2007/12/9追記
http://d.hatena.ne.jp/ryu00026/20071209
で公開してます
http://qwik.jp/tritonn/
TritonnはMySQLから全文検索エンジンSennaを利用可能にするための改造を行うプロジェクト。
MySQLにSenna(およびMeCab)を組み込んだバイナリをリリースしているので、インストールが楽そう。
調子こいてRailsのpluginを実装してみた。。
今のところ、クラスメソッドfind_fulltextを使い操作できる
==普通の検索
Model.find_fulltext({:col1 => "hoge"})
# => SELECT * FROM models WHERE MATCH(col1) AGAINST('hoge');
==search_modifier(IN BOOLEAN MODE | WITH QUERY EXPANSION)を指定
Model.find_fulltext({:col1 => "hoge"}, :modifier => "IN BOOLEAN MODE")
# => SELECT * FROM models WHERE MATCH(col1) AGAINST('hoge' IN BOOLEAN MODE);
==search_modifier(IN BOOLEAN MODE | WITH QUERY EXPANSION)を指定
Model.find_fulltext({:col1 => "hoge"}, :modifier => "IN BOOLEAN MODE")
# => SELECT * FROM models WHERE MATCH(col1) AGAINST('hoge' IN BOOLEAN MODE);
Model.find_fulltext({:col1 => "hoge"}, :modifier => "WITH QUERY EXPANSION")
# => SELECT * FROM models WHERE MATCH(col1) AGAINST('hoge' WITH QUERY EXPANSION);
== OR検索
Model.find_fulltext({:col1 => ["hoge foo"]}, :modifier => "IN BOOLEAN MODE")
# => SELECT * FROM models WHERE MATCH(col1) AGAINST('hoge foo' IN BOOLEAN MODE);
== AND検索
Model.find_fulltext({:col1 => ["hoge","foo"]}, :modifier => "IN BOOLEAN MODE")
# => SELECT * FROM models WHERE MATCH(col1) AGAINST('+hoge +foo' IN BOOLEAN MODE);
こんな感じでいいのかな?
ANDとORは逆にしたほうがいいような気もする・・・・なあ
誰か意見くれ。
とりあえず、週末ぐらいに公開するかな。