Hologres es compatible con el protocolo de comunicación de PostgreSQL, por lo que cualquier herramienta o aplicación que utilice PostgreSQL JDBC Driver puede conectarse. Esta guía explica cómo configurar una conexión JDBC, escribir y consultar datos, así como ajustar el rendimiento.
En esta página
Requisitos previos
Antes de empezar, asegúrese de contar con lo siguiente:
Una instancia de Hologres con una base de datos creada
El endpoint, el puerto y el nombre de la base de datos de su instancia (disponibles en la página Instance Details de la consola de Hologres, en la sección Network Information)
Su AccessKey ID y AccessKey secret
Notas de uso
Utilice PostgreSQL JDBC Driver 42.3.2 o posterior. Esta versión corrige la vulnerabilidad CVE-2022-21724, por lo que las versiones anteriores presentan un riesgo de seguridad.
Para pruebas de rendimiento de escritura, utilice una red Virtual Private Cloud (VPC). La red pública no cumple con los estándares de referencia para pruebas de rendimiento.
-
Hologres no admite combinar operaciones DDL y de escritura en la misma transacción. Ejecute las operaciones DDL y de escritura en transacciones separadas. Si se combinan, se devuelve el error
ERROR: INSERT in ddl transaction is not supported now.Mantenga
autoCommitconfigurado comotrue. Este es el valor predeterminado de JDBC, así que no invoque commit explícitamente en su código:Connection conn = DriverManager.getConnection(url, user, password); conn.setAutoCommit(true); -
Las operaciones de escritura en Hologres no se pueden revertir. Ni
Connection.rollback()ni la sentencia SQLROLLBACKdeshacen un INSERT, UPDATE o DELETE que ya se haya ejecutado; además, una operación de escritura surte efecto de inmediato y es visible para otras conexiones antes del commit.No dependa de las transacciones JDBC para compensar fallos. En su lugar, implemente idempotencia o reconciliación en su aplicación; por ejemplo, utilice
INSERT ON CONFLICTpara que los reintentos sean idempotentes.
Conexión a Hologres mediante JDBC
Paso 1: Agregar la dependencia del controlador
La mayoría de las herramientas cliente SQL incluyen un controlador PostgreSQL integrado; utilícelo si está disponible. Para aplicaciones Java, agregue PostgreSQL JDBC Driver a su proyecto Maven. Descárguelo desde jdbc.postgresql.org/download y use la versión 42.3.2 o posterior (se recomienda la última versión estable).
Hologres utiliza el PostgreSQL JDBC Driver estándar, por lo que no es necesario instalar un controlador específico para Hologres.
Agregue la siguiente dependencia a su archivo pom.xml:
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<!-- Use 42.3.2 or later. The latest stable version is recommended. -->
<version>42.7.13</version>
</dependency>
</dependencies>
Paso 2: Construir la cadena de conexión
El formato de la cadena de conexión es el siguiente:
jdbc:postgresql://<ENDPOINT>:<PORT>/<DBNAME>?user=<ACCESS_ID>&password=<ACCESS_KEY>
Parámetros obligatorios
|
Parameter |
Description |
|
|
La dirección de red de la instancia de Hologres. En la consola, la dirección y el puerto se proporcionan juntos como un solo valor con el formato |
|
|
El puerto de la instancia de Hologres, que corresponde a la parte posterior a los dos puntos en el valor de Domain Name descrito anteriormente. |
|
|
El nombre de la base de datos en Hologres. |
|
|
Su AccessKey ID. Guárdelo en una variable de entorno en lugar de escribirlo directamente en el código. |
|
|
Su AccessKey secret. Guárdelo en una variable de entorno en lugar de escribirlo directamente en el código. |
Parámetros opcionales
Agregue estos parámetros a la cadena de conexión según sea necesario. Separe varios parámetros con &.
|
Parameter |
Effect |
|
|
Etiqueta las conexiones con el nombre de su aplicación, lo que facilita identificarlas en la lista de verificación de consultas lentas. |
|
|
Reescribe las inserciones por lotes en una sola sentencia INSERT de múltiples valores para aumentar el rendimiento de escritura. |
|
|
Establece el esquema predeterminado. Es obligatorio al consultar tablas externas tras habilitar la carga automática de tablas externas desde MaxCompute, ya que los nombres de proyecto de MaxCompute se asignan a esquemas con el mismo nombre. |
|
|
Cambia el controlador JDBC de PostgreSQL del protocolo extendido ( |
Acerca de preferQueryMode
Bajo el protocolo extendido, los parámetros de PreparedStatement se envían con tipos explícitos y el controlador no realiza conversiones implícitas de tipo. Por ello, comparar una columna de enteros con un parámetro varchar puede fallar con un error como ERROR: operator does not exist: integer = character varying. Si encuentra este error de incompatibilidad de tipos, agregue preferQueryMode=simple a la cadena de conexión. De este modo, el controlador envía la sentencia SQL como texto plano y permite que el servidor infiera los tipos de parámetros según el contexto.
Con el modo simple, el controlador deja de usar sentencias preparadas del lado del servidor, por lo que se pierden los resultados de compilación SQL almacenados en caché de los que depende el modo Prepared Statement. El rendimiento disminuye notablemente en consultas de alta frecuencia y escrituras por lotes, lo cual contradice la recomendación de usar el modo Prepared Statement para obtener un mayor rendimiento que se menciona en otra parte de este tema.
Resuelva los errores de incompatibilidad de tipos siguiendo este orden de preferencia:
Utilice el método setter que coincida con el tipo de la columna; por ejemplo, use
setInten lugar desetStringpara una columna INT.O bien, convierta el tipo del parámetro explícitamente en SQL, como en
where id = ?::int.Utilice
preferQueryMode=simplesolo cuando no pueda modificar el código que realiza la llamada, como ocurre con herramientas ORM o BI de terceros. Si también necesita rendimiento en escrituras por lotes, puede combinarlo conreWriteBatchedInserts=true, ya que ambas opciones funcionan en conjunto.
Ejemplo de cadena de conexión con las opciones recomendadas:
jdbc:postgresql://<ENDPOINT>:<PORT>/<DBNAME>?user=<ACCESS_ID>&password=<ACCESS_KEY>&ApplicationName=myApp&reWriteBatchedInserts=true
Si una consulta con PreparedStatement falla con un error como ERROR: operator does not exist: integer = character varying debido a una incompatibilidad de tipos entre el parámetro de la consulta y la columna, agregue preferQueryMode=simple a la cadena de conexión:
jdbc:postgresql://<ENDPOINT>:<PORT>/<DBNAME>?user=<ACCESS_ID>&password=<ACCESS_KEY>&preferQueryMode=simple
Paso 3: Almacenar credenciales en variables de entorno
Incluir credenciales directamente en las cadenas de conexión representa un riesgo de seguridad. En su lugar, guárdelas como variables de entorno. En Linux, agregue lo siguiente a su archivo ~/.bash_profile:
export ALIBABA_CLOUD_USER=<ACCESS_ID>
export ALIBABA_CLOUD_PASSWORD=<ACCESS_KEY>
Paso 4: Conectarse y ejecutar una consulta
Para evitar que las conexiones queden colgadas, configure socketTimeout, loginTimeout y tcpKeepAlive, expuestos como SOCKET_TIMEOUT, LOGIN_TIMEOUT y TCP_KEEP_ALIVE en PGProperty. Consulte el siguiente ejemplo.
El siguiente ejemplo lee las credenciales desde variables de entorno, establece las propiedades de tiempo de espera, se conecta a Hologres y ejecuta una consulta SELECT básica mediante una Statement estándar:
import org.postgresql.PGProperty;
import java.sql.*;
import java.util.Properties;
public class HologresTest {
private void jdbcExample() throws SQLException {
String user = System.getenv("ALIBABA_CLOUD_USER");
String password = System.getenv("ALIBABA_CLOUD_PASSWORD");
String url = String.format(
"jdbc:postgresql://<ENDPOINT>:<PORT>/<DBNAME>?currentSchema=<SCHEMA_NAME>&user=%s&password=%s",
user, password
);
Properties props = new Properties();
// Set SOCKET_TIMEOUT based on actual query execution time to avoid
// premature timeout before the query finishes.
PGProperty.SOCKET_TIMEOUT.set(props, 3600);
PGProperty.LOGIN_TIMEOUT.set(props, 60);
PGProperty.TCP_KEEP_ALIVE.set(props, true);
try (Connection conn = DriverManager.getConnection(url, props)) {
try (Statement st = conn.createStatement()) {
String sql = "SELECT pk, f1 FROM test_tb WHERE pk > 0 LIMIT 100";
try (ResultSet rs = st.executeQuery(sql)) {
while (rs.next()) {
// Read the first column value
String c1 = rs.getString(1);
}
}
}
}
}
}
Escritura y consulta de datos
Escritura de datos
Puede escribir datos mediante el modo Statement o Prepared Statement en JDBC. Utilice el modo Prepared Statement para las operaciones de escritura. En este modo, el servidor almacena en caché los resultados de la compilación SQL, lo que reduce la latencia de escritura y aumenta el rendimiento. Establezca el tamaño del lote como un múltiplo de 256; el tamaño mínimo recomendado es 256.
Los ejemplos de esta sección utilizan la siguiente tabla. Créela primero en su base de datos.
CREATE TABLE test_tb (
pk int PRIMARY KEY,
f1 text,
f2 timestamptz,
f3 double precision
);
Inserción por lotes
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
/*
* Write data in batches using Prepared Statement mode.
* Batch size: 256 rows (minimum recommended).
*/
private static void writeBatchWithPreparedStatement(Connection conn) throws Exception {
try (PreparedStatement stmt = conn.prepareStatement("insert into test_tb values (?,?,?,?)")) {
int batchSize = 256;
for (int i = 0; i < batchSize; ++i) {
stmt.setInt(1, 1000 + i);
stmt.setString(2, "1");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
java.util.Date parsedDate = dateFormat.parse("1990-11-11 00:00:00");
stmt.setTimestamp(3, new java.sql.Timestamp(parsedDate.getTime()));
stmt.setDouble(4, 0.1);
stmt.addBatch();
}
stmt.executeBatch();
}
}
Upsert con INSERT ON CONFLICT
Para actualizar filas existentes en caso de conflicto, utilice la sintaxis INSERT ON CONFLICT de PostgreSQL. La tabla de destino debe tener una clave primaria.
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
private static void insertOverwrite(Connection conn) throws Exception {
try (PreparedStatement stmt = conn.prepareStatement(
"insert into test_tb values (?,?,?,?), (?,?,?,?), (?,?,?,?), (?,?,?,?), (?,?,?,?), (?,?,?,?) " +
"on conflict(pk) do update set f1 = excluded.f1, f2 = excluded.f2, f3 = excluded.f3"
)) {
int batchSize = 6;
for (int i = 0; i < batchSize; ++i) {
stmt.setInt(i * 4 + 1, i);
stmt.setString(i * 4 + 2, "1");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
java.util.Date parsedDate = dateFormat.parse("1990-11-11 00:00:00");
stmt.setTimestamp(i * 4 + 3, new java.sql.Timestamp(parsedDate.getTime()));
stmt.setDouble(i * 4 + 4, 0.1);
}
int affectedRows = stmt.executeUpdate();
System.out.println("affected rows => " + affectedRows);
}
}
El siguiente ejemplo utiliza el modo Prepared Statement para operaciones de escritura, lo que mejora el rendimiento en inserciones repetidas:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
private void jdbcPreparedStmtExample() throws SQLException {
String user = System.getenv("ALIBABA_CLOUD_USER");
String password = System.getenv("ALIBABA_CLOUD_PASSWORD");
String url = String.format(
"jdbc:postgresql://<ENDPOINT>:<PORT>/<DBNAME>?currentSchema=<SCHEMA_NAME>&user=%s&password=%s",
user, password
);
try (Connection conn = DriverManager.getConnection(url)) {
String sql = "insert into test values" +
"(?, ?), (?, ?), (?, ?), (?, ?), (?, ?), " +
"(?, ?), (?, ?), (?, ?), (?, ?), (?, ?)";
try (PreparedStatement st = conn.prepareStatement(sql)) {
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 2 * 10; ++j) {
st.setString(j + 1, UUID.randomUUID().toString());
}
System.out.println("affected row => " + st.executeUpdate());
}
}
}
}
Consulta de datos
Utilice sentencias SQL SELECT estándar para consultar datos de las tablas de Hologres. El ejemplo básico de SELECT en el Paso 4 demuestra este patrón.
Para consultas parametrizadas, utilice el modo Prepared Statement y pase los parámetros con el método setter que coincida con el tipo de la columna:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
private static void queryWithPreparedStatement(Connection conn) throws Exception {
String sql = "select pk, f1, f2, f3 from test_tb where pk = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
// pk is an INT column, so use setInt. Passing a String with setString
// fails under the extended protocol with:
// ERROR: operator does not exist: integer = character varying
stmt.setInt(1, 1000);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getInt("pk") + ", " + rs.getString("f1"));
}
}
}
}
Configuración de un pool de conexiones Druid
Utilice Druid 1.1.12 o posterior para conectarse a Hologres.
Notas de uso:
Establezca
keepAlive=truepara reutilizar conexiones y evitar la rotación constante de las mismas.Las versiones de Druid comprendidas entre la 1.2.12 y la 1.2.21 tienen un problema conocido donde
connectTimeoutysocketTimeouttoman un valor predeterminado de 10 segundos si no se especifican. Actualice a una versión más reciente si encuentra este problema.Configure
initialSize,minIdleymaxActivesegún el tamaño de su instancia y la carga de trabajo.
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- Endpoint URL from the instance configuration page in the console -->
<property name="url" value="${jdbc_url}" />
<!-- AccessKey ID of the user account -->
<property name="username" value="${jdbc_user}" />
<!-- AccessKey secret of the user account -->
<property name="password" value="${jdbc_password}" />
<!-- Pool sizing: adjust based on instance size and workload -->
<property name="initialSize" value="5" />
<property name="minIdle" value="10" />
<property name="maxActive" value="20" />
<!-- Wait up to 60 seconds for a connection from the pool -->
<property name="maxWait" value="60000" />
<!-- Check for idle connections every 2 seconds -->
<property name="timeBetweenEvictionRunsMillis" value="2000" />
<!-- Evict connections idle for more than 10 minutes -->
<property name="minEvictableIdleTimeMillis" value="600000" />
<property name="maxEvictableIdleTimeMillis" value="900000" />
<property name="validationQuery" value="select 1" />
<property name="testWhileIdle" value="true" />
<!-- Do not test connections on borrow/return (reduces overhead) -->
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="keepAlive" value="true" />
<property name="phyMaxUseCount" value="100000" />
<property name="filters" value="stat" />
</bean>
Configuración de parámetros GUC
Los parámetros Grand Unified Configuration (GUC) controlan el comportamiento a nivel de sesión, como los tiempos de espera. Configúrelos al momento de la conexión mediante PGProperty.OPTIONS.
El siguiente ejemplo establece statement_timeout e idle_in_transaction_session_timeout en 12,345 milisegundos:
import org.postgresql.PGProperty;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class GucDemo {
public static void main(String[] args) {
String hostname = "hgpostcn-cn-xxxx-cn-hangzhou.hologres.aliyuncs.com";
String port = "80";
String dbname = "demo";
String jdbcUrl = "jdbc:postgresql://" + hostname + ":" + port + "/" + dbname;
Properties properties = new Properties();
properties.setProperty("user", "xxxxx");
properties.setProperty("password", "xxxx");
// Set session-level GUC parameters
PGProperty.OPTIONS.set(properties,
"--statement_timeout=12345 --idle_in_transaction_session_timeout=12345");
try {
Class.forName("org.postgresql.Driver");
Connection connection = DriverManager.getConnection(jdbcUrl, properties);
PreparedStatement preparedStatement =
connection.prepareStatement("show statement_timeout");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
ResultSetMetaData rsmd = resultSet.getMetaData();
int columnCount = rsmd.getColumnCount();
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < columnCount; i++) {
map.put(rsmd.getColumnName(i + 1).toLowerCase(), resultSet.getObject(i + 1));
}
System.out.println(map);
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
Para consultar la lista completa de parámetros GUC disponibles, véase GUC parameters.
Ajuste de rendimiento
Aplique las siguientes prácticas para maximizar el rendimiento de escritura:
Utilice una red VPC. La red pública introduce latencia que impide alcanzar un rendimiento de escritura óptimo.
Habilite la reescritura de lotes. Agregue
reWriteBatchedInserts=truea la cadena de conexión. Esto reescribe las inserciones individuales en una sola sentencia de múltiples valores, lo que aumenta significativamente el rendimiento.Utilice el modo Prepared Statement. El servidor almacena en caché los resultados de la compilación SQL, lo que reduce la latencia por fila.
Establezca el tamaño del lote como un múltiplo de 256. El tamaño mínimo efectivo es 256. Los múltiplos mayores generan mejoras adicionales en el rendimiento. Para la creación automática de lotes, utilice Holo Client.
Ejemplo de cadena de conexión con todas las opciones de rendimiento habilitadas:
jdbc:postgresql://<ENDPOINT>:<PORT>/<DBNAME>?ApplicationName=<APPLICATION_NAME>&reWriteBatchedInserts=true
Balanceo de carga
A partir de Hologres V1.3, puede configurar varias instancias secundarias de solo lectura en JDBC para distribuir las cargas de trabajo de lectura. Para obtener instrucciones de configuración, consulte JDBC-based load balancing.