我已经创建了一个mariadb集群,并且我试图让Java应用程序能够在其中一个主机死亡时故障转移到另一个主机。
我创建了一个与"jdbc:mysql:sequential://host1,host2,host3/database?socketTimeout=2000&autoReconnect=true"
建立连接的应用程序。应用程序每秒在循环中进行一次查询。如果我杀死应用程序当前正在执行查询的节点(Statement.executeQuery()),由于超时,我得到一个SQLException。我可以捕获异常并重新执行该语句,我看到请求被发送到另一台服务器,因此在这种情况下故障转移正常。但是我期待executeQuery()不会抛出异常并静默地重试另一台服务器。
假设我不应该处理异常并明确重试查询,我错了吗?还有什么我需要配置才能实现这一点吗?
答案 0 :(得分:2)
由于以下原因,自动重新连接很危险。假设你有这段代码:
BEGIN;
SELECT ... FROM tbl WHERE ... FOR UPDATE;
(line 3)
UPDATE tbl ... WHERE ...;
COMMIT;
现在让我们说服务器崩溃(第3行)。该事务将被回滚。在我的伪造的例子中,这只涉及释放tbl上的锁。
现在让我们说一些其他连接在您自动重新连接时在相同行上成功执行相同的事务。
现在,通过自动重新连接,第一个线程忘记了事务的前半部分已回滚,并根据现在已过期的数据继续执行UPDATE。
您需要获得一个例外,以便您可以返回到BEGIN,以便您可以“交易安全”。
无论如何你需要这个 - 使用Galera和 no 崩溃,可能会发生类似的事情。两个线程在相同的时间在两个不同的节点上执行该事务...每个都成功直到它进入COMMIT,此时Galera魔术发生并且其中一个COMMIT被告知失败。 “正确”响应将重播选择失败的服务器上的整个事务。
请注意,Galera与非Galera不同,需要检查COMMIT
上的错误。
更多Galera tips(针对从非Galera迁移的开发者和dbas)
答案 1 :(得分:0)
故障转移并不意味着应用程序不必处理异常。
当连接丢失时,驱动程序将尝试重新连接到另一台服务器。
如果驱动程序无法重新连接到另一台服务器,将抛出SQLNonTransientConnectionException,池将自动丢弃这些连接。
如果连接恢复,有一些边缘情况重新启动查询是安全的:当查询不在事务中,并且连接当前在read-only mode时(使用Spring @Transactional(readOnly = false))例如。对于这种情况,MariaDb java连接将自动重新启动查询。在这些特定情况下,不会抛出异常,故障转移是透明的。
在交易过程中,驱动程序无法重新执行当前查询 即使没有事务,如果查询是UPDATE命令,驱动程序也无法知道数据库服务器是否已收到最后一个请求并执行。
然后驱动程序将发送一个SQLException(SQLState以“25”= INVALID_TRANSACTION_STATE开头),由应用程序来处理这些情况。