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

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

ただいまの
回答率

88.10%

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

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 4,159

score 43

 概要

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の巻き上げのような現象が、関数単位で発生する。
つまり必ずしも上から下に書いてある通りに処理されない...というイメージです。)

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

//この場合、BとCはAの処理開始時に同時にスタートする
public function A(){
  $this->B();
  $this->C();
}

//こうすれば順番に処理される
public function X(){
  if($this->Y()){
    $this->Z();
  }
}

 結論

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

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

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

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

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

 付記

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

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

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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

興味があって調べてみました。

Elixir+Phoenixが早い理由

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

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

目指したんだから、利用する命令の最適化とか色々やっているんでしょう。

並行処理によるDBのデッドロック

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/26 12:28

    確かに。Phoenixとは別で低速なElixirのFWがあればよかったですね(笑)
    FWの作り方でケタの違う処理速度が出るというのは驚きです。
    私も調べてみましたが、並列, 複数のスレッド, コンパイル, 軽量あたりが当然キモですが、
    より私の質問の本質に近い記述として、
    Phoenixがプールし、Elixirが次々に処理するという連携があって、これがうまく働いてい
    というような内容のものがありました。

    DBについてありがとうございます。
    確かに、辻褄を合わせてくれないフレームワークなんか、
    せっかく開発したところで使い勝手が悪くてしょうがないですね ;)
    大変勉強になりました!

    キャンセル

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

  • ただいまの回答率 88.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る