質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.36%
Erlang

Erlangとは、多目的でありガーベッジコレクションを行うプログラミング言語および実行環境です。Erlangは並行処理・分散化された環境・フォルトトレランスを実装しています。

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

Elixir

Elixirは、並列処理や関数型に特化した、Erlang VM (BEAM) 上で動作する汎用プログラミング言語です。分散システム、耐障害性、ソフトリアルタイムシステムなどの機能を持ちます。

Q&A

解決済

1回答

6225閲覧

Elixir+Phoenixが早い理由と、これでDB処理を作った時のデッドロックについて

yokatone

総合スコア43

Erlang

Erlangとは、多目的でありガーベッジコレクションを行うプログラミング言語および実行環境です。Erlangは並行処理・分散化された環境・フォルトトレランスを実装しています。

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

Elixir

Elixirは、並列処理や関数型に特化した、Erlang VM (BEAM) 上で動作する汎用プログラミング言語です。分散システム、耐障害性、ソフトリアルタイムシステムなどの機能を持ちます。

0グッド

0クリップ

投稿2018/08/25 03:37

概要

Elixir + Phoenixが早い理由としてどこがキモになるのか。
関数型言語であること、並行処理が可能であることが大きなシナジーを生んでいる言語と考えてよいのか。
その場合、DBアクセス処理設計を行うときは、並行処理という特性を踏まえた慎重なものにしなければならないのか。

といった要旨です。
整理しきれずすみません。質問が大きく二つございます。

質問したいこと - Elixir+Phoenixが早い理由

Elixir+PhoenixのCRUDを作ってみよう系のコードやベンチマークのコードを見ていると、
(言葉は悪いですが)特に工夫もないコードしかなく、(Erlangで並行処理を宣言するspawnが使われているわけではない)
これで高速なレスポンスが可能である理由がわかりませんでした。

言語特性として並行処理が可能である、VM内でのマルチプロセスが可能である
これが高速の根拠であるのなら、Elixir+Phoenixは裏でマルチプロセス処理を行なっているから早い
ということで良いのでしょうか。それとも単にErlangの関数型言語という仕様だったり、BEAM VMが優秀だからであって、
更にPhoenixは裏で自動ビルドしているので、スクリプト言語に比べて処理が高速である、ということでしょうか。
(=並行処理機能がベンチマーク結果に寄与しているわけではない)

また、もしElixir+Phoenixが裏でマルチプロセス処理を行なっているのであれば、
|>を使わない処理は全てElixir+Phoenixによって並行処理に変換されていたりするのでしょうか。。
(以下の質問はこの前提に立って話が進みます。ここがそもそも間違っていたらすみません。)

質問したいこと - 並行処理によるDBのデッドロック

Elixir+PhoenixでのAPIサーバーを構築する記事を読んでふと感じたのですが、
Erlangの特性である並行処理って、DBアクセス時に簡単にデッドロックを引き起こしてしまいそうなのですが、
実際のところどうなのでしょうか?
(実例が思いつかなくて恐縮です。次の項目で詳しく考えたことを書いてみています。)

うまく言えませんが、関数の設計において、
従来通り設計を行えばよくて、並行処理の特性などは考慮しなくても良いのか、
並行処理の特性を考えながら設計しなければならないのか....

また、後者の場合、アクセス処理は結局DBが行うものなので
Elixirに対応できるDBの選定などが必要になってくる気がしていますが、これも実際必要でしょうか?
(たとえばsqliteは実質的にマルチプロセス向きではなく、PostgreSQLはマルチプロセスで構築されてるから、
sqliteをElixir+PhoenixのDBとして使うと速攻で破綻してしまう、とか。。)

考えたこと

仮定1

並行処理ということは、
普通にWebアプリケーションを作って、APIをPHPなんかで実装して、複数人からアクセスがきた時に発生する処理と、
Elixirで実装して、1人がアクセスしたときに発生する処理は
同じような処理が走るのではないかな(=同時DBアクセスが発生する)と想像しています。

なので、従来の実装の考え方では

「基本的に関数A内で定義してあるDBアクセス処理はこの順番で走るのだから、
この設計さえ間違えなければ、複数人アクセスがきても
誰しもが同じ順番でDBアクセスされるはずなので、問題は発生しない」

だったのが、

「Elixirで処理を関数Aにある通りに定義したが、どの処理が最初に走って終わるのかが明確ではないから
関数Aに内包される全てのDBアクセスの組み合わせを検証して、どれもデッドロックが生じないように
クエリやアクセスを考えなければならない」

となるような気がしています。。

仮定2

また、Elixir+Phoenixの早い理由がいまいちつかめていないのですが、
ソースを見ても関数をすごく細かい粒度に分割していたり、spawnを多用しているわけでもなく、
普通に他言語と同じような書き方をするだけで早くなっているような印象を受けました。
勝手な推測ですが、関数型言語ということで、関数単位で並行処理してるのかな?という妄想をしています。
(=JSにおけるvarの巻き上げのような現象が、関数単位で発生する。
つまり必ずしも上から下に書いてある通りに処理されない...というイメージです。)

例えば次のような形です。

php

