sql >> データベース >  >> RDS >> PostgreSQL

Goroutinesが接続プールをブロックしました

    あなたが持っているのはデッドロック です。 。最悪のシナリオでは、15個のデータベース接続を保持する15個のゴルーチンがあり、それらの15個のゴルーチンはすべて、続行するために新しい接続が必要です。ただし、新しい接続を取得するには、接続を進めて解放する必要があります:デッドロック。

    リンクされたウィキペディアの記事では、デッドロックの防止について詳しく説明しています。たとえば、コードの実行では、必要な(または必要になる)すべてのリソースがある場合にのみ、クリティカルセクション(リソースをロックする)に入る必要があります。この場合、これは2つの接続を予約する必要があることを意味します(正確に2つ。1つしか使用できない場合はそのままにして待機します)。2つある場合は、クエリを続行します。ただし、Goでは事前に接続を予約することはできません。クエリを実行するときに、必要に応じて割り当てられます。

    通常、このパターンは避ける必要があります。最初に(有限の)リソース(この場合はdb接続)を予約するコードを記述しないでください。それを解放する前に、別のリソースを要求します。

    簡単な回避策は、最初のクエリを実行し、その結果を保存して(たとえば、Goスライスに)、それが完了したら、後続のクエリに進むことです(ただし、 sql.Rows 最初)。このように、コードは同時に2つの接続を必要としません。

    そして、エラーを処理することを忘れないでください!簡潔にするために省略しましたが、コードに含めるべきではありません。

    これはどのように見えるかです:

    go func() {
        defer wg.Done()
    
        rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
        var data []int // Use whatever type describes data you query
        for rows.Next() {
            var something int
            rows.Scan(&something)
            data = append(data, something)
        }
        rows.Close()
    
        for _, v := range data {
            // You may use v as a query parameter if needed
            db.Exec("SELECT * FROM reviews LIMIT 1")
        }
    }()
    

    rows.Close()に注意してください deferとして実行する必要があります (パニックの場合でも)実行されることを確認するステートメント。ただし、単にdefer rows.Close()を使用する場合 、これは後続のクエリが実行された後にのみ実行されるため、デッドロックを防ぐことはできません。したがって、deferを使用できる別の関数(匿名関数の場合もあります)で呼び出すようにリファクタリングします。 :

        rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
        var data []int // Use whatever type describes data you query
        func() {
            defer rows.Close()
            for rows.Next() {
                var something int
                rows.Scan(&something)
                data = append(data, something)
            }
        }()
    

    また、2番目のforにも注意してください プリペアドステートメントをループします( sql.Stmt DB.Prepare()によって取得されました 同じ(パラメータ化された)クエリを複数回実行する方がおそらくはるかに良い選択でしょう。

    別のオプションは、新しいゴルーチンで後続のクエリを起動して、現在ロックされている接続(または他のゴルーチンによってロックされている他の接続)が解放されたときに実行されるクエリを実行できるようにすることですが、明示的な同期がないと、いつ制御できませんそれらは実行されます。次のようになります:

    go func() {
        defer wg.Done()
    
        rows, _ := db.Query("SELECT * FROM reviews LIMIT 1")
        defer rows.Close()
        for rows.Next() {
            var something int
            rows.Scan(&something)
            // Pass something if needed
            go db.Exec("SELECT * FROM reviews LIMIT 1")
        }
    }()
    

    プログラムでこれらのゴルーチンも待機させるには、WaitGroupを使用します。 すでに行動しています:

            // Pass something if needed
            wg.Add(1)
            go func() {
                defer wg.Done()
                db.Exec("SELECT * FROM reviews LIMIT 1")
            }()
    



    1. PSQLでユーザーを作成する方法

    2. JSONの作成と複雑なクエリの編集(oracle 11g)

    3. MYSQL-複数のテーブルを参照する1つの列

    4. OSXでpostgresをセットアップしようとしています