翻译与学习HA-JDBC

 

1 背景

有时候,你想在你的应用程序多一点弹性,不依赖于一个单一的数据库实例。有不同的方式,使用HA-JDBC就是一个非常简单的方法,支持故障转移和负载平衡,并允许你透明地将调用路由到多个数据源。它会自动复制所有的写调用(采用主从机制)和负载平衡​​所有读调用。如果数据源被丢弃,也可以重建一个数据源时,它得到恢复。 要留意,你总是需要使用相同的用户名对所有数据源。根据这样的结构,在默认情况下,将检查每分钟是否任何禁用数据库(由于连接故障)可再次启用,将重新同步这些数据库。

HA-JDBC是一个JDBC代理,为底层的JDBC驱动程序提供轻量级,透明,故障容错,集群的能力。


以下是这几天对官网的原文进行了部分翻译,以及自己学习的一个小例子~~~渣英语请忽略- -

2 特性

l  通过JDBC支持任何数据库。

l  高容错性 - 失去一个数据库集群的一个节点,而不会导致/破坏任何交易的。

l  现场激活/去激活,允许维修/升级的数据库节点,而不会中断服务。

l  通过个别节点之间分发负载来提高性能的并发访问。

l  可运行在 Java 1.6 和 1.7并完全支持 JDBC 4.1。

l  可插拔的策略同步失败的数据库节点。

l  公开JMX管理接口,允许数据库和集群管理。

l  能够从在运行时的群集添加/减去数据库节点。

l  可以配置为自动激活失败的数据库节点在预定的离峰时间。

l  Opensource (LGPL)。

 

3 简介

HA-JDBC是一个JDBC代理,使Java应用程序通过JDBC API透明地访问数据库集群。

 

HA- JDBC拥有常用JDBC以下的优点:

高可用性

就算只有一个数据库节点处于活动状态,数据库集群下仍可用于服务请求。

故障容错

由于HA- JDBC通过JDBC API操作,它是事务感知,即使一个数据库节点故障,也不会导致破坏当前事务。

可扩展性

通过对数据库读取请求的进行负载均衡, HA- JDBC能够满足通过水平伸缩(即添加数据库节点)增加的负载。

 

运行环境要求:

- Java 1.6+

- Type IV JDBC 4.x driverfor underlying databases 

- 可选的依赖JAR文件可以从依赖页面下载。

- 每个数据库群集配置XML文件或引导程序配置。

 

4 配置

HA-JDBC通常通过XML文件配置。对过去和现在的HA-JDBC的schema定义,完整列举在XML Schemas页面上。

 

5 XML

用于在运行时加载配置文件资源的方式如下:

1. 从下列来源之一来确定潜在的参数资源名称:

1. 由一个config 属性传入,DriverManager.getConnection(String,Properties)的配置属性来指定,或由DataSource、ConnectionPoolDataSource、XADataSource的config属性来指定;

2. 在ha-jdbc.cluster-id.configuration的系统属性。

3. 使用默认值ha-jdbc-{0}.xml

2. 格式化使用群集的标识符参数化资源名称。

3. 将格式的资源名称转换为URL。如果资源不是一个URL ,搜索CLASSPATH下面的类加载器类中的资源:

    1. 使用线程上下文类加载器查找该配置文件

    2. 使用当前类类加载器加载 HA-JDBC

    3. 使用系统类加载器

 

6 databases定义   

定义数据库,HA-JDBC集群有如下语法:

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <cluster>

       <database id="..." location="..." weight="#">

         <user>...</user>

         <password>...</password>

         <property name="...">...</property>

         <!-- Additional properties -->

       </database>

    </cluster>

</ha-jdbc>

 

id

集群中这个数据库的唯一标识.

location

一般情况下,这描述了数据库的位置。对于基于驱动的集群,它指定数据库的JDBC URL。对于基于数据源的群集,这具体指定:

l  在DataSource实现的类名(从一个新的实例将被创建)。

l  在预先绑定的DataSource的JNDI名称。

weight

定义此数据库节点的负载相对权重。如果不确定,权重假定为1。详情参见平衡器部分。

user

