LoginSignup
3
3

More than 5 years have passed since last update.

🍺Redisより早いNoSQL DBのAerospikeをRubyから使ってみた

Last updated at Posted at 2018-10-01

以前、アドテクについて学ぶ機会があったので知見を残しておく。

アドテク界隈ではAerospikeがよく使われるようだ。
https://www.aerospike.jp/

Aerospikeとは
NoSQL(キー・バリュー・ストア)データベース
インメモリ、もしくはフラッシュメモリ(SSD)にデータを保持
ノードの追加により、処理能力とデータ容量をスケールアウト可能
99%以上のトランザクションを1ms以下のレイテンシで処理可能
僅か数台で百万TPS超のスループットを実現
AppNexusやBlueKaiで稼働実績あり
みたいな感じです。要約すると超絶速いKVSってことでいいと思います。

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
shared/shared.rb
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との処理を行う。

app.rb
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とかこねくりまわさないといけないのでめんどくさい。バグなのかなんなのか調べる必要がありそう。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3