Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

アドテク界隈では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
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とかこねくりまわさないといけないのでめんどくさい。バグなのかなんなのか調べる必要がありそう。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away