通过HA- JDBC使用的用户名连接到数据库,用于同步和元数据缓存。此用户应该具有管理权限。这不同于您的应用程序使用的数据库用户(其中可能有CRUD或只读权限)。

password

         上面数据库用户的密码.

property

定义一组属性,其语义是依赖于集群访问模式。对于基于驱动集群,这些属性被传递给相应的调用Driver.connect(String,Properties)方法。对于基于数据源的群集,如果数据库名指定为:

l  classname, 这些属性被解释为JavaBean属性,用于初始化DataSource实例。 e.g.

<database id="db1" location="org.postgresql.ds.PGSimpleDataSource">

    <property name="serverName">server1</property>

    <property name="portNumber">5432</property>

    <property name="databaseName">database</property>

</database>

 

l  JNDIname, 构造初始上下文时,这些属性被用作JNDI环境属性。 e.g.

<database id="db1" location="java:comp/env/jdbc/db1">

<property name="java.naming.provider.url">...</property>

</database>

 

7 Dialect

群集的方言属性用于HA-JDBC适应特定的数据库供应商。 HA-JDBC包括以下数据库方言:

 

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <cluster dialect="postgresql">

       <!-- ... -->

    </cluster>

</ha-jdbc>

 

8 Balancer

当来执行自群集的读请求,HA-JDBC会使用配置的均衡策略以确定哪个数据库提供服务。每个数据库都可以定义一个权重,影响它是如何通过平衡器确定优先级。如果对于给定的数据库中没有指定的权重,它被假定为1 。

在一般情况下,具有权重为0的节点永远不会服务请求,除非它是集群中的最后一个节点。

默认情况下, HA- JDBC支持4种类型的平衡器:

simple

    请求总是发送到具有最高权重的节点。

random

    请求被发送到一个随机节点。节点的权重会影响将被选择的概率。一个节点将被选择的概率=权重/总权重。

round-robin

请求被连续地发送到每个节点。平衡器移动到下一个节点之前,权重为n的一个节点,将会获得n个请求。

load

请求被发送到具有最小负载的节点。节点的权重会影响给定节点的所计算的负载。一个节点的负载=并发请求/权重。

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <cluster balancer="simple">

       <!-- Read requests will always prefer db1 -->

       <database id="db1" location="..." weight="2"><!-- ... --></database>

       <database id="db2" location="..." weight="1"><!-- ... --></database>

    </cluster>

</ha-jdbc>

 

 

9 同步策略

激活之前,定义数据库的同步策略。一个集群可以定义多个同步的策略,然而,其中的一个必须被指定为默认的同步策略(default-sync)。默认同步策略是用于自动激活触发了同步。其余的只用作在手动激活数据库过程中。HA-JDBC默认支持以下策略。3.0版本,同步策略是通过标识单独定义的,而不是由类名。如果策略暴露任何JavaBean属性,这些都可以访问嵌套属性元素进行覆盖。

passive(被动)

啥事也不做。只读集群可使用这种策略,或者如果你已知道目标数据库已经同步了。

 

dump-restore

 执行从源数据库到目标数据库的输出/恢复。要使用此策略,使用的方言必须支持它(见Dialect.getDumpRestoreSupport())。不像其它同步的策略,这种策略可以同时同步schema和数据。

 

full

删掉目标数据库中的所有表,并从源数据库取数据进行插入。

Property

Default

Description

fetchSize

0

Controls the maximum number of rows to fetch from the source database at a time.

maxBatchSize

100

Controls the maximum number of insert/update/delete statements to execute within a batch.

 

diff

执行源表和目标表的全表扫描对比,并进行必要的插入/更新/删 除。支持以下属性:

Property

Default

Description

versionPattern

Specifies a regular expression matching the column name of a last update timestamp (i.e. version) column. If specified, a version comparison column can be used to determine whether a given row requires updating, instead of a full column scan.

fetchSize

0

Controls the maximum number of rows to fetch from the source database at a time.

maxBatchSize

100

Controls the maximum number of insert/update/delete statements to execute within a batch.

 

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

<sync id="full">

<property name="fetchSize">1000</property>

</sync>

<sync id="diff">

<property name="fetchSize">1000</property>

   <property name="versionPattern">version</property>

</sync>

