Faça a gestão das ligações à base de dados

Esta página fornece práticas recomendadas e exemplos de código específicos do idioma para ajudar a criar aplicações que usam ligações à base de dados do Cloud SQL de forma eficaz.

Estas amostras são excertos de uma aplicação Web completa disponível para si no GitHub. Saiba mais.

Para ver instruções passo a passo sobre como executar uma aplicação Web de exemplo ligada ao Cloud SQL, siga o link para o seu ambiente:

Conjuntos de ligações

Um conjunto de ligações é uma cache de ligações à base de dados que são partilhadas e reutilizadas para melhorar a latência e o desempenho das ligações. Quando a sua aplicação precisa de uma ligação à base de dados, pede uma emprestada ao respetivo conjunto temporariamente. Quando a aplicação termina de usar a ligação, devolve-a ao conjunto, onde pode ser reutilizada da próxima vez que a aplicação precisar de uma ligação à base de dados.

Abrir e fechar ligações

Quando usa uma pool de ligações, tem de abrir e fechar as ligações corretamente, para que as ligações sejam sempre devolvidas à pool quando terminar de as usar. As ligações não devolvidas ou "roubadas" não são reutilizadas, o que desperdiça recursos e pode causar gargalos de desempenho para a sua aplicação.

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;

        }
    }
}

Ir

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

Número de ligações

Todas as ligações à base de dados usam recursos do lado do cliente e do lado do servidor. Além disso, o Cloud SQL impõe limites de ligação gerais que não podem ser excedidos. Criar e usar menos ligações reduz a sobrecarga e ajuda a manter-se abaixo do limite de ligações.

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;

Ir

// 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

Atualmente, o PDO não oferece qualquer funcionalidade para configurar limites de ligação.

Retirada exponencial

Se a sua aplicação tentar estabelecer ligação à base de dados e não o conseguir, a base de dados pode estar temporariamente indisponível. Neste caso, o envio de pedidos de ligação repetidos desperdiça recursos. É preferível aguardar antes de enviar pedidos de ligação adicionais para permitir que a base de dados volte a ficar acessível. A utilização de uma retirada exponencial ou outro mecanismo de atraso atinge este objetivo.

Esta nova tentativa só faz sentido quando se estabelece a ligação pela primeira vez ou quando se obtém uma ligação do conjunto pela primeira vez. Se ocorrerem erros a meio de uma transação, a aplicação tem de tentar novamente e tem de o fazer desde o início de uma transação. Assim, mesmo que o conjunto esteja configurado corretamente, a aplicação pode continuar a detetar erros se as ligações forem perdidas.

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

Ir

Atualmente, o pacote database/sql não oferece nenhuma funcionalidade para configurar o recuo exponencial.

Ruby

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

PHP

Atualmente, o PDO não oferece nenhuma funcionalidade para configurar o recuo exponencial.

Limite de tempo da ligação

Existem muitos motivos pelos quais uma tentativa de ligação pode não ter êxito. A comunicação de rede nunca é garantida e a base de dados pode não conseguir responder temporariamente. Certifique-se de que a sua aplicação processa as ligações danificadas ou sem êxito de forma adequada.

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;

Ir

Atualmente, o pacote database/sql não oferece nenhuma funcionalidade para configurar o limite de tempo da ligação. O limite de tempo é configurado ao nível do controlador.

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,
]

Termine uma associação

Os utilizadores com o privilégio PROCESS no Cloud SQL podem listar ligações que não gerem. No MySQL 5.7.x, os utilizadores têm de ter o privilégio SUPER e, no MySQL 8.0.x, os utilizadores têm de ter o privilégio CONNECTION_ADMIN para executar uma declaração KILL nestas ligações. A declaração KILL termina a ligação de qualquer outro utilizador do MySQL (exceto utilizadores administrativos do Cloud SQL). Os utilizadores sem estes privilégios só podem listar e terminar as associações que gerem.

Pode listar as ligações a uma instância através do cliente mysql e executar o comando SHOW PROCESSLIST. Use o Id para terminar a associação. Por exemplo:

mysql> SHOW PROCESSLIST;
mysql> KILL 6;

Duração da ligação

Limitar a duração de uma ligação pode ajudar a evitar a acumulação de ligações abandonadas. Pode usar o conjunto de ligações para limitar a duração das ligações.

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

Atualmente, a biblioteca Node.js "mysql" não oferece qualquer funcionalidade para controlar a duração de uma ligação.

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

Ir

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

Ruby

Atualmente, o ActiveRecord não oferece qualquer funcionalidade para controlar a duração de uma ligação.

PHP

Atualmente, o PDO não oferece qualquer funcionalidade para controlar a duração de uma ligação.

Para ver a candidatura completa, clique no link abaixo.

Python

Veja a aplicação completa para a linguagem de programação Python.

Java

Veja a aplicação completa para a linguagem de programação Java.

Node.js

Veja a aplicação completa para a linguagem de programação Node.js.

C#

Veja a aplicação completa para a linguagem de programação C#.

Ir

Veja a aplicação completa para a linguagem de programação Go.

Ruby

Veja a aplicação completa para a linguagem de programação Ruby.

PHP

Veja a candidatura completa para a linguagem de programação PHP.

O que se segue?