Mysql: ベストプラクティス

䜜成日 2016幎06月03日  Â·  40コメント  Â·  ゜ヌス: go-sql-driver/mysql

このドラむバヌでは、すべおのHTTPリク゚ストに察しお、 Open("mysql","...")を䜿甚しおデヌタベヌスを開き、すべおのリク゚ストサむクルの最埌にClose()を䜿甚しおデヌタベヌスを閉じるこずがベストプラクティスです。

たた

リク゚ストサむクルに関係なく、䞀床開くだけで閉じないでください。

最も参考になるコメント

@pjebs接続を開いた盎埌にそれらを呌び出しおいたす。
正盎なずころ、これらのパラメヌタヌ SetMaxOpenConns を蚭定しないず、必芁な数の接続が確立されるようです。 SetConnMaxLifetimeを5分たたは10分に蚭定しお、接続が閉じられるかどうかを確認したしたか

接続が開いたらすぐにこれらを呌び出しおみおください。

db.DB().SetConnMaxLifetime(time.Minute*5);
db.DB().SetMaxIdleConns(0);
db.DB().SetMaxOpenConns(5);

これたでに5接続を超えおいるかどうか、および接続のいずれかが5分を超えおアむドル状態になっおいないかどうかを確認したす。 お知らせ䞋さい。 䜿甚可胜なプヌルにアむドル状態の接続がない堎合はハングするず思いたすが、コヌドを読んでいないため、その堎合は倱敗する可胜性がありたす。

党おのコメント40件

コヌドコメントに次のように曞かれおいたす。

DBハンドルは長寿呜であり、倚くのゎルヌチン間で共有されるこずを目的ずしおいるため、DBを閉じるこずはめったにありたせん。

...だから私はノヌだず思いたす。 最悪の堎合、最倧蚱容アむドル接続の構成に基づいお、タむムアりトするか、自動的に閉じられたすか

SetConnMaxLifetimeを参照しおください

たた、http //go-database-sql.org/accessing.html 番号付きリストの埌の郚分も参照しおください。

@benguild SetConnMaxLifetime 、 SetMaxIdleConns 、 SetMaxOpenConnsをどのように構成したしたか

さたざたな負荷条件に合わせお蚭定するためのベストプラクティスの原則をどこで埗るこずができるかに぀いおのアむデアはありたすか

1幎以䞊の間、リク゚ストの開始時にOpen()し、リク゚ストのラむフサむクルの終了時にClose()を䜿甚しお、このドラむバヌを䜿甚しおきたした。 それは完璧に機胜したした。

このアドバむスを聞いお、私は1぀の接続を開き、参照をグロヌバル倉数に残しお、それを䜿い続けたす。 私はそれを決しお閉じたせん。

SetConnMaxLifetime 、 SetMaxIdleConns 、 SetMaxOpenConns蚭定しおいたせん。 それらはデフォルトのたたです。

この単玔なテクニックは私にはうたくいきたせん。 CloudSQLぞのアクティブな接続の数は、Google AppEngineの接続制限に達するたで増え続けたす。 その埌、デヌタベヌスに接続できなくなりたす。

接続が閉じられおいたせん。

CloudSQLのGoサンプルは、開始時に1回だけOpen()を呌び出したす https //github.com/GoogleCloudPlatform/golang-samples/blob/master/getting-started/bookshelf/db_mysql.go

この問題はCloudSQLに固有のものであり、ドラむバヌ自䜓に関連しおいるのではないでしょうか。

たぶん@broadyはいく぀かの掞察を持っおいたすか

さお、私は今閉じおいたせん。 それが問題が始たった時です。

そのサンプルコヌドは、接続が長続きするこずを理解しお䜜成したした。 私は実際に本番アプリでGoのSQLを䜿甚したこずがありたせん。

タむムアりトしたがっお、定期的なキヌプアラむブpingが必芁が原因で切断されおいたすか、それずも゜ケットが壊れおいるために切断されおいたすか

誰がこれを凊理する必芁がありたすか ドラむバヌたたはナヌザヌ

実は、私が蚀ったこずはあたり意味がありたせん。 sqlパッケヌゞは、プヌル内の䞍正な接続を凊理する必芁がありたす。 この問題をこのドラむバヌに限定したしたか、それずもdatabase / sqlのバグですか

@pjebs接続を開いた盎埌にそれらを呌び出しおいたす。
正盎なずころ、これらのパラメヌタヌ SetMaxOpenConns を蚭定しないず、必芁な数の接続が確立されるようです。 SetConnMaxLifetimeを5分たたは10分に蚭定しお、接続が閉じられるかどうかを確認したしたか

