基于JDBC的负载均衡

更新时间:2024-05-30 07:14:25

Hologres从 V1.3版本开始,支持在JDBC配置多个只读从实例以支持简单的负载均衡。本文为您介绍如何基于JDBC实现负载均衡。

背景信息

Hologres中一个主实例可以绑定多个只读从实例,实例与实例之间共享存储,但是计算资源是互相隔离的,从而实现读写分离高可用部署,详情请参见主从实例读写分离部署(共享存储)

Hologres支持使用JDBC配置多个只读从实例,实现简单的负载均衡,如下图所示:image..png

您使用JDBC配置多个只读从实例可以实现以下功能:

  • 将查询请求随机分配到您的只读从实例,避免单个只读从实例负载过高。

  • 查询请求按照顺序连接只读从实例,极大程度避免因为只读从实例故障导致服务不可用,存在以下情况:

    • 当只读从实例1连接失败时,JDBC会自动尝试连接只读从实例2。

    • 当只读从实例1和只读从实例2连接失败时,JDBC会自动尝试连接只读从实例3。

    • 只有当只读从实例1、只读从实例2和只读从实例3均连接失败时,系统才会提示连接失败。

Hologres V2.0.10版本开始,targetServerType参数支持更加丰富的取值,扩展负载均衡使用的场景。

使用说明

前提条件

  • 已购买多个Hologres只读从实例并绑定至Hologres主实例,详情请参见配置共享存储的主从实例

  • 已下载42.3.2以上版本的Postgres JDBC驱动,详情请参见JDBC

命令格式

JDBC配置多个只读从实例需要在连接URL中将多个只读从实例的Endpoint:Port信息用半角逗号(,)分隔,命令格式如下:

jdbc:postgresql://<Endpoint1>:<Port1>,<Endpoint2>:<Port2>,<Endpoint3>:<Port3>.../<DBNAME>?user=<AccessKey ID>&password=<AccessKey Secret>&targetServerType=any&loadBalanceHosts=<value>[&hostRecheckSeconds=<value>]

参数说明

参数

描述

参数

描述

Endpoint

Hologres实例的网络地址。

进入Hologres管理控制台实例详情页获取网络地址。

Port

Hologres实例的端口。

进入Hologres管理控制台实例详情页获取端口。

DBNAME

Hologres创建的数据库名称。

AccessKey ID

当前阿里云账号的AccessKey ID。

您可以单击AccessKey 管理,获取AccessKey ID。

AccessKey Secret

当前阿里云账号的AccessKey Secret。

您可以单击AccessKey 管理,获取AccessKey Secret。

targetServerType

允许连接到指定状态的只读从实例。取值为any,即表示可以连接到URL中的任意Endpoint。

Hologres V2.0.10及以上版本支持如下取值:

  • master:仅连接主实例。

  • slave:仅连接只读从实例。

  • preferSlave:优先连接只读从实例,如果连接不上只读从数据库才连接到主实例。

JDBC会根据GUC参数in_hot_standby的取值判断实例为主实例还是只读从实例。in_hot_standby取值说明如下:

  • off:为主实例。

  • on:为只读从实例。

loadBalanceHosts

指定尝试连接只读从实例的顺序,取值如下:

  • False(默认):按连接URL内顺序连接只读从实例。

  • True:随机连接只读从实例。

hostRecheckSeconds

可连接Endpoint列表的缓存时间,默认为10s

若您希望调整该缓存的时间,可以修改hostRecheckSeconds的参数值,如下示例将缓存改为30s

jdbc:postgresql://{ENDPOINT1}:{PORT1},{ENDPOINT2}:{PORT2},{ENDPOINT3}:{PORT3}.../{DBNAME}?targetServerType=any&loadBalanceHosts=true&hostRecheckSeconds=30

说明

关于更多JDBC配置说明,详情请参见JDBC的手册

