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 - 慢查询的主要内容,如果未能解决你的问题,请参考以下文章