PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Gorutyny zablokowały pulę połączeń

Masz zakleszczenie . W najgorszym przypadku masz 15 gorutyn posiadających 15 połączeń z bazą danych, a wszystkie te 15 gorutyn wymagają nowego połączenia, aby kontynuować. Ale aby uzyskać nowe połączenie, należałoby przejść dalej i zwolnić połączenie:impas.

Powiązany artykuł w Wikipedii zawiera szczegółowe informacje na temat zapobiegania impasowi. Na przykład wykonanie kodu powinno wprowadzić sekcję krytyczną (która blokuje zasoby), gdy ma wszystkie potrzebne zasoby (lub będzie potrzebować). W tym przypadku oznacza to, że musiałbyś zarezerwować 2 połączenia (dokładnie 2; jeśli tylko 1 jest dostępne, zostaw je i poczekaj), a jeśli masz te 2, dopiero wtedy kontynuuj zapytania. Ale w Go nie ma możliwości wcześniejszej rezerwacji połączeń. Są one przydzielane w razie potrzeby podczas wykonywania zapytań.

Generalnie należy unikać tego wzorca. Nie powinieneś pisać kodu, który najpierw rezerwuje (skończony) zasób (w tym przypadku połączenie z bazą danych), a zanim go zwolni, będzie wymagał kolejnego.

Łatwym obejściem jest wykonanie pierwszego zapytania, zapisanie jego wyniku (np. w wycinku Go), a kiedy już to zrobisz, przejdź do kolejnych zapytań (ale nie zapomnij też zamknąć sql.Rows pierwszy). W ten sposób Twój kod nie wymaga 2 połączeń jednocześnie.

I nie zapomnij poradzić sobie z błędami! Pominąłem je dla zwięzłości, ale nie powinieneś w kodzie.

Tak mogłoby to wyglądać:

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")
    }
}()

Zauważ, że rows.Close() powinien być wykonywany jako defer oświadczenie, aby upewnić się, że zostanie wykonane (nawet w przypadku paniki). Ale jeśli po prostu użyjesz defer rows.Close() , który zostałby wykonany dopiero po wykonaniu kolejnych zapytań, więc nie zapobiegnie zakleszczeniu. Więc przekształciłbym go, aby wywołać go w innej funkcji (która może być funkcją anonimową), w której można użyć 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)
        }
    }()

Zauważ też, że w drugim for zapętl przygotowaną instrukcję (sql.Stmt ) nabyte przez DB.Prepare() byłoby prawdopodobnie znacznie lepszym wyborem do wielokrotnego wykonania tego samego (sparametryzowanego) zapytania.

Inną opcją jest uruchamianie kolejnych zapytań w nowych gorutynach, tak aby zapytanie wykonane w tym miejscu mogło nastąpić w momencie zwolnienia aktualnie zablokowanego połączenia (lub innego połączenia zablokowanego przez dowolną inną gorutynę), ale wtedy bez jawnej synchronizacji nie masz kontroli, kiedy zostają straceni. Może to wyglądać tak:

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")
    }
}()

Aby Twój program również czekał na te gorutyny, użyj WaitGroup masz już w akcji:

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



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Postgres SELECT gdzie WHERE jest UUID lub ciągiem

  2. Jak zrzucić dane dużego obiektu z Postgres 9.4, a następnie zaimportować je do Postgres8.x?

  3. Zrozumienie wydajności zapytań PostgreSQL

  4. Uzyskiwanie wyników z wartościującej w tabeli funkcji Postgresql za pomocą JOOQ

  5. Streaming PostgreSQL a replikacja logiczna – porównanie