1//この場合、BとCはAの処理開始時に同時にスタートする 2public function A(){ 3 $this->B(); 4 $this->C(); 5} 6 7//こうすれば順番に処理される 8public function X(){ 9 if($this->Y()){ 10 $this->Z(); 11 } 12}

結論

典型的なデッドロックの例として、
テーブルA, テーブルBがあって、
BがAに対して参照を持っている時、トランザクション1がAに対してUpdate, トランザクション2がBに対してInsertすると発生しますが、
DBアクセス処理が順番に処理されず、同時並行的に処理される場合、容易にこれが生じてしまいそうな気がしています。

なので、Elixir+Phoenixが高速な理由が仮定2の通りならば、
次のような関数の定義を行うと、デッドロックが(容易に)発生してしまうような気がしていますが、どうなのでしょうか。

elixir

1def 関数A_裏でspawnが自動で行われてると仮定すると、この関数ではデッドロックが発生する?(connect) do 2 case TableA.Update(params) do 3 {:ok, _} -> 4 # ... 5 case TableB.Insert(params) do 6 {:ok, _} -> 7 # ...

また上のコードが真である前提に立つと、
次のような形で定義すればデッドロックが起きないような気がしていますが、
さながらコールバック地獄で、実装が大変そうだな...と感じていますが
実際にデッドロックを意識したとき、PhoenixのDBアクセス部分を設計するときのコードは
大抵こんな形になるものなのでしょうか。

elixir

1def 関数A_パイプラインを使って順番に処理すれば、デッドロックが発生しない?(connect) do 2 case TableA.Update(params) do 3 {:ok, _} -> 4 |> case TableB.Insert(params) do 5 {:ok, params} -> 6 # ... 7 {:error, params} -> 8 |> putMsg(:error, params)

付記

自分で調べていて混乱していたので、ここで使っている言葉の概念を次のようにまとめてみました。
(合っているといいのですが)

ことば意味
非同期処理関数と関数の間隙にキューされた処理が割り込む。ワンオペでうまいこと厨房を回してるイメージ(レンジを3分にセットして、その間に配膳して、チンされたら取り出して....みたいな)
並行処理(マルチプロセス/マルチタスク)別のプロセスを起動する。その分リソースを食う。複数人で処理を行うイメージ。きちんと管理しないと同期しない。お店で例えるとドライブスルー担当、調理担当、配膳担当がいて、それぞれちゃんと現在の業務内容を共有しないと業務が滞るイメージ
並列処理(マルチスレッド)1つのプロセスの中で複数のスレッドを起動する。スレッドの使うメモリは共通。並行処理を包摂している。お店で例えると何店舗もあるチェーン店のそれぞれの動きを俯瞰で見てるイメージ

正直言ってデッドロック排他制御などの用語の字面だけ知っているような初心者で、
Elixirについては実際にコードも書いたことがなく、
上の表を見てもわかるように、マルチ***についても全く詳しくないです。

このFWが早い理由と、並行でDBアクセスが発生するときどう処理するのか、といった点に興味を持っただけなので、
完全に杞憂だったらすみません。

十分な知識がないまま、色々質問してしまってすみません。
ご存知の方、ご回答のほどよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

興味があって調べてみました。
Elixir+Phoenixが早い理由

何に対して速いというところが明確ではありませんが、類似したものに比べてという事にします。
速さに関してはフレームワークの差です。Phoenixが速いのです。
どのようにして高速に処理しているのかは、そのアーキテクチャを調べてみて下さい。
Phoenix Framework(v0.8) 概要

高い生産性と高いアプリケーションパフォーマンス,両方の長所を持っていることを目指しています.

目指したんだから、利用する命令の最適化とか色々やっているんでしょう。
並行処理によるDBのデッドロック

ここでの並列処理はフレームワーク内の事を指していると思いますので、それは無いでしょう。

並列処理は起点と終点の内側でパラレルに処理を行い、起点と終点の区間の長さを短くするものです。
到達する終点ではシリアルに実行したものと同じでなければならず、処理をパラレルに行ったものがその責を負う必要があります。

DBMSとフロントの境界はトランザクションです。
トランザクションに処理の流れを記述することになるので、ここに並列という要素が入る余地はありません。
DBMSも並列動作を行いますが、それも上記で述べたようにトランザクション内のある起点と終点間の話です。

トランザクションを同時に発行する場合の排他などは設計に依存する話で、トランザクションを跨いで含めて管理するというようなアーキテクチャのフレームワークは汎用的には成り得ないので、独自に作らない限りは存在しないと思います。

投稿2018/08/26 01:45

編集2018/08/26 02:02
sazi

総合スコア25300

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yokatone

2018/08/26 03:28

確かに。Phoenixとは別で低速なElixirのFWがあればよかったですね(笑) FWの作り方でケタの違う処理速度が出るというのは驚きです。 私も調べてみましたが、並列, 複数のスレッド, コンパイル, 軽量あたりが当然キモですが、 より私の質問の本質に近い記述として、 Phoenixがプールし、Elixirが次々に処理するという連携があって、これがうまく働いてい というような内容のものがありました。 DBについてありがとうございます。 確かに、辻褄を合わせてくれないフレームワークなんか、 せっかく開発したところで使い勝手が悪くてしょうがないですね ;) 大変勉強になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問