MySQL 出现 java.sql.SQLException 之 Got error 28 from storage engine 问题分析以及解决方法

来源:互联网 发布:格伦罗宾逊数据 编辑:程序博客网 时间:2024/05/21 09:07

1 发现问题

一直好好的 MySQL 数据库,今天突然报错了,是用户在查询数据时发现的(web 应用)。赶紧看看应用日志:

Caused by: java.sql.SQLException: Got error 28 from storage engine    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1078)    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4237)    at com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2190)    at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:2046)    at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3543)    at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:491)    at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3245)    at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2413)    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2836)    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2825)    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2156)    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2323)    at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeQuery(FilterChainImpl.java:2712)    at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_executeQuery(FilterEventAdapter.java:465)    at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeQuery(FilterChainImpl.java:2709)    at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_executeQuery(FilterEventAdapter.java:465)    at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeQuery(FilterChainImpl.java:2709)    at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.executeQuery(PreparedStatementProxyImpl.java:132)    at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeQuery(DruidPooledPreparedStatement.java:227)    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:56)    ... 139 more

2 原因分析

2.1 分析源代码

根据出错日志的堆栈信息,可以看出是 MySQL 在验证操作系统返回的响应包时报的错(MysqlIO.checkErrorPacket) ,打开源代码跟踪看看:

private void checkErrorPacket(Buffer resultPacket) throws SQLException {    int statusCode = resultPacket.readByte();    // Error handling    if (statusCode == (byte) 0xff) {        String serverErrorMessage;        int errno = 2000;        if (this.protocolVersion > 9) {            errno = resultPacket.readInt();            String xOpen = null;            serverErrorMessage = resultPacket.readString(this.connection.getErrorMessageEncoding(), getExceptionInterceptor());            if (serverErrorMessage.charAt(0) == '#') {                // we have an SQLState                if (serverErrorMessage.length() > 6) {                    xOpen = serverErrorMessage.substring(1, 6);                    serverErrorMessage = serverErrorMessage.substring(6);                    if (xOpen.equals("HY000")) {                        xOpen = SQLError.mysqlToSqlState(errno, this.connection.getUseSqlStateCodes());                    }                } else {                    xOpen = SQLError.mysqlToSqlState(errno, this.connection.getUseSqlStateCodes());                }            } else {                xOpen = SQLError.mysqlToSqlState(errno, this.connection.getUseSqlStateCodes());            }            clearInputStream();            StringBuilder errorBuf = new StringBuilder();            String xOpenErrorMessage = SQLError.get(xOpen);            if (!this.connection.getUseOnlyServerErrorMessages()) {                if (xOpenErrorMessage != null) {                    errorBuf.append(xOpenErrorMessage);                    errorBuf.append(Messages.getString("MysqlIO.68"));                }            }            errorBuf.append(serverErrorMessage);            if (!this.connection.getUseOnlyServerErrorMessages()) {                if (xOpenErrorMessage != null) {                    errorBuf.append("\"");                }            }            appendDeadlockStatusInformation(xOpen, errorBuf);            if (xOpen != null && xOpen.startsWith("22")) {                throw new MysqlDataTruncation(errorBuf.toString(), 0, true, false, 0, 0, errno);            }            throw SQLError.createSQLException(errorBuf.toString(), xOpen, errno, false, getExceptionInterceptor(), this.connection);        }        serverErrorMessage = resultPacket.readString(this.connection.getErrorMessageEncoding(), getExceptionInterceptor());        clearInputStream();        if (serverErrorMessage.indexOf(Messages.getString("MysqlIO.70")) != -1) {            throw SQLError.createSQLException(SQLError.get(SQLError.SQL_STATE_COLUMN_NOT_FOUND) + ", " + serverErrorMessage,                    SQLError.SQL_STATE_COLUMN_NOT_FOUND, -1, false, getExceptionInterceptor(), this.connection);        }        StringBuilder errorBuf = new StringBuilder(Messages.getString("MysqlIO.72"));        errorBuf.append(serverErrorMessage);        errorBuf.append("\"");        throw SQLError.createSQLException(SQLError.get(SQLError.SQL_STATE_GENERAL_ERROR) + ", " + errorBuf.toString(), SQLError.SQL_STATE_GENERAL_ERROR, -1,                false, getExceptionInterceptor(), this.connection);    }}

这些代码都是为了对操作系统返回的错误代码信息,进行转义,转换为 MySQL 自己的错误信息。所以 SQLError 一定就是 MySQL 的错误信息字典咯!打开一看,果然是这样:

mysqlToSqlState = new Hashtable<Integer, String>();mysqlToSqlState.put(MysqlErrorNumbers.ER_SELECT_REDUCED, SQL_STATE_WARNING);mysqlToSqlState.put(MysqlErrorNumbers.ER_WARN_TOO_FEW_RECORDS, SQL_STATE_WARNING);...mysqlToSqlState.put(MysqlErrorNumbers.ER_LOCK_DEADLOCK, SQL_STATE_ROLLBACK_SERIALIZATION_FAILURE);

MysqlErrorNumbers 是 MySQL 定义的错误代码常量类,里面可以找到 Got error 28 from storage engine 定义的出处:

public final static int ER_GET_ERRNO = 1030; //SQLSTATE: HY000 Message: Got error %d from storage engine...

有的人会发现这个 ER_GET_ERRNO 根本就没有被其他代码调用过!个人猜测是因为 MySQL 的 jar 源代码与实际的 jar 版本不一致造成的。

2.2 神秘的 “28”

那么 “28” 究竟又是什么意思呢?

在 MySQL 官网有这样的一个问题解答,翻译如下:无法创建或者写数据到文件怎么办?

如果在执行 SQL 查询时,会得到下列错误,那就意味着 MySQL 无法在临时目录上创建临时文件(数据的结果集需要用到临时文件)。

Can't create/write to file '\\sqla3fe_0.ism'.

windows 经常会出现这样的提示,Unix 的提示与 windows 类似。

修复方法是使用 mysqld 方法,重新指定一个有权限文件夹,像这样:

[mysqld]tmpdir=C:/temp

C:/temp 必须存在并且有足够的磁盘空间供 MySQL 使用。

没有权限也会引发这个问题,所以必须确保 MySQL 有权限写 tmpdir 文件夹。

也可以使用 perror 命令来确定错误代码的含义。还有一个原因是磁盘空间满了:

shell> perror 28OS error code  28:  No space left on device

看到了没有,这就是 28 的真正含义!

查了一下 linux 的错误码中 28 的含义,也是 No space left on device!

现在清楚了吧 O(∩_∩)O~

3 解决问题

linux 中先查询磁盘空间使用量:

myServer# df -h

肯定有一个文件系统是 100%,可能像这样:

Filesystem    Size    Used   Avail Capacity  Mounted on/dev/vdisk     13G     13G     46M   100%    /devfs         1.0k    1.0k      0B   100%    /dev

剩下的事情就简单咯,删除无用文件;把大文件移动到其他文件系统;甚至在 MySQL 中重新指定临时文件目录的路径…这些方法目的都是为了把磁盘空间给腾出来 O(∩_∩)O~

阅读全文
0 0
原创粉丝点击