<cluster default-sync="diff"><!-- ... --></cluster>

</ha-jdbc>

 

 

10 集群状态管理

状态管理模块负责存储集群中的每个数据库的工作状态,以及任何耐久性状态。如果HA-JDBC配置为分布式的,或者如果配置的状态管理是持久性的,那么在启动过程中,HA-JDBC获取其初始集群被取出的状态或者从另一台服务器取出的状态。如果没有状态能找到,则所有可访问的数据库被假定为是有效的。想要在配置启动后集群状态不生效,可以在启动的时候使用系统属性 ha-jdbc.state.clear=true。

HA- JDBC包括以下状态管理器实现:

simple

一个非持久状态管理器,存储在内存中的群集状态。

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

<state id="simple"/>

<cluster><!-- ... --></cluster>

</ha-jdbc>

 

sql

一个用作于内嵌式数据库的持久状态管理器。提供程序支持以下属性,还有操纵连接池的行为的属性。连接池属性及其默认值,在 ApacheCommons Pool documentation能找到。

Property

Default

Description

urlPattern

jdbc:h2:{1}/{0}
jdbc:hsqldb:{1}/{0}
jdbc:derby:{1}/{0};create=true

A MessageFormat pattern indicating the JDBC url of the embedded database. The pattern can accept 2 parameters:

The cluster identifier

$HOME/.ha-jdbc

user

Authentication user name for the embedded database.

password

Authentication password for the above user.

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <state id="sql">

       <property name="urlPattern">jdbc:h2:/temp/ha-jdbc/{0}</property>

    </state>

    <cluster><!-- ... --></cluster>

</ha-jdbc>

 

berkeleydb

一个BerkeleyDB database的持久状态管理器。

Property

Default

Description

locationPattern

{1}/{0}

A MessageFormat pattern indicating the base location of the embedded database. The pattern can accept 2 parameters:

The cluster identifier

$HOME/.ha-jdbc

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <state id="berkeleydb">

       <property name="locationPattern">/tmp/{0}</property>

    </state>

    <cluster><!-- ... --></cluster>

</ha-jdbc>

 

sqlite

一个SQLitedatabase的持久状态管理器。

Property

Default

Description

locationPattern

{1}/{0}

A MessageFormat pattern indicating the base location of the embedded database. The pattern can accept 2 parameters:

The cluster identifier

$HOME/.ha-jdbc

 

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <state id="sqlite">

       <property name="locationPattern">/tmp/{0}</property>

    </state>

    <cluster><!-- ... --></cluster>

</ha-jdbc>

 

 

11 持久性

在3.0版本, HA- JDBC支持对用户事务持久性等级的配置。启用时,HA-JDBC将跟踪事务,这样,发生崩溃后,在重新启动时,它可以检测并从部分的提交进行恢复(即在集群数据库中某个数据库一个事务已提交完成了,但并没在所有的数据库都完成)。耐用性持久性机制是由状态管理器配置决定的。默认情况下,HA-JDBC支持以下持久性等级:

none

不调用跟踪。这持久性等级不会检测,也不会恢复提交中的崩溃。此级别提供最佳性能,但对崩溃没有提供保障。只读数据库集群应该使用这个级别。

coarse(粗粒度)

只跟踪集群的调用,并不会跟踪每个数据库调用。这持久性等级会检测,但不会恢复提交中的崩溃。 恢复后,如果任何群集调用仍然在日志中存在,所有的从数据库需停用,然后手动重启。这个级别提供了性能和弹性之间的折衷。

fine

跟踪集群的调用,并会跟踪每个数据库调用。这持久性等级会检测,也会执行恢复提交中的崩溃。恢复后,如果任何群集调用仍然在日志中存在,在事务未完成的从数据库将被停用。虽然这个等级是最慢的,但它是从崩溃确保恢复弹性的最高等级。

 

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <cluster durability="fine">

       <!-- ... -->

    </cluster>

</ha-jdbc>

 

 

12 分布能力

Distributed capabilities

表示在这个文件中定义的数据库集群将可以被多个JVM访问。默认情况下,HA-JDBC支持以下:

jgroups

使用JGroups通道播出群集成员的变化到其他对等节点。JGroups的提供方识别以下属性:

