使用JDBC和MySQL解决“通信链路故障”问题

278

我正在尝试连接到本地的MySQL服务器,但是一直出现错误。

这是代码。

public class Connect {

    public static void main(String[] args) {
        Connection conn = null;

        try {
            String userName = "myUsername";
            String password = "myPassword";

            String url = "jdbc:mysql://localhost:3306/myDatabaseName";
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            conn = DriverManager.getConnection(url, userName, password);
            System.out.println("Database connection established");
        } catch (Exception e) {
            System.err.println("Cannot connect to database server");
            System.err.println(e.getMessage());
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                    System.out.println("Database Connection Terminated");
                } catch (Exception e) {}
            }
        }
    }
}

以及错误信息:

Cannot connect to database server
Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1116)
        at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:344)
        at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2333)
        at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2370)
        at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2154)
        at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:792)
        at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:47)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:381)
        at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:305)
        at java.sql.DriverManager.getConnection(DriverManager.java:582)
        at java.sql.DriverManager.getConnection(DriverManager.java:185)
        at Connect.main(Connect.java:16)
    Caused by: java.net.ConnectException: Connection refused
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
        at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
        at java.net.Socket.connect(Socket.java:529)
        at java.net.Socket.connect(Socket.java:478)
        at java.net.Socket.<init>(Socket.java:375)
        at java.net.Socket.<init>(Socket.java:218)
        at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:257)
        at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:294)
        ... 15 more
我已设置了类路径,确保 my.cnf 文件中的跳过网络选项被注释掉。
Java 版本为 1.2.0_26(64 位),MySQL 版本为 5.5.14,MySQL 连接器版本为 5.1.17。
我确保用户可以访问我的数据库。

7
注意底部的CausedBy。SQL服务器从未接受该连接。如果从命令行执行telnet localhost 3306会发生什么?MySQL服务器正在运行吗? - Jim Garrison
2
问题已解决,我在my.cnf中添加了一个bind-address条目。 - Anthony
4
@Anthony,您应该将您的评论作为答案并接受它... - Fildor
2
我尝试进入控制面板中的Windows服务,启动mysql服务,然后它就可以工作了...尝试这个解决方案。 - tinku
我遇到了同样的问题。我改变了端口号,这对我有用。它也会对像我这样的人有用。 - Aravin
显示剩余6条评论
25个回答

496

我在两个程序中遇到了同样的问题。我的错误是这样的:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.

我花了好几天时间来解决这个问题。我尝试了很多不同网站上提到的方法,但是都没有起作用。最后我改变了代码,并找出了问题所在。我会试着告诉你一些不同的方法,并把它们总结在这里。

当我在网上寻找解决方案时,我发现有许多解决方案至少对一些人有效,但其他人说它对他们无效!为什么会有这么多种方法解决这个错误呢?
看起来当连接服务器出现问题时,这个错误通常会发生。也许问题是由于错误的查询字符串或太多连接到数据库造成的。
因此,我建议您逐个尝试所有解决方案,不要放弃!

以下是我在互联网上找到的解决方案,每个解决方案至少有一个人用它解决了他的问题:

提示:对于那些需要更改MySQL设置的解决方案,您可以参考以下文件:

  • Linux:/etc/mysql/my.cnf/etc/my.cnf(取决于Linux发行版和使用的MySQL软件包)

  • Windows:C:\ProgramData\MySQL\MySQL Server 5.6\my.ini(请注意,这是ProgramData,而不是Program Files)

以下是解决方案:

  • 更改bind-address属性:

    取消注释或将bind-address属性更改为以下IP之一:

 bind-address="127.0.0.1"