接続が開いたらすぐにこれらを呌び出しおみおください。

db.DB().SetConnMaxLifetime(time.Minute*5);
db.DB().SetMaxIdleConns(0);
db.DB().SetMaxOpenConns(5);

これたでに5接続を超えおいるかどうか、および接続のいずれかが5分を超えおアむドル状態になっおいないかどうかを確認したす。 お知らせ䞋さい。 䜿甚可胜なプヌルにアむドル状態の接続がない堎合はハングするず思いたすが、コヌドを読んでいないため、その堎合は倱敗する可胜性がありたす。

アプリケヌションコヌドに問題があるのではないかず思いたす。 ハンドラヌが結果セットを閉じおいない堎合、既存の接続がただ䜿甚されおいるため、ドラむバヌはたすたす倚くの接続を開く必芁がありたす。 リク゚ストごずに接続が開かれたり閉じられたりするず、進行䞭のク゚リによっお開いたたたになっおいる接続が終了するため、接続が積み重なるこずはありたせん。

これは、単䞀のグロヌバル持続的接続ぞの切り替えで問題が発生した理由を説明しおいたす。

たた、 @ pjebsは、コヌドのどこかに偶然に远加のデヌタベヌスプヌルを䜜成しおいないこずを明らかに確認しおください。 どこかに新しいプヌルが割り圓おられおいる堎合、問題がゆっくりず発生する可胜性がありたす。 以前にdefer db.close()を呌び出しおいた堎合、このようなものは怜出されない可胜性がありたすが、以前のコヌドが䜿い捚おであるこずに基づいお、たったく新しいプヌルが䜜成/開かれおいる堎合があるように思われたすスタむルの。

デヌタベヌス接続の埅ち時間がそれほど悪くなく、埌でプヌルの割り圓おが正垞に解陀される限り、各リク゚ストメ゜ッドでの開閉も機胜したすが、ほずんどの蚭定では、2぀の別々のアプリケヌションがない限り、プヌルを䜿甚する方が良いず思いたす。 tたずえば倚くの接続を長期間開いたたたにしおおくず、アむドル状態であっおも、他の接続が䞀郚を割り圓おるのをブロックする可胜性がありたす。

@dgryskiええ、私もそう思っおいたした䞊蚘を参照。 単䞀の䜿甚から保持されたプヌルぞの切り替え䞭に、別のプヌルがコヌドのどこかに䜜成されおいお、割り圓おが解陀されおいない堎合などがありたす。

関連する別の問題

以前の方法では、デバッグの目的でこれを行うために䜿甚したす倖郚キヌ制玄゚ラヌを回避するため。

db.Exec("SET foreign_key_checks = 0;")
_, err := db.Exec("INSERT IGNORE INTO " + c.Q_VALENTINES_PIVOT_CUSTOMER_EVENT + " (customer_id, event_id, gender, allocated_group, session_1, session_2, public_name, profile_photo, registered_time) VALUES " + values + ";")
    if err != nil {
        db.Exec("SET foreign_key_checks = 1;")
        return err
    }
    db.Exec("SET foreign_key_checks = 1;")

それは垞に動䜜するために䜿甚したす。

「接続プヌル」を䜿甚しおいるので、これは機胜したせん。
https://github.com/go-sql-driver/mysql/issues/208 これが理由を説明しおいたす。 同じ接続を䜿甚するずいう保蚌はなく、 SETはセッションにのみ蚭定されおいるため、必ずしもメむンステヌトメントに適甚されるずは限りたせん。

ずいう事は承知しおいたす。 しかし、どうしおそれが私の以前の方法で機胜したのでしょうか。 私は垞に新しい接続を䜜成し、芁求サむクルの終わりにそれを閉じたしたが、䜜成した「接続」は匕き続き接続プヌルを利甚したすか

@pjebs接続プヌルは耇数の接続を開く可胜性がありたす。 䞀床に1぀の接続のみが必芁な堎合は、1぀の接続を維持したす。 以前のケヌスでは、同じプヌルで2぀のデヌタベヌス芁求が同時に発生したこずはなかったため、1぀の接続しか確立されたせんでした。 したがっお、すべおのExecは同じ接続で発生したした。 適切なプヌルに移動するず、これは圓おはたりたせん。

より倚くのコヌドを芋るこずができれば、これはデバッグが容易になりたす。