Property

Default

Description

stack

udp-sync.xml

Defines one of the following:

Name of a system resource containing the JGroups XML configuration.

URL of the JGroups XML configuration file.

Path of the JGroups XML configuration on the local file system.

Legacy protocol stack property string.

See the JGroups wiki for assistance with customizing the protocol stack.

timeout

60000

Indicates the number of milliseconds allowed for JGroups operations.

 

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <distributable id="jgroups">

       <property name="stack">udp.xml</property>

    </distributable>

    <cluster><!-- ... --></cluster>

</ha-jdbc>

 

13 元数据缓存

HA- JDBC大量使用数据库元数据。出于性能的目的,这些信息应该尽可能被缓存。默认情况下, HA- JDBC包括以下元数据缓存选项:

none

元数据在请求同时被加载,但不缓存。

lazy

元数据在请求同时分别被加载,并对每个数据库分别进行缓存。

shared-lazy

元数据在请求同时被加载,并进行缓存。

eager

所有必要的元数据在HA-JDBC初始化过程中进行加载与对每个数据库分别缓存。

shared-eager

所有必要的元数据在HA-JDBC初始化过程中进行加载与缓存。

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <cluster meta-data-cache="shared-eager">

       <!-- ... -->

    </cluster>

</ha-jdbc>

 

14 处理JDBC语句

14.1 DatabaseReads

数据库读取(例如SELECT语句)的处理采用如下算法:

    1. 从均衡器配置中获取的下一个数据库。

    2. 对此数据库执行语句。

    3. 如果语句执行成功,将结果返回给调用者。

    4. 如果语句执行失败,我们将分析捕获的异常。

       1. 如果异常被确定为是一个失败:

             1. 关闭数据库。

             2.重复使用下一个可用的数据库。

    2.如果异常确定不是失败,异常抛回给调用者。

 

可替代地,数据库的写入可以配置为同时对master 数据库和backup 数据库进行写执行。虽然这将导致更好的性能,但是如果多个应用程序线程尝试更新相同的数据库行会导致死锁。如果您的使用情况与此限制兼容,可以通过transaction-mode并行属性进行启用。

 

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <cluster transaction-mode="parallel">

       <!-- ... -->

    </cluster>

</ha-jdbc>

 

14.2 Database Writes

默认情况下,数据库写入(例如INSERT / UPDATE / DELETE语句)的处理方式使用以下算法:

    1. 在主数据库中执行语句。

    2. 如果语句执行失败和异常被确定为是失败的:

        1. 停用主数据库。

        2. 使用另一个新的主数据库。

    3. 否则,如果语句执行成功或者是异常未被确定为是一个失败。

        1. 并行执行语句在backup 数据库。

       2. 主数据库中的结果与备份数据库的结果进行比较。       

       3. 如果备份数据库中的结果与主数据库中的结果不匹配,则停用备份数据库。

 

15 处理失败

要确定一个给定的异常是否数据库故障,请咨询我们的配置方言。默认实现返回以下内容:

Dialect.indicatesFailure(SQLException):

可以确定为故障,如果异常是java.sql.SQLNonTransientConnectionException.实例。

 

Dialect.indicatesFailure(XAException)

可以确定为故障,如果异常的错误代码是XAException.XAER_RMFAIL。

任何dialect 可以覆盖这个行为,可以通过查看供应商代码, SQL状态等知悉。

如果HA-JDBC确定一个给定的数据库已失效,数据库将被停用。使其失效的过程如下:

记录一个错误信息日志。

把这个数据库从激活的数据库群集中移除。

通过Cluster StateManager持久化新的集群状态。

如果数据库集群是分布式的,则广播该数据库失效的信息到其他服务。

数据库可通过JMX手动禁用.

你可以通过配置HA-JDBC的ailure-detect-schedule 属性来主动检测数据库故障。这个属性值使用cron表达式,来指定调度的数据库集群检测数据库故障和停用时间点。

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <!-- Failure detection will run every minute -->

    <cluster failure-detect-schedule="0 * * ? * *">

       <!-- ... -->

    </cluster>

</ha-jdbc>

 

 

16 恢复故障节点

数据库的激活(重激活)过程如下:

