Gestione delle connessioni ai database

Questa pagina fornisce best practice ed esempi di codice specifici per i linguaggi per aiutarti a creare applicazioni che utilizzano in modo efficace le connessioni ai database Cloud SQL.

Questi esempi sono estratti di un'applicazione web completa disponibile su GitHub. Scopri di più.

Per istruzioni dettagliate sull'esecuzione di un'applicazione web di esempio connessa a Cloud SQL, segui il link per il tuo ambiente:

Pool di connessione

Un pool di connessioni è una cache di connessioni di database che vengono condivise e riutilizzate per migliorare la latenza e le prestazioni della connessione. Quando l'applicazione richiede una connessione al database, ne prende temporaneamente in prestito una dal pool; quando l'applicazione ha completato la connessione, restituisce la connessione al pool, dove può essere riutilizzata la prossima volta che l'applicazione richiede una connessione al database.

Aprire e chiudere le connessioni

Quando utilizzi un pool di connessioni, devi aprire e chiudere le connessioni correttamente, in modo che le connessioni vengano sempre restituite al pool quando hai finito di utilizzarle. Le connessioni non ripristinate o "perdite" non vengono riutilizzate, il che comporta uno spreco di risorse e può causare colli di bottiglia delle prestazioni per l'applicazione.

Python

# Preparing a statement before hand can help protect against injections.
stmt = sqlalchemy.text(
    "INSERT INTO votes (time_cast, candidate) VALUES (:time_cast, :candidate)"
)
try:
    # Using a with statement ensures that the connection is always released
    # back into the pool at the end of statement (even if an error occurs)
    with db.connect() as conn:
        conn.execute(stmt, parameters={"time_cast": time_cast, "candidate": team})
        conn.commit()
except Exception as e:
    # If something goes wrong, handle the error in this section. This might
    # involve retrying or adjusting parameters depending on the situation.
    # ...

Java

// Using a try-with-resources statement ensures that the connection is always released back
// into the pool at the end of the statement (even if an error occurs)
try (Connection conn = pool.getConnection()) {

  // PreparedStatements can be more efficient and project against injections.
  String stmt = "INSERT INTO votes (time_cast, candidate) VALUES (?, ?);";
  try (PreparedStatement voteStmt = conn.prepareStatement(stmt);) {
    voteStmt.setTimestamp(1, now);
    voteStmt.setString(2, team);

    // Finally, execute the statement. If it fails, an error will be thrown.
    voteStmt.execute();
  }
} catch (SQLException ex) {
  // If something goes wrong, handle the error in this section. This might involve retrying or
  // adjusting parameters depending on the situation.
  // ...
}

Node.js

try {
  const stmt = 'INSERT INTO votes (time_cast, candidate) VALUES (?, ?)';
  // Pool.query automatically checks out, uses, and releases a connection
  // back into the pool, ensuring it is always returned successfully.
  await pool.query(stmt, [timestamp, team]);
} catch (err) {
  // If something goes wrong, handle the error in this section. This might
  // involve retrying or adjusting parameters depending on the situation.
  // ...
}

C#

using MySql.Data.MySqlClient;
using System;

namespace CloudSql
{
    public class MySqlTcp
    {
        public static MySqlConnectionStringBuilder NewMysqlTCPConnectionString()
        {
            // Equivalent connection string:
            // "Uid=<DB_USER>;Pwd=<DB_PASS>;Host=<INSTANCE_HOST>;Database=<DB_NAME>;"
            var connectionString = new MySqlConnectionStringBuilder()
            {
                // Note: Saving credentials in environment variables is convenient, but not
                // secure - consider a more secure solution such as
                // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
                // keep secrets safe.
                Server = Environment.GetEnvironmentVariable("INSTANCE_HOST"),   // e.g. '127.0.0.1'
                // Set Host to 'cloudsql' when deploying to App Engine Flexible environment
                UserID = Environment.GetEnvironmentVariable("DB_USER"),   // e.g. 'my-db-user'
                Password = Environment.GetEnvironmentVariable("DB_PASS"), // e.g. 'my-db-password'
                Database = Environment.GetEnvironmentVariable("DB_NAME"), // e.g. 'my-database'

                // The Cloud SQL proxy provides encryption between the proxy and instance.
                SslMode = MySqlSslMode.Disabled,
            };
            connectionString.Pooling = true;
            // Specify additional properties here.
            return connectionString;

        }
    }
}

Go

insertVote := "INSERT INTO votes(candidate, created_at) VALUES(?, NOW())"
_, err := db.Exec(insertVote, team)

Ruby

@vote = Vote.new candidate: candidate