使用示例

  • 如下示例会将查询随机分发到三个只读从实例上,并且当其中一个实例连接失败时,JDBC会自动尝试切换到另一个实例连接。

    import java.sql.*;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    public class hatest {
        public static void main(String[] args) {
            // 设置Hologres实例的连接endpoint,只读从实例1
            String endpoint1 = "hgpostcn-cn-wxxxxxxxx01-cn-shanghai.hologres.aliyuncs.com:80";
            // 设置Hologres实例的连接endpoint,只读从实例2
            String endpoint2 = "hgpostcn-cn-wxxxxxxxx02-cn-shanghai.hologres.aliyuncs.com:80";
            // 设置Hologres实例的连接endpoint,只读从实例3
            String endpoint3 = "hgpostcn-cn-wxxxxxxxx03-cn-shanghai.hologres.aliyuncs.com:80";      
            // 设置待连接的数据库名
            String dbname = "postgres";
            String jdbcUrl = "jdbc:postgresql://" + endpoint1 + "," + endpoint2 + "," + endpoint3 + "/" + dbname;
            Properties properties = new Properties();
            // 设置连接数据库的用户名
            properties.setProperty("user", "xxxx");
            //设置连接数据库的密码
            properties.setProperty("password", "xxxx");
            // 配置targetServerType,此处配置为any,表示可以对任意endpoint发送请求
            properties.setProperty("targetServerType", "any");
            // 配置LoadBalance策略,此处配置true,表示开启LoadBalance
            properties.setProperty("loadBalanceHosts", "true");
            // 配置hostRecheckSeconds时间,此处配置为10秒
            properties.setProperty("hostRecheckSeconds", "10");
            try {
                Class.forName("org.postgresql.Driver");
                Connection connection = DriverManager.getConnection(jdbcUrl, properties);
                PreparedStatement preparedStatement = connection.prepareStatement("show hg_frontend_endpoints;" );
                ResultSet resultSet = preparedStatement.executeQuery();
                while (resultSet.next()) {
                    ResultSetMetaData rsmd = resultSet.getMetaData();
                    int columnCount = rsmd.getColumnCount();
                    Map 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();
            }
        }
    }
  • 如下示例可以将查询轮询100次分发到2个实例上。

    import java.sql.*;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    public class hatest {
        public static void main(String[] args) {
            int x = 1;
            while( x <= 100 ){
                // 设置Hologres实例的连接endpoint,主实例
                String endpoint1 = "hgpostcn-cn-wxxxxxxxx04-cn-hangzhou.hologres.aliyuncs.com:80";
                // 设置Hologres实例的连接endpoint,只读从实例
                String endpoint2 = "hgpostcn-cn-wxxxxxxxx05-cn-hangzhou.hologres.aliyuncs.com:80";
                // 设置待连接的数据库名
                String dbname = "postgres";
                String jdbcUrl = "jdbc:postgresql://" + endpoint1 + "," + endpoint2 + "/" + dbname ;
                Properties properties = new Properties();
                // 设置连接数据库的用户名
                properties.setProperty("user", "xxx");
                // 设置连接数据库的密码
                properties.setProperty("password", "xxx");
                // 配置targetServerType,此处配置为any,表示可以对任意endpoint发送请求
                properties.setProperty("targetServerType", "any");
                // 配置LoadBalance策略,此处配置true,表示开启LoadBalance
                properties.setProperty("loadBalanceHosts", "true");
                // 配置hostRecheckSeconds时间,此处配置为10秒
                properties.setProperty("hostRecheckSeconds", "10");
                try {
                    Class.forName("org.postgresql.Driver");
                    Connection connection = DriverManager.getConnection(jdbcUrl, properties);
                    PreparedStatement preparedStatement = connection.prepareStatement("show hg_frontend_endpoints;" );
                    ResultSet resultSet = preparedStatement.executeQuery();
                    while (resultSet.next()) {
                        ResultSetMetaData rsmd = resultSet.getMetaData();
                        int columnCount = rsmd.getColumnCount();
                        Map 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();
                }
                x++;
            }
        }
    }

    此时观察两个实例的监控信息,可以看到两个实例的连接数基本一致,查看实例的监控信息请参见查看监控指标

    • 实例hgpostcn-cn-wxxxxxxxx04的监控信息。image..png

    • 实例hgpostcn-cn-wxxxxxxxx05的监控信息。image..png

  • 本页导读 (1)
  • 背景信息
  • 使用说明
  • 前提条件
  • 命令格式
  • 参数说明
  • 使用示例
AI助理

点击开启售前

在线咨询服务

你好,我是AI助理

可以解答问题、推荐解决方案等