@pjebsデヌタベヌストランザクションずしおこれを実行しお、同じ接続に確実にヒットするようにする必芁がありたすhttp //jinzhu.me/gorm/advanced.html#transactions

なぜそれがあなたの蚀うこずをしおいるのかはわかりたせんが、ステヌトメントの芁求ごずに異なる接続を䜿甚するこずは完党にラむンから倖れおいるわけではありたせん。 これは「プヌル」の背埌にある䞀皮の考え方です...リク゚ストを行っおいるものが必ずしもわからず、同時に動䜜するリク゚ストが倚数存圚する可胜性があるこずを考えるず。 単䞀の接続がその存続期間党䜓にわたっお単䞀の芁求のために予玄されるのはちょっずばかげおいるでしょうが、そうです。 トランザクションを䜿甚したす。

トランザクション内で接続を亀換するこずは完党にラむンから倖れたす。 構成パラメヌタヌを倉曎するず、コヌドの他の郚分からの他のク゚リや他の芁求に圱響を䞎える可胜性があるため、実行しおいるこずは実際にはトランザクション内にある必芁がありたす。 珟圚曞かれおいる方法...コヌドがパニックになった堎合、その特定の接続で元に戻されるこずはありたせん その点で、トランザクションが終了しない堎合たずえば、クラッシュ/パニックが発生した堎合は、トランザクションのロヌルバックを延期するようにしおください。そうしないず、デヌタベヌス接続がハングしたり、別のプロセスがそのデヌタベヌス接続を採甚しお開始したりする可胜性がありたす。コミットされないようなトランザクションぞの倉曎など— https://github.com/jinzhu/gorm/issues/1056

@broadyのコヌドで気づきたした https //github.com/GoogleCloudPlatform/golang-samples/blob/master/getting-started/bookshelf/db_mysql.go#L82
接続を開いた埌、すぐにPing()を呌び出しお、接続がただ応答しおいるかどうかをテストしたす。
そうでない堎合、圌はただやめたす。 他にできるこずはありたすか もう䞀床開いおみるべきですか

https://godoc.org/github.com/garyburd/redigo/redis#Poolラむブラリでは、優れたプヌルも実装されおいたす。
TestOnBorrowずいう優れた関数があり、少し䞋の䟋ではPing()も実行したす。

このドラむバヌに䌌たようなものはありたすか

@pjebs実際の珟圚の問題は䜕ですか

Ping()を実装するタむミングず、Pingが倱敗した堎合の察凊方法に関するベストプラクティスに぀いお尋ねるだけです。

ああなるほど。 私が知る限り、あなたはそうする必芁はないはずです... しかし、プヌルにただ接続が切断されおいる堎合は...必芁ず思われたす。

間違いなくPingする必芁がありたす。 圌らはredigo リンクの䞊ず@broadyのコヌドでそれを行いたす。

db.Exec("SELECT 1+1")はPINGずしお䜿甚できたす。

「間違いなく」Pingする必芁がありたすか それは意味がありたせん...接続が有効でない堎合はどうなりたすか 䞭止したすか、それずも再詊行したすか

.Ping盞圓するク゚リ呌び出しを自動的に実行しないのはなぜですか

db.Exec "SELECT 1 + 1"はPINGずしお䜿甚できたす。

では、なぜ私たちは間違いなく.Pingする必芁があるのでしょうか。

Pingは、接続ず認蚌が有効であるこずを怜蚌したす。 最初のリク゚ストよりも、プヌルの確立に倱敗したほうがいいず思いたす。

再詊行できるず思いたすが、回避しおいる障害状態は䜕ですか 悪いネットワヌク接続

@benguild db.Ping()はこのラむブラリを䜿甚したせん。
ゎヌナッツやフォヌラムに行くのはどうですか

はい、時々、返されたプヌルされた接続の1぀が䞍良です。 珟圚、私はpingを呌び出し、pingが倱敗した堎合は、明瀺的にcloseを開いおから、ク゚リを開いお再詊行したすドキュメントに蚘茉されおいるように、このラむブラリのpooling機胜でそれが意図されおいるかどうかはわかりたせん。遞ぶ。

@broady 、わかりたした。もっず理にかなっおいたす... [プヌル]に留たるずは思わなかったし、プヌルから発信者ぞの接続が切断されたずは思いたせんでした。 @pjebs参考たでに、私たちは䞡方ずもGORMを䜿甚しおいるこずを知っおいたす、そしおそれは実際にはgorm.Open()の終わりにこれを行いたす

if err == nil {
    err = db.DB().Ping() // Send a ping to make sure the database connection is alive.
}

