この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。
以前、アドテクについて学ぶ機会があったので知見を残しておく。
アドテク界隈ではAerospikeがよく使われるようだ。 https://www.aerospike.jp/
Aerospikeとは NoSQL(キー・バリュー・ストア)データベース インメモリ、もしくはフラッシュメモリ(SSD)にデータを保持 ノードの追加により、処理能力とデータ容量をスケールアウト可能 99%以上のトランザクションを1ms以下のレイテンシで処理可能 僅か数台で百万TPS超のスループットを実現 AppNexusやBlueKaiで稼働実績あり みたいな感じです。要約すると超絶速いKVSってことでいいと思います。
http://tech.im-dmp.net/entry/archives/421
Rubyから使用した。
Aerospike Ruby Client
というGemを使用した。
https://github.com/aerospike/aerospike-client-ruby
ぶっちゃけ、Rubyから使うのはオススメしない。Aerospikeで速さを求めているのに、わざわざ遅いRubyを使うのはナンセンスだと思う。 自分は短時間で成果が求められる環境で使用したため慣れているRubyを使用した。
RubyからAerospikeの使い方
RDBとAerospikeの名前の対応関係は以下のようになっている。 Aerospikeでnamespaceを新しく作るのはそこそこめんどくさいので、初期からあるtest namespaceをとりあえず使っておけばOK
RDB | Aerospike |
---|---|
db | namespace |
table | set |
column | bin |
module Shared attr_accessor :write_policy, :policy, :client, :logger def init host = '33.222.111.00' @@options = { :host => host, :port => 3000, # Aerospikeはデフォルトで3000番 :namespace => 'test', # デフォルトでtestというnamespaceがある :set => 'advertisers', # 操作したいsetをする(メソッドの引数にしてもよさそう) } @write_policy = WritePolicy.new @policy = Policy.new(connection_queue_size: 10000, timeout: 0.005) @logger = Logger.new(STDOUT, Logger::INFO) @client = host ? Client.new(Host.new(host, port)) : Client.new end def host @@options[:host] end def port @@options[:port] end def namespace @@options[:namespace] end def set_name @@options[:set] end end
shared/shared.rb
を利用することで、Aerospikeとの処理を行う。
require 'sinatra' require 'aerospike' require './shared/shared' include Aerospike include Shared class MyApp < Sinatra::Base # ~~~~いろいろ省略~~~~ # binを更新する # 引数 # pk: primary key # client: Aerospikeクライアント(ex: Shared.client) # add_bin1: bin1の増加量(ex: 5) # add_bin2: bin2の増加量(ex: 10) def update_bins(pk, client, add_bin1, add_bin2) key = Key.new(Shared.namespace, Shared.set_name, pk) record = client.get(key) bin1 = record.bins['bin1'] bin2 = record.bins['bin2'] bin1 += add_bin1 bin2 += add_bin2 client.put(key, record.bins) end # bin1, bin2を取得する # 引数 # pk: primary key # client: Aerospikeクライアント(ex: Shared.client) # return: Hash{ :bin1 => Int, :bin2 => Int } def get_bins(pk, client) key = Key.new(Shared.namespace, Shared.set_name, pk) bins_hash = client.get(key, ['bin1', 'bin2'], Shared.policy).bins bins_hash end # update_bins(1, Shared.client, 5, 10) のように呼び出して使用 end
ハマったところ(バグ?)
def get_records(client) stmt = Statement.new(Shared.namespace, Shared.set_name, ['bin1']) records = client.query(stmt) end
このように書くとSQLでいうところのselect bin1 from Shared.set_name
が動くはずなのだが、全件ひっぱてくることができなかった。なぜか、4割ほどのレコードだけ引っ張ってくることができた。対策として、一つのレコードで全件分のデータを保存するために、Integer型でなくList型でbinを持つことが考えられる。Listとかこねくりまわさないといけないのでめんどくさい。バグなのかなんなのか調べる必要がありそう。