日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

erlang mysql driver_erlang_mysql_driver 源码分析2

發布時間:2025/4/5 数据库 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 erlang mysql driver_erlang_mysql_driver 源码分析2 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

pool模型

探究erlang_mysql_driver對同一時刻大量請求的支持

mysql:fetch 和 mysql_conn

今天看到網絡上的一篇文章,說erlang_mysql_driver的連接池實際上是沒有意義的。

大概意思是,我們使用mysql:fetch去執行sql語句,mysql:fetch會call一條消息到mysql_dispatcher進程中。所以當我們同一時刻大量調用mysql:fetch的時候,mysql_dispatcher中就會有多條call消息在阻塞在消息隊列里,那么后面調用的進程必須等待,每個請求需要等待上一個請求執行結束后才能開始執行,所以雖然mysql_dispatcher背后有多個連接進程(mysql_conn)但是他們并沒有起到并發使用的作用。

乍一看,好像挺有道理的。但是我又覺得不對勁,畢竟作者不至于挖個這么大的坑吧,于是測試了一下。

同一時刻,spawn 10萬個進程,每個進程都調用mysql:fetch進行數據庫查詢。

按上面的說法,那么這個時候應該會有大量的消息阻塞在mysql_dispatcher中,測試結果卻是mysql_dispatcher的消息隊列很快就處理完了。也就是mysql_dispatcher很快就把這些消息分發給了mysql_conn,這里我建立9個mysql_conn進程。然后大部分的消息(10萬個請求)被堆積在9個mysql_conn進程的消息隊列中,而且每個mysql_conn收到的消息是平均的。

證明我們的mysql_dispatcher還是能夠順利完成任務的,而且可以看出mysql_dispatcher處理這些消息肯定只有簡單的分發消息,沒有涉及數據io過程的。

gen_server:call gen_server:reply

那么上面講到mysql:fetch不是調用了gen_server:call嗎,而gen_server:call確實是會阻塞的。但是這里阻塞的是調用者的進程,也就是我spawn出來的那些進程。而mysql_dispatcher對于這些消息的處理是非常快的,沒有涉及到數據的io過程。

fetch_queries(PoolId, From, State, QueryList) ->

with_next_conn(

PoolId, State,

fun(Conn, State1) ->

Pid = Conn#conn.pid,

mysql_conn:fetch(Pid, QueryList, From),

{noreply, State1}

end).

mysql_dispatcher僅僅將消息轉發給合適的mysql_conn,然后返回{noreply, NewState}。看好了,這里是noreply,所以業務進程調用mysql:fetch并不能在這里獲得返回,這個時候業務進程還屬于繼續阻塞狀態。

那么mysql:fetch的返回結果是從哪里得到?

mysql:fetch獲得的返回結果是通過mysql_conn 使用gen_server:reply返回給調用進程的。

%% GenSrvFrom is either a gen_server:call/3 From term(),

%% or a pid if no gen_server was used to make the query

send_reply(GenSrvFrom, Res) when is_pid(GenSrvFrom) ->

%% The query was not sent using gen_server mechanisms

GenSrvFrom ! {fetch_result, self(), Res};

send_reply(GenSrvFrom, Res) ->

gen_server:reply(GenSrvFrom, Res).

這里可能會有一個疑問就是,mysql_conn如何找到mysql:fetch 的調用進程并且正確地將值返回給他,如果在調用進程等待的返回值期間,先收到其他返回值怎么辦?

關于這個問題,要查詢官方文檔上關于gen_server:call的說法。當使用gen_server:call向某一指定的進程發送call消息的時候,收到消息的一方是這樣處理的 :Module:handle_call(Request, From, State)。

讓我們再看下文檔,From is a tuple {Pid, Tag} where pid is the client and Tag is a unique tag.

如果收到handle_call的一方,使用{reply, Reply, State}返回,那么Reply will be given back to From as the return value of call/2,3。但是問題來了,我們的mysql_dispatcher并沒有使用常規手段,他直接返回{noreply, NewState}。那么mysql:fetch的調用不是收不到返回值了,不要急,文檔說了 if the function returns {noreply, NewState}, Any reply to From must be given explicitly using gen_server:reply/2。

問題又來了,難道mysql_dispatcher沒有使用gen_server:reply?確實沒有!但是他把From直接傳遞給了mysql_conn,最終是mysql_conn查詢結束后使用gen_server:reply,把結果最終返回給了阻塞在mysql:fetch中的業務進程

所以,在erlang_mysql_driver的連接池中一開始建立多個連接,在面對大量請求的時候,確實是有幫助的,可以多個連接同時執行,最終的io壓力會放在這幾個連接進程上,mysql_dispatcher頂多就是要維護的進程池有點大罷了。

大并發執行fetch是否會有大量timeout 報錯?

這個是題外話了,在測試的時候遇到的問題。因為畢竟只有10個連接來處理10萬個請求,那么后面的幾萬個請求肯定要排隊到好久之后的。這個時間一旦超過了設置的timeout時間,那么就會有timeout報錯。 然而測試開始的時候,我沒有看到timeout報錯,一直很疑惑。后來發現是timeout報錯導致業務進程直接掛了,已經沒法打印報錯出來了。 在上面的測試中,只有9個mysql_conn,同一時刻卻要處理10萬條sql。那么肯定會有其他大量的調用一直處于阻塞狀態的,我們使用mysql:fetch(PoolId, Query)的形式查詢,而mysql:fetch其實是封裝了gen_server call,這個方法默認的timeout時間是5秒。如果在5秒內沒有收到返回值,就會扔出一個timeout的錯誤。而如果不去catch這個錯誤,進程就直接掛了,那么錯誤也打印不出來了。 在測試中,使用mysql:fetch(PoolId, Query)的調用,剛開始的進程能收到返回值,但是5秒后,進程就只能收到timeout報錯了。 另外 使用mysql:fetch(PoolId, Query, infinity)的調用,進程會一直等待,測試表明雖然有很多的sql查詢請求,每個mysql_conn都收到了1萬左右的消息,但是最終都能執行并返回結果。

總結

以上是生活随笔為你收集整理的erlang mysql driver_erlang_mysql_driver 源码分析2的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。