或者
 bind-address="0.0.0.0"
  • 注释掉"skip-networking"

    如果您的MySQL配置文件中有skip-networking行,请在该行开头添加#符号进行注释。

  • 更改"wait_timeout"和"interactive_timeout"

    在MySQL配置文件中添加以下内容:

    [wait_timeout][1] = *number*
    
    interactive_timeout = *number*
    
    connect_timeout = *number*
    
  • 确保Java不会将“localhost”转换为[:::1]而不是[127.0.0.1]

    由于MySQL识别127.0.0.1IPv4),但不识别:::1IPv6

    可以通过以下两种方法之一来避免这种情况:

    1. 在连接字符串中使用127.0.0.1而不是localhost,以避免localhost被转换为:::1

    2. 使用选项-Djava.net.preferIPv4Stack=true运行Java,强制Java使用IPv4而不是IPv6。在Linux上,这也可以通过运行(或将其放置在/etc/profile中)来实现:

       export _JAVA_OPTIONS="-Djava.net.preferIPv4Stack=true"
      
  • 检查操作系统代理设置、防火墙和反病毒程序

    确保防火墙或反病毒软件未阻止 MySQL 服务。

    在 Linux 上暂时停止 iptables。如果 iptables 配置不正确,它们可能允许将 tcp 数据包发送到 mysql 端口,但阻止从同一连接返回的 tcp 数据包。

  •  # Redhat enterprise and CentOS
     systemctl stop iptables.service
     # Other linux distros
     service iptables stop
    

    停止Windows上的防病毒软件。

    更改连接字符串。检查您的查询字符串。您的连接字符串应该像这样:

    dbName = "my_database";
    dbUserName = "root";
    dbPassword = "";
    String connectionString = "jdbc:mysql://localhost/" + dbName + "?user=" + dbUserName + "&password=" + dbPassword + "&useUnicode=true&characterEncoding=UTF-8";
    
    确保字符串中没有空格。所有连接字符串都应连续,没有任何空格字符。 尝试用回送地址 127.0.0.1 替换“localhost”。 还可以尝试将端口号添加到连接字符串中,例如:
    String connectionString = "jdbc:mysql://localhost:3306/my_database?user=root&password=Pass&useUnicode=true&characterEncoding=UTF-8";
    

    一般MySQL的默认端口是3306。

    不要忘记将用户名和密码更改为您的MySQL服务器的用户名和密码。

    • 更新JDK驱动程序库文件
    • 测试不同的JDK和JRE(如JDK 6和7)
    • 不要更改max_allowed_packet

    "max_allowed_packet"是MySQL配置文件中的一个变量,表示最大数据包大小,而不是最大数据包数量。因此,它无法解决此错误。

    • 更改Tomcat安全性设置

    将TOMCAT6_SECURITY=yes更改为TOMCAT6_SECURITY=no

    • 使用validationQuery属性

    使用validationQuery="select now()"确保每个查询都有响应

    • AutoReconnect

    将此代码添加到连接字符串中:

    &autoReconnect=true&failOverReadOnly=false&maxReconnects=10
    

    虽然这些解决方案都不适用于我,但我建议你尝试一下。因为有些人通过遵循这些步骤解决了他们的问题。

    但是什么解决了我的问题呢?

    我的问题是我在数据库上有很多SELECT。每次我都会创建一个连接,然后关闭它。尽管每次我都关闭连接,但系统面临了众多连接并给我带来了那个错误。 我所做的就是将我的连接变量定义为整个类的公共(或私有)变量,并在构造函数中进行初始化。 然后每次我只使用那个连接。它解决了我的问题,也大大提高了我的速度。

    #结论# 没有简单和独特的方法来解决这个问题。我建议您考虑自己的情况并选择上述解决方案。如果您在程序开始时遇到此错误,并且根本无法连接到数据库,则可能存在连接字符串问题。但是,如果您在与数据库进行了多次成功的交互之后遇到此错误,则问题可能是连接数过多,您可以考虑更改“wait_timeout”和其他MySQL设置或重写代码以减少连接数。


    3
    我只添加了wait_timeout参数,参考了http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_wait_timeout。 - Siddharth
    2
    @Soheil,你能加上“暂时禁用iptables”吗?这就是我的问题所在。iptables配置错误,允许TCP数据包发送到MySQL端口,但阻止了从同一连接返回的TCP数据包。 - Basil Musa
    1
    @Soheil,感谢您提供的解决方案 - 对我来说,在MySQL配置中更改wait_timeout参数就解决了问题!初始值仅设置为“300”! - thorinkor
    1
    使用正确的事务管理器后,速度真的很快。谢谢。 - Markus Barthlen
    30
    对我来说,是在URL的末尾添加useSSL=false&allowPublicKeyRetrieval=true,即jdbc:mysql://localhost:3306/cloudapp?useSSL=false&allowPublicKeyRetrieval=true - Freeman
    显示剩余14条评论

    18
    如果你使用的是MAMP PRO,解决方法很简单,我真的希望在开始搜索互联网来解决这个问题之前能注意到它。 只需要从MAMP MySQL选项卡中点击“允许MySQL的网络访问”即可。
    就是这样简单。
    哦,如果你是MAMP用户,仅点击这个框可能就可以解决你的问题,但你可能还需要像上面的帖子所述,将绑定地址更改为0.0.0.0或127.0.0.1。

    这节省了我很多时间。当我知道这只是一个一行解决方案时,我感觉自己快要到达沸点了。 - Shantanu
    对我来说,端口与 MAMP 在启动页面上所说的完全不同。进入 MAMP 首选项并检查端口选项卡以查看您实际的 MySQL 端口。 - Strainy
    我的应用程序能在Windows和Ubuntu上运行,但不能在我安装了MAMP PRO的Mac上运行。这也是我让我的应用程序在Mac上运行的方法。 - kimbaudi

    9

    bind-address设置为服务器的网络IP而不是默认的本地主机,以及对我的用户设置权限,对我很有效。

    我的my.cnf:

    bind-address = 192.168.123.456
    

    MySQL控制台:

    GRANT ALL PRIVILEGES ON dbname.* to username@'%' IDENTIFIED BY 'password';
    

    1
    或者使用0.0.0.0作为所有接口的地址... - Eric Kramer
    答案对我有用,你能描述一下上面的mysql指令中@'%'是什么意思吗? - alex
    bind-address = 0.0.0.0 对我有效。 - Daddy32

    6

    如果您遇到一组 Docker 容器 的问题,请确保不仅仅使用 EXPOSE 命令暴露端口号 3306,同时也需要将容器外部的端口映射到内部 -p 3306:3306。对于 docker-compose.yml 文件:

    version: '2'
    
    services:
        mdb:
            image: mariadb:10.1
            ports:
                - "3306:3306"
            
    

    3
    相关地,我尝试使用本地主机、127.0.0.1、0.0.0.0或固定 IP 进行连接,但需要使用服务名称进行连接(这里是:jdbc:mysql://mdb:3306/...)。 - chris.currin
    5
    就像@chris.currin提到的那样,从另一个docker容器访问时提供“container_name”非常重要。例如:“jdbc:mysql://mysql_container:3306/db_name?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true” - prayagupa
    1
    我在两个不同的容器中使用Tomcat和MySQL时遇到了相同的错误。使用jdbc:mysql://mysql_container:3306/db_name后,问题得到解决。 - dheeraj kumar

    5

    在我的情况下(我是个新手),我正在测试连接MySQL数据库的Servlet,其中一个异常就是上面提到的异常。

    这让我有些头晕,但我意识到这是因为我还没有在localhost上启动我的MySQL服务器
    启动服务器后,问题得到解决。

    所以,检查MySQL服务器是否正常运行


    5
    在我的情况下,
    1. 更改远程机器的mysql配置文件,在/etc/mysql/my.cnf中更改 bind-address = 127.0.0.1#bind-address = 127.0.0.1

    2. 在远程机器上,使用以下命令更改mysql用户权限: GRANT ALL PRIVILEGES ON *.* TO 'user'@'%' IDENTIFIED BY 'password';

    3. 重要提示:在远程机器上重新启动mysql:sudo /etc/init.d/mysql restart


    在Ubuntu/Debian中安装MySQL之后,设置过程是安全的并默认阻止任何外部访问。为了从任何远程机器访问MySQL,总是需要这些步骤。 - Luis Crespo

    5

    我刚刚也遇到了同样的问题。 这是因为MySQL守护程序绑定到了机器的IP,这是为了与具有连接权限的用户连接@your_machine所必需的。 在这种情况下,用户应该有CONNECT USER_NAME@MACHINE_NAME_OR_IP的权限。

    我想远程访问我的计算机,所以我在my.cnf中进行了更改:

    bind-address = MY_IP_ADDRESS
    

    To

    bind-address = 0.0.0.0
    

    这将允许来自localhost和外部(在我的情况下)的用户连接到实例。 如果将MySQL绑定到0.0.0.0,则以下两个权限都可以使用:

    USER_NAME@MACHINE_NAME_OR_IP
    
    USER_NAME@localhost
    

    3
    如上述详细回答所述,这个错误可能由很多原因引起。
    我也遇到过这个问题。我的环境是Mac OSX 10.8,在一个使用Vagrant管理的Ubuntu 12.04虚拟机中使用MySQL 5.5.34。
    我已经在Vagrant配置文件中正确设置了端口转发。我可以从我的Mac和虚拟机内部telnet到MySQL实例。所以我知道MySQL守护进程正在运行并可达。但当我尝试通过JDBC连接时,我收到了“通信链路故障”的错误。
    在我的情况下,问题是通过编辑/etc/mysql/my.cnf文件解决的。具体来说,我注释掉了“# bind-address = 127.0.0.1”行。

    你在哪里修改的?是在主机上还是在 Vagrant 的 MySql 上? - Joe
    我在运行MySQL的客户VM中进行了配置更改。我不想直接在我的Mac上运行MySQL,所以我使用了一个VM。 - devdanke

    3
    在我的情况下,是由于长时间未使用而导致服务器断开连接的空闲超时。连接保持打开状态,但未被使用。然后进行客户端重新启动即可解决问题,我认为重新连接也可以解决问题。
    一个不错的解决方案是定期从守护进程/服务对连接进行ping操作。

    2

    我刚刚重新启动了MySQL(根据此处的提示:https://stackoverflow.com/a/14238800),问题得到了解决。

    我在MacOS(10.10.2)上安装了通过homebrew安装的MySQL(5.6.21),遇到了同样的问题。

    让人困惑的是,我的一个应用程序可以正常连接到数据库,而另一个则不行。

    在尝试了该问题被接受的答案中建议的修复com.mysql.jdbc.CommunicationsException异常的许多方法后,我很惊讶重新启动MySQL有效。

    我遇到的问题可能是如上述链接中所建议的那样:

    您正在使用连接池吗?如果是,则尝试重新启动服务器。 可能你的一些连接处于关闭状态。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接