1. 检测目标数据库是否确实仍存活.

2. 获取锁,阻塞所有客户端对该数据库集群的写操作。

3. 使用给定的同步策略同步目标数据库与主数据库。

4. 将目标数据库添加到活动数据库集合中。

5. 通过Cluster State Manager持久化新的集群状态。       

-如果数据库集群是分布式的,则广播该数据库激活的信息到其他服务。

6. 释放步骤2获取的锁。

在一般情况下,数据库同步是一个密集的和侵入的任务。为了保持数据库的一致性,集群中的每个数据库节点被锁定为读(即写被阻塞),直到同步完成。

由于同步可能在任何地点发生,可能花费数秒钟甚至数个小时(根据数据库大小和选用的同步策略),如果你的数据库集群是在高写卷(write volume)的环境中使用,那么建议在仅在非高峰期执行数据库激活操作。

 

另外,可以通过指定 auto-activate-schedule 属性以尝试自动激活不活动的数据库。如果指定了,HA-JDBC 会尝试自动激活禁用的数据库,会依据于指定的 cron 调度时间。

e.g.

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">

    <!-- Auto-activation will run every day at 2:00 AM -->

    <cluster auto-activate-schedule="0 0 2 ? * *">

       <!-- ... -->

    </cluster>

</ha-jdbc>

 

16 使用例子

使用HSQLDB作为数据库,HA-JDBC提供集群JDBC代理。

Java代码:

public class HsqldbCluster {
  
    CountDownLatch beginSignal = new CountDownLatch(1);
    CountDownLatch logSignal = new CountDownLatch(5);
    Executor insertPool = Executors.newFixedThreadPool(5);
    Executor queryPool = Executors.newFixedThreadPool(1);
  
