J2EE - PostgreSQL - JDBC - 慢查询

Posted

技术标签:

【中文标题】J2EE - PostgreSQL - JDBC - 慢查询【英文标题】:J2EE - PostgreSQL - JDBC - Slow query 【发布时间】:2014-02-11 17:08:43 【问题描述】:

我在使用 PostgreSQL 数据库和 JDBC 驱动程序时遇到了一个奇怪的行为(在 J2EE 应用程序中)。

为了简单起见,假设我有两张桌子:

SHIP_MESSAGE(id, datetime, latitude, longitude)
SUBMARINE_MESSAGE(id, datetime, latitude, longitude, immersion)

它们中的每一个都包含在给定日期时间发出的消息。现在我必须在网站上重播这些信息。

我使用 AJAX 大约每 300-500 毫秒请求一次信息。请求类似于: select id, datetime, latitude, longitude from SHIP_MESSAGE where date >= '2013-02-11 18:00:00' order by date limit 1;

如果我执行一次(从第一个表中获取消息),大约需要 150 毫秒,这很好。但是如果我执行两次(从两个表中获取消息),每个查询需要 800 到 2000 毫秒!

所以这很好用:

MessageDAO dao = new MessageDAO(); // Data access object, used to execute my query

Date d1 = new Date();
ShipMessage message = dao.getShipMessageAtDate(date);
Date d2 = new Date();

System.out.println( d2.getTime() - d1.getTime() ); // Around 150 ms

这不是:

MessageDAO dao = new MessageDAO();

Date d1 = new Date();
NavigationMessage message = dao.getShipMessageAtDate(date);
Date d2 = new Date();
SubmarineMessage m2 = dao.getSubmarinMessageAtDate(date);
Date d3 = new Date();

System.out.println( d2.getTime() - d1.getTime() ); // Between 800 and 2000 ms
System.out.println( d3.getTime() - d2.getTime() ); // Between 800 and 2000 ms

我正在使用单例模式来建立连接。如果我创建两个 Connection 对象,它可以正常工作(~ 150 ms),但我不想这样做,因为当客户端太多时我将无法打开足够的连接。

有什么想法吗?

我尝试使用连接来让一个请求包含两条消息的数据,但它也太长(1-3 秒),这很奇怪,因为如果我直接在终端中执行它会很快。

谢谢!

编辑:

这里是 MessageDAO 的代码:

public class MessageDAO extends DAO 

    public MessageDAO()  super(); 
    public MessageDAO(Connection connection)  super(connection); 

    public NavSensorsMessage getShipMessageDate(Date date) throws SQLException 

        String sql = "select * from BOAT_MESSAGE where date >= ? order by date limit 1;";

        PreparedStatement ps = _connection.prepareStatement(sql);
        ps.setTimestamp(1, new java.sql.Timestamp(date.getTime()));

        ResultSet result = ps.executeQuery();
        result.next();

        Date datetime = result.getDate("datetime");
        float latitude = result.getFloat("latitude");
        float longitude = result.getFloat("longitude");

        return new ShipMessage(datetime, latitude, longitude);
    

这是 DAO 类:

public abstract class DAO 


    // -------------------------------------------------------------------------
    protected Connection _connection;
    // -------------------------------------------------------------------------



    // -------------------------------------------------------------------------
    // Constructors :
    public DAO()  _connection = StaticPostgreSQLConnection.getInstance(); 
    public DAO(Connection connection)  _connection = connection; 
    // -------------------------------------------------------------------------

这里是 StaticPostgreSQLConnection :

public class StaticPostgreSQLConnection 


    // -------------------------------------------------------------------------
    private static final String _driverName = "org.postgresql.Driver";
    private static final String _url = "jdbc:postgresql://localhost:5432/telesciences";
    private static final String _user = "mylogin";
    private static final String _password = "mypassword";
    private static Connection _connection;
    // -------------------------------------------------------------------------



    // -------------------------------------------------------------------------
    public static Connection getInstance() 

        if (_connection == null) 
            try 
                Class.forName(_driverName);
                _connection = DriverManager.getConnection(_url, _user, _password);
            
            catch (ClassNotFoundException | SQLException e)  e.printStackTrace(System.err); 
        
        return _connection;
    
    // -------------------------------------------------------------------------



    // -------------------------------------------------------------------------
    public static void close() 

        try 
            _connection.close();
            _connection = null;
        
        catch (SQLException e)  e.printStackTrace(System.err); 
    
    // -------------------------------------------------------------------------



    // -------------------------------------------------------------------------
    public static void begin() throws SQLException  getInstance().createStatement().execute("BEGIN;"); 
    public static void commit() throws SQLException  getInstance().createStatement().execute("COMMIT;"); 
    public static void rollback() throws SQLException  getInstance().createStatement().execute("ROLLBACK;"); 
    // -------------------------------------------------------------------------

