[Elixir in Action]Erlang Term Storageを知る

Getting Startedなどやっているとよく出てくる、定番な機能ですね。ETSは Erlang Term Storage の略です。

ある1つのprocessをキャッシュ代わりに使っている場合、そのprocessの性能や拡張性が処理のボトルネックになることが多くなります。そんなときの解決策としてETSが説明されています。(ETSに限らず、何かの要素と依存関係を持つ場合、その要素のいずれかがボトルネックになることは多々ありますね。)

特徴

  • Positive
    • etsは、owner processが起動している間だけ使える(in-memoryで)
    • serialize(連続的に)なキャッシュに書き込める
    • 分けられたメモリ空間に書き込まれ、多くのprocessから並行にアクセス可能
      • globalでアクセス可能
    • mutable
    • 非常に高速なアクセスが可能
      • 末尾に内容を記載
  • Negative
    • client processとETS tableはコピーされる
      • ETSに保存されるデータが原因でprocessがcrashする場合、 Supervisorによってリカバリされたprocessも再びcrashする という、悪循環に陥ることがある
        • BEAMの良さであるリカバリシステムを崩す原因になる
      • ETSには、複雑で大きなデータは扱うべきではない
    • ETSはネットワーク越しの他BEAMインスタンスとは共有できない
    • 本当に性能/拡張性を改善したいときだけにするといった用途を制限するほうが良い
      • 高い性能で多くのprcessで共有したいデータか?など

ETSの使い方

New

http://www.erlang.org/doc/man/ets.html#new-2

  • table types
    • :set The table is a set table – one key, one object, no order among objects. This is the default table type.
    • :ordered_set The table is a ordered_set table – one key, one object, ordered in Erlang term order, which is the order implied by the operators. Tables of this type have a somewhat different behavior in some situations than tables of the other types. Most notably the ordered_set tables regard keys as equal when they compare equal, not only when they match. This means that to an ordered_set, the integer() 1 and the float() 1.0 are regarded as equal. This also means that the key used to lookup an element not necessarily matches the key in the elements returned, if float()’s and integer()’s are mixed in keys of a table.
    • :bag The table is a bag table which can have many objects, but only one instance of each object, per key.
    • :duplicate_bag The table is a duplicate_bag table which can have many objects, including multiple copies of the same object, per key.
  • table access permission
    • :public Any process may read or write to the table.
    • :protected The owner process can read and write to the table. Other processes can only read the table. This is the default setting for the access rights.
    • :private Only the owner process can read or write to the table.

使ってみるとこんな感じ。

iex(10)> a = :ets.new :neko, [:set]
iex(11)> :ets.insert a, {:neko, 1}
iex(12)> :ets.insert a, {:neko, 2}
iex(13)> :ets.lookup a, :neko
[neko: 2]
iex> a = :ets.new :neko, [:bag]
iex> :ets.insert_new a, {:neko, 1}
iex> :ets.insert_new a, {:neko, 2}
iex> :ets.liikup a, :neko
[neko: 1, neko: 2]
iex> a = :ets.new :neko, [:duplicate_bag]
iex> :ets.insert a, {:neko, 1}
iex> :ets.insert a, {:neko, 2}
iex> :ets.insert a, {:neko, 2}
iex> :ets.lookup a, :neko
[neko: 1, neko: 2, neko: 2]

key以外によるアクセス

:ets.select/2 には以下のような仕様が与えられています。要素を取り出すとき、様々な条件式を与えることができるのですね。

  • select(Tab, MatchSpec) -> [Match]
    • Types:
      • MatchSpec = [MatchFunction]
      • MatchFunction = {MatchHead, [Guard], [Result]}
      • MatchHead = “Pattern as in ets:match”
      • Guard = {“Guardtest name”, …}
      • Result = “Term construct”

:ets. fun2ms/1 を使うと、↑のMatchSpecを変数として定義もできるので、これで関数を定義して select で引数として要素を取得する、という流れができそうですね。

性能

:ets を使ったキャッシュと、single processベースのキャッシュ。比較すると10倍程度の秒間リクエスト数が増加するらしいですね。:ets のほうが10倍程度多く秒間にアクセス/処理を実施できていました。スループットもetsのほうが高いとでていました。例は書籍をごらんください。

そのため、single processの処理性能が低いというボトルネックを解消する手段としては有効なのですね。なるほど。


4分の3くらい通過。
ここら辺まで読んで、この書籍はGetting StartedやProgramming Elixirを読んで取り掛かると良いかなという感じですね。いきなりこの書籍は中々理解に時間がかかりそう。

1 Comment

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.