    private void closeStatement(Connection conn1, Statement stat) {
        try {
            if (stat != null) {
                stat.close();
            }
            if (conn1 != null) {
                conn1.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
  
    public void createTabe() {
        Connection conn = null;
        Statement stat = null;
        try {
            conn = DriverManager.getConnection("jdbc:ha-jdbc:cluster", "SA", "");
            stat = conn.createStatement();
            stat.executeUpdate("SET DATABASE TRANSACTION CONTROL MVCC");
            stat.executeUpdate("CREATE TABLE TEST_TABLE(id VARCHAR(36),name VARCHAR(100))");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeStatement(conn, stat);
            beginSignal.countDown();
        }
    }
  
    public void insert() {
        for (int i = 0; i < 5; i++) {
            insertPool.execute(insertTask);
        }
    }
  
    public void query() {
        queryPool.execute(queryTask);
    }
  
    public static void main(String[] args) {
        try {
            Class.forName("org.hsqldb.jdbcDriver");
            HsqldbCluster test = new HsqldbCluster();
            test.createTabe();
            test.insert();
            test.logSignal.await();
            test.query();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  
    // tasks
    Runnable insertTask = new Runnable() {
        public void run() {
            Connection conn1 = null;
            Statement stat = null;
            try {
                beginSignal.await(); // wait for create table
                conn1 = DriverManager.getConnection("jdbc:ha-jdbc:cluster", "SA", "");
                stat = conn1.createStatement();
                long start = System.currentTimeMillis();
                for (int i = 0; i < 200000; i++) {
                    stat.executeUpdate("INSERT INTO TEST_TABLE VALUES('测试id','测试name')");
                }
                long end = System.currentTimeMillis();
                long causeTime = end - start;
                System.out.println("1. insert data into TABLE OK! Cause time is : " + causeTime);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                closeStatement(conn1, stat);
                logSignal.countDown();
            }
        }
    };
  
    Runnable queryTask = new Runnable() {
        public void run() {
            Connection conn1 = null;
            PreparedStatement stat = null;
            try {
                beginSignal.await(); // wait for create table
                conn1 = DriverManager.getConnection("jdbc:ha-jdbc:cluster", "SA", "");
                stat = conn1.prepareStatement("SELECT COUNT(1) totalCount FROM TEST_TABLE WHERE ID = '测试id'");
                long start = System.currentTimeMillis();
                long r = 0L;
                ResultSet rs = stat.executeQuery();
                if (rs.next()) {
                    r = rs.getLong("totalCount");
                }
                long end = System.currentTimeMillis();
                long causeTime = end - start;
                System.out.println("3. query data from TABLE OK! Cause time is : " + causeTime + ". And result count is : " + r);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                closeStatement(conn1, stat);
            }
        }
    };
  }


           
System.out.println("create table success!");
       
} catch (Exception e) {
            e.printStackTrace()
;
       
} finally {
            closeStatement(conn
, stat);
           
beginSignal.countDown();
       
}
    }

   
public void insert() {
       
for (int i = 0; i < 5; i++) {
           
insertPool.execute(insertTask);
       
}
    }

   
public void query() {
       
queryPool.execute(queryTask);
   
}

   
public static void main(String[] args) {
       
try {
           
//加载HSQLDBJDBC驱动
           
Class.forName("org.hsqldb.jdbcDriver");
           
HsqldbCluster test = new HsqldbCluster();
           
test.createTabe();

            long
start = System.currentTimeMillis();

           
test.insert();

           
test.logSignal.await();
            long
end = System.currentTimeMillis();
            long
causeTime = end - start;
           
System.out.println("toltal: insert data into TABLE all OK! Cause time is : " + causeTime);

           
// test.query();

       
} catch (Exception e) {
            e.printStackTrace()
;
       
}
    }


   
// tasks

   
Runnable insertTask = new Runnable() {
       
public void run() {
            Connection conn1 =
null;
           
Statement stat = null;
            try
{
               
beginSignal.await(); // wait for create table

               
conn1 = DriverManager.getConnection("jdbc:ha-jdbc:cluster", "SA", "");

               
stat = conn1.createStatement();
                long
start = System.currentTimeMillis();
                for
(int i = 0; i < 200000; i++) {
                    stat.executeUpdate(
"INSERT INTO TEST_TABLE VALUES('测试id','测试name')");
               
}
               
long end = System.currentTimeMillis();
                long
causeTime = end - start;
               
System.out.println("1. insert data into TABLE OK! Cause time is : " + causeTime);
           
} catch (Exception e) {
                e.printStackTrace()
;
           
} finally {
                closeStatement(conn1
, stat);
               
logSignal.countDown();
           
}
        }
    }
;

   
Runnable queryTask = new Runnable() {
       
public void run() {
            Connection conn1 =
null;
           
PreparedStatement stat = null;
            try
{
               
beginSignal.await(); // wait for create table

               
conn1 = DriverManager.getConnection("jdbc:ha-jdbc:cluster", "SA", "");
               
stat = conn1.prepareStatement("SELECT COUNT(1) totalCount FROM TEST_TABLE WHERE ID = '测试id'");
                long
start = System.currentTimeMillis();

                long
r = 0L;
               
ResultSet rs = stat.executeQuery();
                if
(rs.next()) {
                    r = rs.getLong(
"totalCount");
               
}

               
long end = System.currentTimeMillis();
                long
causeTime = end - start;
               
System.out.println("3. query data from TABLE OK! Cause time is : " + causeTime + ". And result count is : " + r);
           
} catch (Exception e) {
                e.printStackTrace()
;
           
} finally {
                closeStatement(conn1
, stat);
           
}
        }
    }
;
}

 

 

 

HA-JDBC XML配置:

<ha-jdbc xmlns="urn:ha-jdbc:cluster:3.0">
   <sync
id="diff">
      <property
name="fetchSize">1000</property>
      <property
name="maxBatchSize">100</property>
   </sync>
   <cluster
default-sync="diff" balancer="load" meta-data-cache="none" dialect="net.sf.hajdbc.dialect.hsqldb.HSQLDBDialect" transaction-mode="parallel" auto-activate-schedule="0 * * ? * *"
         
failure-detect-schedule="0 * * ? * *">
      <database
id="db1" location="jdbc:hsqldb:hsql://172.16.*.*;mem:memdb1">
         <user>
SA</user>
         <password></password>
       </database>
      <database
id="db2" location="jdbc:hsqldb:hsql://172.16.*.*;mem:memdb2">
         <user>
SA</user>
         <password></password>
      </database>
   </cluster>
</ha-jdbc>

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值