java - 优化大表上的 MySQL 查询

标签 java mysql jdbc

我将 mysql 与 JDBC 结合使用。

我有一个大型示例表,其中包含 630 万行,我正在尝试对其执行高效的选择查询。见下文: enter image description here

我在表上创建了三个额外的索引,如下所示: enter image description here

像这样执行SELECT查询SELECT latitude, longitude FROM 3dag WHERE timestamp BETWEEN "+startTime+"AND "+endTime+"AND HourOfDay=4 AND DayOfWeek=3" 的运行时间非常高,达到 256356 毫秒,或略高于四分钟。我对同一查询的解释给出了我这个: enter image description here

我检索数据的代码如下:

    Connection con = null;
    PreparedStatement pst = null;
    Statement stmt = null;
    ResultSet rs = null;

    String url = "jdbc:mysql://xxx.xxx.xxx.xx:3306/testdb";
    String user = "bigd";
    String password = "XXXXX";

    try {
        Class.forName("com.mysql.jdbc.Driver");
        con = DriverManager.getConnection(url, user, password);
        String query = "SELECT latitude, longitude FROM 3dag WHERE timestamp BETWEEN "+startTime+" AND "+endTime+" AND HourOfDay=4 AND DayOfWeek=3";
        stmt = con.prepareStatement("SELECT latitude, longitude FROM 3dag WHERE timestamp>=" + startTime + " AND timestamp<=" + endTime);
        stmt = con.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
        stmt.setFetchSize(Integer.MIN_VALUE);
        rs = stmt.executeQuery(query);

        System.out.println("Start");
        while (rs.next()) {

            int tempLong = (int) ((Double.parseDouble(rs.getString(2))) * 100000);
            int x = (int) (maxLong * 100000) - tempLong;
            int tempLat = (int) ((Double.parseDouble(rs.getString(1))) * 100000);
            int y = (int) (maxLat * 100000) - tempLat;

            if (!(y > matrix.length) || !(y < 0) || !(x > matrix[0].length) || !(x < 0)) {
                matrix[y][x] += 1;
            }
        }
        System.out.println("End");
        JSONObject obj = convertToCRS(matrix);
        return obj;

    }catch (ClassNotFoundException ex){
        Logger lgr = Logger.getLogger(Database.class.getName());
        lgr.log(Level.SEVERE, ex.getMessage(), ex);
        return null;
    }
    catch (SQLException ex) {
        Logger lgr = Logger.getLogger(Database.class.getName());
        lgr.log(Level.SEVERE, ex.getMessage(), ex);
        return null;
    } finally {
        try {
            if (rs != null) {
                rs.close();
            }
            if (pst != null) {
                pst.close();
            }
            if (con != null) {
                con.close();
            }
        } catch (SQLException ex) {
            Logger lgr = Logger.getLogger(Database.class.getName());
            lgr.log(Level.WARNING, ex.getMessage(), ex);
            return null;
        }
    }

删除 while(rs.next()) 循环中的每一行都会给我同样可怕的运行时间。

我的问题是如何优化此类查询?我很好奇 .setFetchSize() 以及这里的最佳值应该是多少。文档显示 INTEGER.MIN_VALUE 导致逐行获取,这是否正确?

感谢任何帮助。

编辑 在时间戳、DayOfWeek 和 HourOfDay 创建新索引后,我的查询运行速度加快了 1 分钟,并解释为:

enter image description here

最佳答案

预先的一些想法:

  • 您是否确实检查了 SQL 执行时间(从 .executeQuery() 到第一行?)还是执行 + 迭代超过 630 万行?
  • 您准备了 PreparedStatement 但不使用它?!
  • 使用 PreparedStatement,将 tiemstamp、dayOfWeek、hourOfDay 作为参数传递
  • 创建一个 可以满足您的where 条件的索引。以可以消除具有最高排名字段的最多项目的方式对键进行排序。

索引可能如下所示:

CREATE INDEX stackoverflow on 3dag(hourOfDay, dayOfWeek, Timestamp);

在 MySQL 中执行 SQL - 你什么时候到达那里?

  • 尝试不使用 stmt.setFetchSize(Integer.MIN_VALUE); 这可能会产生许多不需要的网络往返。

关于java - 优化大表上的 MySQL 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34251541/

相关文章:

Java 连接到 Amazon Redshift

java - java中如何声明数组为long类型?

java - 在插入数据库之前尝试将 Arraylist<LatLng> 转换为字符串

php - 我应该将所有 mysql 查询存储在另一个 php 文件中吗?

mysql - 在 Fedora 21 上安装 RMySQL

java - 不支持游标类型/并发组合

Java 断言 - $assertionsDisabled 与 $assertionsEnabled

java - 每次输入/输出后是否需要重新创建 DataInputStream 或 DataOutputStream

mysql - 如何将同一个表中的两个值相加并存储在同一个表的计算记录中?

java - pg_hba.conf 中没有条目