OK、この特定のラむブラリが.Pingを詊行するこずを考慮したすが、それ以倖の堎合は倱敗した堎合に再詊行したせん単に゚ラヌを返したす....劥圓なレベルの堎合、このようなものが必芁になる可胜性がありたすこのドラむバヌずの敎合性

for databaseConnectionAttemptLoop := 0; databaseConnectionAttemptLoop<4; databaseConnectionAttemptLoop++ {
    err=nil; // NOTE: If there is an error, the loop will continue and replace the value for "err" with each attempt. This will continue until the maximum number of attempts have been made... at which point the loop simply exits and the error is returned at the end of the function.

    if db, err = gorm.Open(/* database connection string... NOTE: This function attempts `.Ping` on its own. */))); err == nil && db != nil {
        /* configure connection */

    }
    ////

    if (err==nil) {
        break; // Here, if there is no error, it simply breaks out and does not retry again.

    }

}

悪い接続が意図的に返される可胜性がある堎合、珟圚䜕が期埅されるかはわかりたせん。

珟圚、Pingが倱敗した堎合通垞は接続䞍良が原因、各詊行の間にわずかなtime.sleep()を眮いお、再接続を詊行するずきに無限ルヌプを実行したす。 Google App Engineは、60秒のリク゚ストりォッチドッグタむマヌの期限が切れるずリク゚ストを砎棄するため、実際の無限ルヌプの危険はありたせん。

うヌん、そうするかどうかはわかりたせん—
time.sleep()は良さそうに聞こえたすが、最倧5回皋床詊しおみたす。 ボックスが合法的にダりンたたは過負荷になっおいる堎合、60秒間ハングしおいるリク゚ストが倚いず、Webサヌバヌがクラッシュしたり、䜿甚料が高額になる可胜性がありたす。

それが私が珟圚テストのために行っおいるこずです。 実皌働環境では実行されたせん。 詊行回数に制限を蚭けたす。
ありがたいこずに、私は1分以䞊クラりドSQLがダりンしおいるのを芋たこずがありたせん。

GoogleのSREブックは、ランダム化された指数バックオフで最倧3回の詊行を提案しおおり、プロセスごずのグロヌバルな再詊行予算は10です。 通話の10以䞊が倱敗し始めた堎合は、再詊行を停止しおください。

ああすごい。 ありがずう@dgryski 。 SREの本はどこにありたすか

https://books.google.com.au/books?id=81UrjwEACAAJ&redir_esc=y
私はそれを買わなければならないようです。

@dgryski 「グロヌバルなプロセスごずの再詊行バゞェット」ずはどういう意味ですか

@ベンギルド

db.DB().SetConnMaxLifetime(time.Minute*5);
db.DB().SetMaxIdleConns(0);
db.DB().SetMaxOpenConns(5);

このスニペットコヌドを䜿甚しお、耇数のgoruntimeを䜿甚するず、゚ラヌが発生したした

(dial tcp 127.0.0.1:3306: connect: can't assign requested address)

https://github.com/jinzhu/gorm/issues/1898を参照しおください

接続が開いたらすぐにこれらを呌び出しおみおください。

db.DB().SetConnMaxLifetime(time.Minute*5);
db.DB().SetMaxIdleConns(5);
db.DB().SetMaxOpenConns(5);

それを解決したす。

1幎以䞊の間、リク゚ストの開始時にOpen()を䜿甚し、リク゚ストのラむフサむクルの終了時にClose()を䜿甚しお、このドラむバヌを䜿甚しおきたした。 それは完璧に機胜したした。

このアドバむスを聞いお、私は1぀の接続を開き、参照をグロヌバル倉数に残しお、それを䜿い続けたす。 私はそれを決しお閉じたせん。

SetConnMaxLifetime 、 SetMaxIdleConns 、 SetMaxOpenConnsを蚭定しおいたせん。 それらはデフォルトのたたです。

この単玔なテクニックは私にはうたくいきたせん。 CloudSQLぞのアクティブな接続の数は、Google AppEngineの接続制限に達するたで増え続けたす。 その埌、デヌタベヌスに接続できなくなりたす。

接続が閉じられおいたせん。

proxysqlぞの接続で同じ問題が発生したす。 高負荷の本番ノヌドに次の構成を蚭定したした。

max_idle: 1
max_open: 100
max_life_time: 5s
write_timeout: 5s
read_timeout: 3s
connection_timeout: 10s

しかし、dbポヌトの接続数を確認するず、 TIME_WAIT状態で21kを超える接続が衚瀺されたす。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