Datenbankverbindungen verwalten

Diese Seite enthält Best Practices und sprachspezifische Codebeispiele, mit denen Sie Anwendungen erstellen können, die Cloud SQL-Datenbankverbindungen effektiv nutzen.

Die Beispiele sind Auszüge aus einer vollständigen App Engine-Anwendung, die Ihnen auf GitHub zur Verfügung steht. Weitere Informationen

Verbindungspools

Ein Verbindungspool ist ein Cache von Datenbankverbindungen, die zur Verbesserung der Verbindungslatenz und -leistung gemeinsam genutzt und wiederverwendet werden. Wenn eine Anwendung eine Datenbankverbindung benötigt, leiht sie sich vorübergehend eine aus dem Pool aus. Sobald die Anwendung die Verbindung nicht mehr benötigt, gibt sie sie an den Pool zurück. Dort steht sie zur erneuten Verwendung zur Verfügung.

Verbindungen öffnen und schließen

Wenn Sie einen Verbindungspool verwenden, müssen Sie die Verbindungen ordnungsgemäß öffnen und schließen, damit sie immer wieder an den Pool zurückgegeben werden, sobald Sie sie nicht mehr brauchen. Nicht zurückgegebene oder "verlorene" Verbindungen werden nicht wiederverwendet, was Ressourcen verschwendet und zu Leistungsengpässen für Ihre Anwendung führen kann.

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, time_cast=time_cast, candidate=team)
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#

insertTimestamp = DateTime.UtcNow;
try
{
    using(var connection = new MySqlConnection(_connectionString.ConnectionString))
    {
        connection.OpenWithRetry();
        using (var insertVoteCommand = connection.CreateCommand())
        {
            insertVoteCommand.CommandText =
                @"INSERT INTO votes (candidate, time_cast) VALUES (@candidate, @time_cast)";
            var candidate = insertVoteCommand.CreateParameter();
            candidate.ParameterName = "@candidate";
            candidate.DbType = DbType.String;
            candidate.Value = team;
            insertVoteCommand.Parameters.Add(candidate);
            var timeCast = insertVoteCommand.CreateParameter();
            timeCast.ParameterName = "@time_cast";
            timeCast.DbType = DbType.DateTime;
            timeCast.Value = insertTimestamp;
            insertVoteCommand.Parameters.Add(timeCast);
            await insertVoteCommand.ExecuteNonQueryAsync();
        }
    }
    return Content($"Vote successfully cast for '{team}' at time {insertTimestamp}!");
}
catch (Exception ex)
{
    // If something goes wrong, handle the error in this
    // section. This might involve retrying or adjusting
    // parameters depending on the situation.
    return StatusCode((int)HttpStatusCode.InternalServerError, ex);
}

Go

sqlInsert := "INSERT INTO votes(candidate, created_at, updated_at) VALUES(?, NOW(), NOW())"
if team == "TABS" || team == "SPACES" {
	if _, err := app.db.Exec(sqlInsert, team); err != nil {
		fmt.Fprintf(w, "unable to save vote: %s", err)
		return fmt.Errorf("DB.Exec: %v", err)
	}
	fmt.Fprintf(w, "Vote successfully cast for %s!\n", team)
}
return nil

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
    );
}

Verbindungsanzahl

Jede Datenbankverbindung nutzt client- und serverseitige Ressourcen. Darüber hinaus sind in Cloud SQL Verbindungslimits festgelegt, die nicht überschritten werden können. Das Erstellen und Verwenden von weniger Verbindungen verringert den Aufwand und hilft Ihnen, das Verbindungslimit einzuhalten.

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.
dbPool.SetMaxIdleConns(5)

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

Ruby

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

PHP

PDO bietet derzeit keine Funktion zum Konfigurieren von Verbindungslimits.

Exponentielle Backoffs

Wenn Ihre Anwendung erfolglos versucht, eine Verbindung zur Datenbank herzustellen, ist die Datenbank möglicherweise vorübergehend nicht verfügbar. In diesem Fall können durch das Senden zu vieler gleichzeitiger Verbindungsanfragen zusätzliche Datenbankressourcen verschwendet werden, was die Wiederherstellung verzögert. Exponentielle Backoffs verhindern, dass Ihre Anwendung zu viele Verbindungsanfragen sendet, wenn keine Verbindung zur Datenbank hergestellt werden kann.

Dieser Wiederholungsversuch ist nur bei der ersten Verbindung oder beim ersten Zugriff auf eine Verbindung aus dem Pool sinnvoll. Wenn während einer Transaktion Fehler auftreten, muss die Anwendung den Wiederholungsversuch ausführen und dabei die Transaktion von vorn starten. Selbst wenn der Pool ordnungsgemäß konfiguriert ist, können bei Verbindungsunterbrechungen in der Anwendung Fehler auftreten.

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

Das Paket database/sql bietet derzeit keine Funktion zum Konfigurieren von exponentiellen Backoffs.

Ruby

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

PHP

PDO bietet derzeit keine Funktion zum Konfigurieren von exponentiellen Backoffs.

Zeitüberschreitung der Verbindung

Es gibt viele Gründe, warum ein Verbindungsversuch fehlschlagen kann. Die Netzwerkkommunikation kann niemals garantiert werden und die Datenbank reagiert möglicherweise vorübergehend nicht. Achten Sie darauf, dass Ihre Anwendung unterbrochene oder fehlgeschlagene Verbindungen ordnungsgemäß verarbeitet.

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

Das Paket database/sql bietet derzeit keine Funktion zum Konfigurieren von Zeitlimits für Verbindungen. Zeitlimits werden auf Treiberebene konfiguriert.

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.
$connConfig = [
    PDO::ATTR_TIMEOUT => 5,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
];

Verbindung beenden

Ein MySQL-Nutzer mit der Berechtigung PROCESS in Cloud SQL kann eine KILL-Anweisung für eine Verbindung eines anderen MySQL-Nutzers ausführen (außer Cloud SQL-Administratornutzer).

Sie können die Verbindungen zu einer Instanz mit dem MySQL-Client und dem Befehl SHOW PROCESSLIST auflisten. Verwenden Sie Id, um die Verbindung zu beenden. Beispiel:

mysql> SHOW PROCESSLIST;
mysql> KILL 6;

Verbindungsdauer

Durch die Begrenzung der Verbindungsdauer kann verhindert werden, dass sich abgebrochene Verbindungen ansammeln. Die Verbindungsdauer können Sie über den Verbindungspool begrenzen.

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
# reestablished
"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

Die Node.js-Bibliothek mysql bietet derzeit keine Funktion zum Steuern der Verbindungsdauer.

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.
dbPool.SetConnMaxLifetime(1800)

Ruby

ActiveRecord bietet derzeit keine Funktion zum Steuern der Verbindungsdauer.

PHP

PDO bietet derzeit keine Funktion zum Steuern der Verbindungsdauer.

Klicken Sie unten auf den jeweiligen Link, um sich die vollständige Anwendung anzusehen.

Python

Sehen Sie sich die vollständige Anwendung für die Programmiersprache Python an.

Java

Sehen Sie sich die vollständige Anwendung für die Programmiersprache Java an.

Node.js

Sehen Sie sich die vollständige Anwendung für die Programmiersprache Node.js an.

C#

Sehen Sie sich die vollständige Anwendung für die Programmiersprache C# an.

Go

Sehen Sie sich die vollständige Anwendung für die Programmiersprache Go an.

Ruby

Sehen Sie sich die vollständige Anwendung für die Programmiersprache Ruby an.

PHP

Sehen Sie sich die vollständige Anwendung für die Programmiersprache PHP an.

Nächste Schritte