PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Goroutines блокира пул от връзки

Това, което имате, е блокировка . В най-лошия сценарий имате 15 goroutines, държащи 15 връзки към бази данни и всички тези 15 goroutines изискват нова връзка, за да продължат. Но за да получите нова връзка, ще трябва да преминете и да освободите връзка:блокиране.

Свързаната статия в wikipedia описва предотвратяването на блокиране. Например изпълнението на код трябва да влиза в критична секция (която заключва ресурси), когато разполага с всички ресурси, от които се нуждае (или ще има нужда). В този случай това означава, че ще трябва да запазите 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)
        }
    }()

Също така имайте предвид, че във втория for цикъл на подготвен израз (sql.Stmt ), придобит от DB.Prepare() вероятно би било много по-добър избор да изпълните една и съща (параметризирана) заявка няколко пъти.

Друга опция е да стартирате последващи заявки в нови goroutines, така че заявката, изпълнена в това, да може да се случи, когато текущо заключената връзка бъде освободена (или всяка друга връзка, заключена от която и да е друга goroutine), но след това без изрична синхронизация нямате контрол, когато те биват екзекутирани. Може да изглежда така:

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

За да накарате вашата програма да чака и тези goroutines, използвайте WaitGroup вече сте в действие:

        // 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. SQLAlchemy, Psycopg2 и Postgresql COPY

  2. Команда PostgreSql, за да видите данните от таблицата

  3. Как да заменя таблица в Postgres?

  4. Какво е еквивалентът на DO блок за Postgres <9.0

  5. Грешка в PostGIS:Не може да се избере функция с най-добър кандидат