PostgreSQL 使用 PreparedStatement 导致查询慢的分析

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介:

实验环境:

DB is PostgreSQL version 8.2.15 

JDK1.8

测试一

使用JDBC查询一个SQL:

复制代码
public static void test1(String url, Properties props){
        String sql = "SELECT l.src_ip, l.location_id, "
                + "SUM(l.us_bytes) as up_usage, "
                + "SUM(l.ds_bytes) as down_usage, "
                + "(SUM(l.us_bytes) + SUM(l.ds_bytes) ) as total_usage "
                + "FROM unmapped_endpoint_location_hours l "
                + "where l.org_id = 195078 "
                + "AND date_time >= '2017-04-01 00:00:00.0' AND date_time < '2017-04-08 00:00:00.0' "
                + "AND l.location_id in (2638,2640,2654 ) "
                + "GROUP BY l.src_ip, l.location_id ";
        
        Connection conn = null;
        Statement sta = null;
        try {
            System.out.println("Start query1:" );
            long s_time = System.currentTimeMillis();
            conn = DriverManager.getConnection(url, props);
            sta = conn.createStatement();
            sta.execute(sql);
            System.out.println("Using Time: " + (System.currentTimeMillis() - s_time));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (sta != null) {
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
复制代码

结果:

Start query1:
Using Time: 11519 ms

测试二

使用JDBC PreparedStatement 查询相同的SQL:

复制代码
public static void test2(String url, Properties props){
        String sql2 = "SELECT l.src_ip, l.location_id, "
                + "SUM(l.us_bytes) as up_usage, "
                + "SUM(l.ds_bytes) as down_usage, "
                + "(SUM(l.us_bytes) + SUM(l.ds_bytes) ) as total_usage "
                + "FROM unmapped_endpoint_location_hours l "
                + "where l.org_id = ? "
                + "AND date_time >= ? AND date_time < ? "
                + "AND l.location_id in (2638,2640,2654 ) "
                + "GROUP BY l.src_ip, l.location_id";
        
        Connection conn = null;
        PreparedStatement preSta = null;
        try {
            System.out.println("Start query2:");
            long s_time = System.currentTimeMillis();
            conn = DriverManager.getConnection(url, props);
            preSta = conn.prepareStatement(sql2);
            preSta.setString(1, "195078");
            preSta.setString(2, "2017-04-01 00:00:00.0");
            preSta.setString(3, "2017-04-09 00:00:00.0");
            preSta.executeQuery();
            System.out.println("Using Time: " + (System.currentTimeMillis() - s_time));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preSta != null) {
                try {
                    preSta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
复制代码

结果:

Start query2:
Using Time: 143031 ms

相同的SQL,测试二和测试一结果为什么差别这么大?

测试一的SQL没有使用PreparedStatement 方式,直接给了原始的SQL。测试二的使用了PreparedStatement ,但是在set参数的时候用的都是String。

两者查询速度相差10倍,这是不是很奇怪?

现在来做另一个实验:

测试三

使用JDBC PreparedStatement 查询相同的SQL:

复制代码
public static void test3(String url, Properties props){
        String sql2 = "SELECT l.src_ip, l.location_id, "
                + "SUM(l.us_bytes) as up_usage, "
                + "SUM(l.ds_bytes) as down_usage, "
                + "(SUM(l.us_bytes) + SUM(l.ds_bytes) ) as total_usage "
                + "FROM unmapped_endpoint_location_hours l "
                + "where l.org_id = ? "
                + "AND date_time >= ? AND date_time < ? "
                + "AND l.location_id in (2638,2640,2654 ) "
                + "GROUP BY l.src_ip, l.location_id";
        
        Connection conn = null;
        PreparedStatement preSta = null;
        try {
            System.out.println("Start query3:");
            long s_time = System.currentTimeMillis();
            conn = DriverManager.getConnection(url, props);
            preSta = conn.prepareStatement(sql2);
            
            int org_id = 195078;
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
            Date d1 = null;
            Date d2 = null;
            try {
                d1 = df.parse("2017-04-01 00:00:00");
                d2 = df.parse("2017-04-09 00:00:00");
            } catch (ParseException e1) {
                e1.printStackTrace();
            }
            preSta.setInt(1, org_id);
            preSta.setTimestamp(2, new java.sql.Timestamp(d1.getTime()));
            preSta.setTimestamp(3, new java.sql.Timestamp(d2.getTime()));
            preSta.executeQuery();
            System.out.println("Using Time: " + (System.currentTimeMillis() - s_time));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preSta != null) {
                try {
                    preSta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
复制代码

结果:

Start query3:
Using Time: 16245 ms

测试结果和测试一的结果差不多,为什么?

这次测试同样使用了PreparedStatement,但是在设置参数的时候指定了参数的类型。

explan analyze

查看explan

复制代码
dev=# explain analyze SELECT count(loc.name) AS totalNum
dev-# FROM (SELECT t.src_ip, t.location_id, t.up_usage, t.down_usage, t.total_usage
dev(#       FROM (SELECT l.src_ip, l.location_id,
dev(#                   SUM(l.us_bytes) as up_usage,
dev(#                   SUM(l.ds_bytes) as down_usage,
dev(#                   (SUM(l.us_bytes) + SUM(l.ds_bytes) ) as total_usage
dev(#             FROM unmapped_endpoint_location_hours l
dev(#             where l.org_id = 195078
dev(#                   AND date_time >= '2017-04-11 00:00:00.0' AND date_time < '2017-04-20 00:00:00.0'
dev(#                   AND l.location_id in (2638,2640)
dev(#                   GROUP BY l.src_ip, l.location_id ) t
dev(# WHERE t.total_usage > 0.0 ) m
dev-# LEFT OUTER JOIN locations loc on m.location_id = loc.id WHERE loc.org_id = 195078;
复制代码

Time: 15202.518 ms

复制代码
Prepare Expalin:

PREPARE  test(int,text,text,int) as
SELECT count(loc.name) AS totalNum
FROM (SELECT t.src_ip, t.location_id, t.up_usage, t.down_usage, t.total_usage
      FROM (SELECT l.src_ip, l.location_id,
                  SUM(l.us_bytes) as up_usage,
                  SUM(l.ds_bytes) as down_usage,
                  (SUM(l.us_bytes) + SUM(l.ds_bytes) ) as total_usage
            FROM unmapped_endpoint_location_hours l
            where l.org_id = $1
                  AND date_time >= $2 AND date_time < $3
                  AND l.location_id in (2638,2640)
                  GROUP BY l.src_ip, l.location_id ) t
WHERE t.total_usage > 0.0 ) m
LEFT OUTER JOIN locations loc on m.location_id = loc.id WHERE loc.org_id = $4;

Explain analyze EXECUTE test(195078,'2017-04-11 00:00:00.0','2017-04-20 00:00:00.0',195078);
dev=# EXECUTE test(195078,'2017-04-11 00:00:00.0','2017-04-20 00:00:00.0',195078);
复制代码

Time: 98794.544 ms

 

结论

PostgreSQL 在使用原始SQL的时候会用表中类型来查,能有效根据where条件过滤结果。

当参数都是使用String的时候,没有指定类型时,PostgreSQL没有先做类型转换,而是扫描了所有的数据,对所有的数据根据where条件过滤结果。

当查询参数指定类型的时候,PostgreSQL可以先根据where条件过滤结果。

 

相关连接:

It seems when using JDBC with prepare statement, the query will be slow in postgresql:

http://www.postgresql-archive.org/Slow-statement-when-using-JDBC-td3368379.html

http://grokbase.com/t/postgresql/pgsql-general/116t4ewawk/reusing-cached-prepared-statement-slow-after-5-executions

https://stackoverflow.com/questions/28236827/preparedstatement-very-slow-but-manual-query-quick


    本文转自阿凡卢博客园博客,原文链接:http://www.cnblogs.com/luxiaoxun/p/7131975.html,如需转载请自行联系原作者

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
2月前
|
关系型数据库 分布式数据库 数据库
PolarDB常见问题之加了索引但是查询没有使用如何解决
PolarDB是阿里云推出的下一代关系型数据库,具有高性能、高可用性和弹性伸缩能力,适用于大规模数据处理场景。本汇总囊括了PolarDB使用中用户可能遭遇的一系列常见问题及解答,旨在为数据库管理员和开发者提供全面的问题指导,确保数据库平稳运行和优化使用体验。
|
4月前
|
关系型数据库 MySQL Serverless
高顿教育:大数据抽数分析业务引入polardb mysql serverless
高顿教育通过使用polardb serverless形态进行数据汇总,然后统一进行数据同步到数仓,业务有明显高低峰期,灵活的弹性伸缩能力,大大降低了客户使用成本。
|
12天前
|
SQL 关系型数据库 数据库
SQL 42501: Postgresql查询中的权限不足错误
SQL 42501: Postgresql查询中的权限不足错误
|
25天前
|
SQL 存储 Oracle
关系型数据库查询数据的语句
本文介绍了关系型数据库中的基本SQL查询语句,包括选择所有或特定列、带条件查询、排序、分组、过滤分组、表连接、限制记录数及子查询。SQL还支持窗口函数、存储过程等高级功能,是高效管理数据库的关键。建议深入学习SQL及相应数据库系统文档。
13 2
|
2月前
|
存储 关系型数据库 MySQL
TiDB与MySQL、PostgreSQL等数据库的比较分析
【2月更文挑战第25天】本文将对TiDB、MySQL和PostgreSQL等数据库进行详细的比较分析,探讨它们各自的优势和劣势。TiDB作为一款分布式关系型数据库,在扩展性、并发性能等方面表现突出;MySQL以其易用性和成熟性受到广泛应用;PostgreSQL则在数据完整性、扩展性等方面具有优势。通过对比这些数据库的特点和适用场景,帮助企业更好地选择适合自己业务需求的数据库系统。
|
3月前
|
SQL 关系型数据库 分布式数据库
在PolarDB for PostgreSQL中,你可以使用LIKE运算符来实现类似的查询功能,而不是使用IF函数
在PolarDB for PostgreSQL中,你可以使用LIKE运算符来实现类似的查询功能,而不是使用IF函数
47 7
|
3月前
|
关系型数据库 分布式数据库 PolarDB
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
85 3
|
3月前
|
关系型数据库 分布式数据库 PolarDB
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
76 1
|
3月前
|
关系型数据库 分布式数据库 PolarDB
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
电子书阅读分享《PolarDB开发者大会:PolarDB在线数据实时分析加速》
89 1
|
3月前
|
存储 关系型数据库 分布式数据库
PolarDB for PostgreSQL查询问题之条件查询失败如何解决
PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。