ActiveRecordHandlerSocketなんていうgemを作っています。
業務的に必要に迫られて、memcachedとかRedisとかいろいろ触ってたんですが、HandlerSocketに行き着きました。
んで、せっかくActiveRecordのRDBをMySQL使ってて、HandlerSocketも使うんなら、うまくActiveRecord的な感じでHandlerSocketを使えたらいいよね、見たいな。そんなノリ。
ちなみにまだ作り始め(1週間も経ってない。。。)で、業務側の環境に合わせているので、依存関係のgemはものすごく古いバージョンになってたりします。
やっとこせ、という感じで作ったのですが、アドバイスやらご指摘やらいただけたら超嬉しいです。
(早速、修正中です。)
何はともあれ、HandlerSocketなんてPluginを作られている DeNA/樋口さん や rubyインターフェースのgemを作られてる miyucyさんに感謝です。(どなたにも面識がないのですが。。。)
自分の初gemだったりするので、まずはプレゼン。
ActiveRecordHandlerSocket
ActiveRecordのインターフェースに近い形で、既存のモデルにHandlerSocketのインターフェースを組み込みます。
ファーストリリースバージョンの0.0.1では、insert/delete系は未サポートで、readonlyになっています。
前提
mysqlにhandlersocket pluginが組み込まれている状態からスタートします。
ActiveRecord、と言っておきつつ、基本的に、Railsのプロジェクトで使うことを想定しています。
下ごしらえ 1 gemのインストール
依存関係はActiveRecordとhandlersocket gem
handlersocket gemは、ネイティブビルドですので、うまくインストールしてください。
あとは、ActiveRecordHandlerSocketをインストールして下さい。
下ごしらえ 2 Railsの環境設定
handlersocketへの接続のための設定をします。
データベースの設定を書いているYAMLファイル(config/database.yml
)に、以下のような内容を環境別に追記します。
development_hs_read: host: localhost port: 9998 database: your_dev_database test_hs_read: host: localhost port: 9998 database: your_test_database production_hs_read: host: localhost port: 9998 database: your_database
データベースは通常ActiveRecordから接続しているデータベースを活用できます。
ホストとポート番号は、環境依存ですので、ご確認を。
ARへの組み込み
今回のActiveRecordHandlerSocketですが、暗黙的な拡張だとわかりづらさがまずところがあるため、明示的な宣言風の記述が必要です。
わかりづらい点は、、、
さらに、、、
- インデックスとの接続は意外とコストで、毎回接続しているとHandlerSocketを使っているメリットが無くなる。
ではサンプルコード。
require 'active_record_handlersocket' class User < ActiveRecord::Base handlersocket :id, "PRIMARY", %W[id name age] end
ActiveRecord::Base.handlersocket
が組み込みのためのインターフェースです。
ActiveRecord::Base.handlersocket( key, index_name, fields )
key
: このインデックスの割当につける名前。handlersocket経由のfindに対して命名規約的に使用する。- 別クラスで同名の
key
を指定しても上書きはされないが、同一クラスで同名のkey
を指定すると上書きされる。
- 別クラスで同名の
index_name
: テーブルに存在するインデックスの名前。PRIMARYインデックスを含む。- 現行のHandlerSocketはSQLをサポートしているわけではないので、重複のないインデックスを使うと、扱う側もわかりやすい。
fields
: selectするカラムを配列で指定。- 自由指定。
find
さすがに、find
を拡張するとややこしくなりそうだったので、hs
というプレフィックスを使っています。
ActiveRecord::Base.hsfind_by_{key}(find_key, options = {}) #=> AR object or nil # or ActiveRecord::Base.hsfind_multi_by_{key}(*find_keys, options = {}) #=> Array
ActiveRecord::Base.handlersocket
の第1引数に渡したkey
を利用してコールします。
例えば、、、
User.hsfind_by_id(1) #=> #<User id: 1, ... >
User.hsfind_multi_by_id(1, 2) #=> [#<User id: 1, ...>, #<User id: 2, ...>]
options
には、2種類のオプションパラメータを指定可能です。
:operator
: レコードを見つけるときの比較演算に使用する演算子を指定します。デフォルトは、=
。>
とか。
:limit
:hsfind_multi_by_...
の時かつ、:operator
で以上や以下などを使っている場合に有効。- 1つ1つの
find_keys
に対してのlimitであるため、取得するレコード数の上限にはなりません。(次までにいい名前に変えておきます。)
- 1つ1つの
※0.0.2以降は、:each_limit
になる予定です。
benchmark・その他
1000件程度のデータのうち100件にランダムアクセスを100000回繰り返した時。MySQLサーバーはlocalhostで立ち上がっている。ロジックはRailsに依存。
user system total real InnoDB 19.120000 5.420000 24.540000 ( 29.167859) HandlerSocket 5.700000 2.610000 8.310000 ( 13.612221)
(もうちょっと改善したい。。。)
そもそもでMySQL自体とても速いんですが、とりあえず普通にMySQLにアクセスするよりは早くなります。という話で、、、
HandlerSocketの利点は、、、(別のところでベンチマークをとっているのですが、今回は割愛。)
- 大量のコネクションを張っても劣化しにくい。
- データを増やしても劣化しにくい。
みたいなところで、memcached
などと比べた時にパフォーマンスの高さを見せてくれました。
Railsで1サーバー運用、くらいでは全然生きてこない(むしろ手間なだけ)ですが、複数台のサーバーで一気にアクセスさせるといいかんじにメリットが出てきます。
- 作者: Baron Schwartz,Peter Zaitsev,Vadim Tkachenko,菊池研自(監訳),株式会社クイープ
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/11/25
- メディア: 大型本
- この商品を含むブログ (6件) を見る