Galapagos Tech Blog

株式会社ガラパゴスのメンバーによる技術ブログです。

Elixirから使うErlang:etsのおはなし

御機嫌よう、ガラパゴスのおとめです。

先日の記事では何の断りもなく:etsを触ってみたりしましたが、今日は、これは一体何なのかというおはなしをしたいと思います。

おさらい:ErlangとElixirとPhoenix Framework

Phoenix FrameworkがElixirという言語で実装されたWebフレームワークだということはもはや説明の必要もないと思いますが、さらに、Elixirという言語はErlang VMで動作します。ちょうどScalaJVMで動作するみたいに。

さて、ScalaからJavaの資産を、まあ、使うことができることから想像できるように、Elixirからは当然Erlangの資産を使うことができます。etsはそうした資産の一つです。

etsってなあに?

etsは、Erlang VMに含まれるインメモリ・データベースです。例えばRailsをお使いの方なら、Redisやmemcachedのお世話になったことがあるかもしれませんが、Erlangにはその機能がVMにあります。

例えばRDBMSに仕舞われたあるデータへの読み込みアクセスがとても多くて、DBアクセスが性能上のボトルネックになっているなら、メモリにキャッシュしますね。ここでErlangという言語を乱暴に簡単に説明すると、Erlangでは、そうですね、オブジェクト指向におけるオブジェクトが全部別々のプロセスなっていて、特定のプロセスへのメッセージが多い、というような形で、先に挙げたRDBMSの場合のようなボトルネックが生じることがあります。

そこで登場するのが、特定のプロセスの代わりに並列でアクセスできるetsです。

etsには、タプルで表現できるものならば、なんでも入れることができます。

公式のドキュメントはこちら。 http://erlang.org/doc/man/ets.html)http://erlang.org/doc/man/ets.html)

etsを使ってみる

テーブルの作成

:ets.new/2を呼び出してテーブルを作成します。ここでちょっと、Phoenix.PubSubがetsテーブルを作成するコードを見てみましょう。

^server = :ets.new(server, [:set, :named_table, read_concurrency: true])

この二つ目のリストが、etsテーブルを作成する時のオプションだということは想像できるかと思います。

:setを指定することで、一意のキーバリューストアとしています。順序はありません。setの場合、指定したキーの取得にかかる時間は一定になります。他にordered_set(要素が順番に並ぶ。アクセス時間はO(long N))、bag(同じキーで別の値を格納できる)、duplicate_bag(同じキーと値の別のレコードを格納できる)があります。

:named_tableを指定することで、名前でテーブルにアクセスできるようにしています。これを指定しないと、作られるテーブルは名前なしになります。どういうことかというと、例をあげましょう。

:named_tableを指定しない場合は、このように戻りのオブジェクトを使ってアクセスします。

table = :ets.new(:some_name, [:set])
:ets.insert(table, {a, 1})

:named_tableを指定すると、名前さえ分かっていれば良いので、このように戻りのオブジェクトは不要になります。

:ets.new(:some_table, [:set, :named_table])
:ets.insert(:some_table, {a, 1})

さて、最後に指定しているread_concurrency: trueは、読み取り並列性の最適化オプションです。trueを指定すると、読み取りが速くなり、代わりに書き込みが遅くなります。読むばかりでほとんど書くことがない、つまりキャッシュのような用途でetsを使う場合はtrueにします。

何か書いてみる

上の方でも書きましたが、etsにはタプルならなんでも入れることができます。これまで何度か「テーブル」という表現をしましたが、スキーマはありません。ただし、タプルの最初の項目がキーになります。

書き込みには:ets.insert/2を使います。

:ets.insert(:some_table, {key, some, [value, or, object]})

setではキーの重複は許されないので、insert/2で既存のキーを指定すると置換されます。

:ets.insert(:some_table, {key, some, value})
# -> {key, some, value}
:ets.insert(:some_table, {key, other, value})
# -> {key, other, value}

値を取得してみる

:ets.lookup/2で、キーをもとにして値を取り出します。戻りはリストになります。

:ets.lookup(:some_table, key)
# -> [{key, other, value}]

ところで、先日の記事では、lookup/2ではなく、lookup_element/3を使っていました。これは何でしょうか。

lookup_element/3では、タプルから取得する要素を何番目という形で指定できます。

:ets.lookup(:some_table, key, 2)
# -> [other]

消してみる

:ets.delete/2でテーブルとキーを指定することで、指定したキーの値を削除します。

:ets.delete(:some_table, key)

紛らわしいですが、キーを指定せずにdelete/1とすると、テーブルごと削除になります。

:ets.delete(:some_table)

その他の操作

さて、ここまで基本的な操作を見てきましたし、先日の記事で読んでみたPhoenix.PubSubで使われていた関数もこれくらいな感じなのですが、それ以外の操作もいろいろとあります。公式ドキュメントを見てみると、検索するのにも、lookup/2だけでなく、match/nselect/nがありますし、さらには、foldl/3foldr/3のような魅力的な関数も並んでいますね。もちろんこれらの関数もElixirから使うことができます。

さいごに

今回はElixirから使うErlangの資産の代表例として、etsを簡単に触ってみました。こういう機能が最初から使えるのは便利ですね。

さて、ガラパゴスでは、いろいろな技術に興味がある方を大絶賛超募集しちゃっています。皆様のご応募をお待ちしています。

RECRUIT | 株式会社ガラパゴス iPhone/iPad/Androidのスマートフォンアプリ開発

では、御機嫌よう。

--

この記事は業務の一環として業務時間を使って書きました。