编辑 2:

这是一段 postgres 日志:

2014-02-12 11:02:02 CET LOG:  durée : 0.122 ms, analyse <unnamed> : select * from NAVIGATION_MESSAGE where date >= $1 order by date limit 1
2014-02-12 11:02:02 CET LOG:  durée : 0.143 ms, lien <unnamed> : select * from NAVIGATION_MESSAGE where date >= $1 order by date limit 1
2014-02-12 11:02:02 CET DÉTAIL:  paramètres : $1 = '2011-07-02 01:08:05.16'
2014-02-12 11:02:02 CET LOG:  exécute <unnamed>: select * from NAVIGATION_MESSAGE where date >= $1 order by date limit 1
2014-02-12 11:02:02 CET DÉTAIL:  paramètres : $1 = '2011-07-02 01:08:05.16'
2014-02-12 11:02:02 CET LOG:  durée : 157.295 ms

2014-02-12 11:02:02 CET LOG:  durée : 0.114 ms, analyse <unnamed> : select * from NAVSENSORS_MESSAGE where date >= $1 order by date limit 1
2014-02-12 11:02:02 CET LOG:  durée : 0.161 ms, lien <unnamed> : select * from NAVSENSORS_MESSAGE where date >= $1 order by date limit 1
2014-02-12 11:02:02 CET DÉTAIL:  paramètres : $1 = '2011-07-02 01:08:04.88'
2014-02-12 11:02:02 CET LOG:  exécute <unnamed>: select * from NAVSENSORS_MESSAGE where date >= $1 order by date limit 1
2014-02-12 11:02:02 CET DÉTAIL:  paramètres : $1 = '2011-07-02 01:08:04.88'
2014-02-12 11:02:02 CET LOG:  durée : 157.598 ms

每个请求只需要 150 毫秒,所以问题不在于 postgres 本身。我想这来自于我建立关系的方式。

【问题讨论】:

你能展示 MessageDao 获取方法的实现,尤其是获取连接吗? 启用详细的 PostgreSQL 查询日志记录 - 至少 log_statement = 'all'log_min_duration_statement = 0。然后报告 PostgreSQL 说语句在 PostgreSQL 日志中运行的时间。这将帮助您隔离延迟的位置。还可以考虑启用auto_explain,并将从 JDBC 运行时生成的计划与手动运行时生成的计划进行比较。 你忘了包括基础知识。请显示 exact PgJDBC 版本和 exact PostgreSQL 版本 (select version())。 感谢您的回答。我将编辑原始消息以添加 MessageDAO 代码。版本是:PostgreSQL:i686-pc-linux-gnu 上的 9.2.4,由 gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 编译,32 位。 JDBC:9.2-1003。我会尝试记录方法。 我用一段日志更新了我的原始消息。每个请求需要 150ms,所以 Postgres 不是问题,它必须来自我(或 JDBC)使用连接的方式。任何的想法 ?我将 Postgress 更新为 9.2.6,将 JDBC 更新为 9.3-1100.jdbc41,没有变化。 【参考方案1】:

解决了! 我做了两件事:

我更改了获取连接的方式,使用 Tomcat 的数据源。这让我可以在 300 毫秒内执行每个请求(因此,在执行连续请求时不再有延迟)

我在我的表的日期字段上创建了一个索引,这将每个请求的持续时间从 150 毫秒减少到... 0-2 毫秒!

【讨论】:

以上是关于J2EE - PostgreSQL - JDBC - 慢查询的主要内容,如果未能解决你的问题,请参考以下文章

J2EE的13个规范之 JDBC 及其使用

J2EE的13种规范

J2ee的13个规范

J2EE的13种核心技术

无法使用 Postgresql 将 JDBC 连接到 sonarqube

Android上的Postgresql JDBC连接错误