# ActiveRecord creates and executes your SQL and automatically
# handles the opening and closing of the database connection.
if @vote.save
  render json: "Vote successfully cast for \"#{@vote.candidate}\" at #{@vote.time_cast} PST!"
else
  render json: @vote.errors, status: :unprocessable_entity
end

PHP

// Use prepared statements to guard against SQL injection.
$sql = 'INSERT INTO votes (time_cast, candidate) VALUES (NOW(), :voteValue)';

try {
    $statement = $conn->prepare($sql);
    $statement->bindParam('voteValue', $value);

    $res = $statement->execute();
} catch (PDOException $e) {
    throw new RuntimeException(
        'Could not insert vote into database. The PDO exception was ' .
        $e->getMessage(),
        $e->getCode(),
        $e
    );
}

Numero di connessioni

Ogni connessione al database utilizza risorse lato client e lato server. Inoltre, Cloud SQL impone limiti di connessione complessivi che non possono essere superati. La creazione e l'utilizzo di meno connessioni riducono l'overhead e ti aiutano a rimanere al di sotto del limite di connessioni.

Python

# Pool size is the maximum number of permanent connections to keep.
pool_size=5,
# Temporarily exceeds the set pool_size if no connections are available.
max_overflow=2,
# The total number of concurrent connections for your application will be
# a total of pool_size and max_overflow.

Java

// maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal
// values for this setting are highly variable on app design, infrastructure, and database.
config.setMaximumPoolSize(5);
// minimumIdle is the minimum number of idle connections Hikari maintains in the pool.
// Additional connections will be established to meet this value unless the pool is full.
config.setMinimumIdle(5);

Node.js

// 'connectionLimit' is the maximum number of connections the pool is allowed
// to keep at once.
connectionLimit: 5,

C#

// MaximumPoolSize sets maximum number of connections allowed in the pool.
connectionString.MaximumPoolSize = 5;
// MinimumPoolSize sets the minimum number of connections in the pool.
connectionString.MinimumPoolSize = 0;

Go

// Set maximum number of connections in idle connection pool.
db.SetMaxIdleConns(5)

// Set maximum number of open connections to the database.
db.SetMaxOpenConns(7)

Ruby

# 'pool' is the maximum number of permanent connections to keep.
pool: 5

PHP

Al momento PDO non offre alcuna funzionalità per configurare i limiti di connessione.

Backoff esponenziale

Se l'applicazione tenta di connettersi al database senza riuscirci, il database potrebbe essere temporaneamente non disponibile. In questo caso, l'invio di richieste di connessione ripetute comporta uno spreco di risorse. È preferibile attendere prima di inviare richieste di connessione aggiuntive per consentire al database di tornare ad accedere. L'utilizzo di un backoff esponenziale o di un altro meccanismo di ritardo consente di raggiungere questo obiettivo.

Questo nuovo tentativo ha senso solo alla prima connessione o quando si ottiene una connessione dal pool per la prima volta. Se si verificano errori a metà di una transazione, l'applicazione deve ripetere questo tentativo dall'inizio della transazione. Quindi, anche se il pool è configurato correttamente, l'applicazione potrebbe comunque visualizzare errori in caso di perdita delle connessioni.

Python

# SQLAlchemy automatically uses delays between failed connection attempts,
# but provides no arguments for configuration.

Java

// Hikari automatically delays between failed connection attempts, eventually reaching a
// maximum delay of `connectionTimeout / 2` between attempts.

Node.js

// The mysql module automatically uses exponential delays between failed
// connection attempts.

C#

Policy
    .Handle<MySqlException>()
    .WaitAndRetry(new[]
    {
        TimeSpan.FromSeconds(1),
        TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(5)
    })
    .Execute(() => connection.Open());

Go

Il pacchetto database/sql al momento non offre alcuna funzionalità per configurare il backoff esponenziale.

Ruby

# ActiveRecord automatically uses delays between failed connection attempts,
# but provides no arguments for configuration.

PHP

Al momento PDO non offre alcuna funzionalità per configurare il backoff esponenziale.

Timeout della connessione

Esistono molti motivi per cui un tentativo di connessione potrebbe non riuscire. La comunicazione di rete non è mai garantita e il database potrebbe non essere temporaneamente in grado di rispondere. Assicurati che l'applicazione gestisca le connessioni interrotte o non riuscite.

Python

# 'pool_timeout' is the maximum number of seconds to wait when retrieving a
# new connection from the pool. After the specified amount of time, an
# exception will be thrown.
pool_timeout=30,  # 30 seconds

Java

// setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout.
// Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an
// SQLException.
config.setConnectionTimeout(10000); // 10 seconds
// idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that
// sit idle for this many milliseconds are retried if minimumIdle is exceeded.
config.setIdleTimeout(600000); // 10 minutes

Node.js

// 'connectTimeout' is the maximum number of milliseconds before a timeout
// occurs during the initial connection to the database.
connectTimeout: 10000, // 10 seconds
// 'acquireTimeout' is the maximum number of milliseconds to wait when
// checking out a connection from the pool before a timeout error occurs.
acquireTimeout: 10000, // 10 seconds
// 'waitForConnections' determines the pool's action when no connections are
// free. If true, the request will queued and a connection will be presented
// when ready. If false, the pool will call back with an error.
waitForConnections: true, // Default: true
// 'queueLimit' is the maximum number of requests for connections the pool
// will queue at once before returning an error. If 0, there is no limit.
queueLimit: 0, // Default: 0

C#

// ConnectionTimeout sets the time to wait (in seconds) while
// trying to establish a connection before terminating the attempt.
connectionString.ConnectionTimeout = 15;

Go

Il pacchetto database/sql al momento non offre alcuna funzionalità per configurare il timeout della connessione. Il timeout è configurato a livello di driver.

Ruby

# 'timeout' is the maximum number of seconds to wait when retrieving a
# new connection from the pool. After the specified amount of time, an
# ActiveRecord::ConnectionTimeoutError will be raised.
timeout: 5000

PHP

// Here we set the connection timeout to five seconds and ask PDO to
// throw an exception if any errors occur.
[
    PDO::ATTR_TIMEOUT => 5,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]

Terminare una connessione

Gli utenti con il privilegio PROCESS in Cloud SQL possono elencare le connessioni che non gestiscono. In MySQL 5.7.x, gli utenti devono disporre del privilegio SUPER e in MySQL 8.0.x, gli utenti devono avere il privilegio CONNECTION_ADMIN per eseguire un'istruzione KILL su queste connessioni. L'istruzione KILL termina la connessione di qualsiasi altro utente MySQL (eccetto gli utenti amministrativi di Cloud SQL). Gli utenti senza questi privilegi possono solo elencare e terminare le connessioni che gestiscono.

Puoi elencare le connessioni a un'istanza utilizzando il client mysql ed eseguendo il comando SHOW PROCESSLIST. Usa Id per terminare la connessione. Ad esempio:

mysql> SHOW PROCESSLIST;
mysql> KILL 6;

Durata della connessione

Limitare la durata di una connessione può aiutare a impedire l'accumulo di connessioni abbandonate. Puoi utilizzare il pool di connessioni per limitare la durata delle connessioni.

Python

# 'pool_recycle' is the maximum number of seconds a connection can persist.
# Connections that live longer than the specified amount of time will be
# re-established
pool_recycle=1800,  # 30 minutes

Java

// maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that
// live longer than this many milliseconds will be closed and reestablished between uses. This
// value should be several minutes shorter than the database's timeout value to avoid unexpected
// terminations.
config.setMaxLifetime(1800000); // 30 minutes

Node.js

La libreria Node.js "mysql" al momento non offre alcuna funzionalità per controllare la durata di una connessione.

C#

// ConnectionLifeTime sets the lifetime of a pooled connection
// (in seconds) that a connection lives before it is destroyed
// and recreated. Connections that are returned to the pool are
// destroyed if it's been more than the number of seconds
// specified by ConnectionLifeTime since the connection was
// created. The default value is zero (0) which means the
// connection always returns to pool.
connectionString.ConnectionLifeTime = 1800; // 30 minutes

Go

// Set Maximum time (in seconds) that a connection can remain open.
db.SetConnMaxLifetime(1800 * time.Second)

Ruby

Al momento ActiveRecord non offre alcuna funzionalità per controllare la durata di una connessione.

PHP

Al momento PDO non offre alcuna funzionalità per controllare la durata di una connessione.

Per visualizzare la richiesta di partecipazione completa, fai clic sul collegamento seguente.

Python

Visualizza l'applicazione completa per il linguaggio di programmazione Python.

Java

Visualizza l'applicazione completa per il linguaggio di programmazione Java.

Node.js

Visualizza l'applicazione completa per il linguaggio di programmazione Node.js.

C#

Visualizza l'applicazione completa per il linguaggio di programmazione C#.

Go

Visualizza l'applicazione completa per il linguaggio di programmazione Go.

Ruby

Visualizza l'applicazione completa per il linguaggio di programmazione Ruby.

PHP

Visualizza l'applicazione completa per il linguaggio di programmazione PHP.

Passaggi successivi