JDBC 在性能测试中的应用

本文涉及的产品
应用实时监控服务-应用监控,每月50GB免费额度
应用实时监控服务-用户体验监控,每月100OCU免费额度
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
简介: 我们能否绕开 http 协议,直接测试数据库的性能?是否觉得从数据库中导出 CSV 文件来构造压测数据很麻烦?怎样在压测结束后做数据清理?能不能通过数据库中的插入(删除)记录对压测请求做断言?使用阿里云性能测试工具 PTS 可以轻松解决上述问题。

作者:黄炎帝


前言


我们能否绕开 http 协议,直接测试数据库的性能?是否觉得从数据库中导出 CSV 文件来构造压测数据很麻烦?怎样在压测结束后做数据清理?能不能通过数据库中的插入(删除)记录对压测请求做断言?使用阿里云性能测试工具 PTS 可以轻松解决上述问题。


什么是 JDBC


JDBC(Java DataBase Connectivity,Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。


简单地说,JDBC 可做三件事:与数据库建立连接、发送操作数据库的语句并处理结果。


JDBC 的设计原理


整体架构


1.png

image.gif

JDBC 制定了一套和数据库进行交互的标准,数据库厂商提供这套标准的实现,这样就可以通过统一的 JDBC 接口来连接各种不同的数据库。可以说 JDBC 的作用是屏蔽了底层数据库的差异,使得用户按照 JDBC 写的代码可以在各种不同的数据库上进行执行。那么这是如何实现的呢?如下图所示:


2.png

image.gif

JDBC 定义了 Driver 接口,这个接口就是数据库的驱动程序, 所有跟数据库打交道的操作最后都会归结到这里 ,数据库厂商必须实现该接口,通过这个接口来完成上层应用的调用者和底层具体的数据库进行交互。Driver 是通过 JDBC 提供的 DriverManager 进行注册的,注册的代码写在了 Driver 的静态块中,如 MySQL 的注册代码如下所示:


static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }


作为驱动定义的规范 Driver,它的主要目的就是和数据库建立连接,所以其接口也很简单,如下所示:


public interface Driver {
  //建立连接
    Connection connect(String url, java.util.Properties info)
        throws SQLException;
    boolean acceptsURL(String url) throws SQLException;
    DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
                         throws SQLException;
    int getMajorVersion();
    int getMinorVersion();
    boolean jdbcCompliant();
    public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}


作为 Driver 的管理者 DriverManager,它不仅负责 Driver 的注册/注销,还可以直接获取连接。它是怎么做到的呢?观察下面代码发现,实际是通过遍历所以已经注册的 Driver,找到一个能够成功建立连接的 Driver,并且将 Connection 返回,DriverManager 就像代理一样,将真正建立连接的过程还是交给了具体的 Driver。


for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }
            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }
        }


Connection 设计


通过上节我们知道数据库提供商通过实现Driver接口来向用户提供服务,Driver接口的核心方法就是获取连接。Connection是和数据库打交道的核心接口,下面我们看看它的设计方案。


3.png


通过观察设计图我们发现主要有两类接口:DataSource 和 Connection。下面我们逐一进行介绍。


  • DataSource


直接看源码,如下所示,发现它的核心方法竟然和 Driver 一样,也是获取连接。那为什么还要 DataSource 呢?Driver 本身不就是获取连接的吗?下面我们就看看 DataSource 到底是怎么获取连接的。


 public interface DataSource  extends CommonDataSource, Wrapper {
  Connection getConnection() throws SQLException;
  Connection getConnection(String username, String password)
    throws SQLException;
}


然而我们发现 JDBC 只定义了 DataSource 的接口,并没有给出具体实现,下面我们就以 Spring 实现的 SimpleDriverDataSource 为例,来看看它是怎么做的,代码如下所示,发现 DataSource 的 getConnection(...)方法,最后竟然还是交由 driver.connect(...)去真正建立连接。所以又回到最开始我们所描述的, Driver 才是真正的与数据库打交道的接口。


protected Connection getConnectionFromDriver(Properties props) throws SQLException {
        Driver driver = getDriver();
        String url = getUrl();
        Assert.notNull(driver, "Driver must not be null");
        if (logger.isDebugEnabled()) {
            logger.debug("Creating new JDBC Driver Connection to [" + url + "]");
        }
        return driver.connect(url, props);
    }


那么问题来了,为什么还需要 DataSource 这样的接口,岂不多此一举么?显然不会。DataSource 是加强版的 Driver。它将核心的建立连接的过程交由 Driver 执行,而对于建立缓存,处理分布式事务和连接池等看似与建立连接无关的事情自己来处理。如类的设计图所示,以 PTS 使用的 Druid 连接池为例:


  • ConnectionPoolDataSource:连接池的实现,此数据源实现并不直接创建数据库物理连接,而是一个逻辑实现,它的作用在于池化数据库物理连接。
  • PooledConnection:配合 ConnectionPoolDataSource,由它获取一个池化对象 PooledConnection,再通过该 PooledConnection 间接获取到物理连接。


显然,通过连接池我们可以从连接的管理中抽身,提高连接的利用效率,也能提升压力机的施压能力。


Statement 设计


建立连接之后,用户可能要开始写 SQL 语句,并且交由数据库去执行了。这些是通过 Statement 来实现的。主要分为:


  • Statement:定义一个静态的 SQL 语句,数据库每次执行都需要重新编译,一般用于仅执行一次查询并返回结果的情形。
  • PreparedStatement:定义一个带参的预编译的 SQL 语句,下次执行时,会从缓存中取出遍以后的语句,而不需要重新编译一遍,适用于执行多次相同逻辑的 SQL 语句,当然它还有防 SQL 注入等功能,安全性和效率较高,使用比较频繁。对于性能测试来说,选择 PreparedStatement 最为合适。
  • CallableStatement:用来调用存储过程。


ResultSet 设计


JDBC 使用 ResultSet 接口来承接 Statement 的执行结果。ResultSet 使用指针的方式(next())来逐条获取检索结果,当指针指向某条数据时,用户可以自由的选择获取某一列的数据。PTS 通过将 ResultSet 转化成 CSV 文件,辅助用户以一条 SQL 语句,构造复杂的压测数据。


JDBC 架构总结


通过上面的介绍我们发现,JDBC 的设计还是层次感分明的。


(1)Driver 和 DriverManager 是面向数据库的,设计了一套 Java 访问数据的规范,数据库厂商只需要实现这套规范即可;


(2)DataSource 和 Connection 是面向应用程序开发者的,它们不关心 JDBC 具体是如何跟数据库进行交互的,通过统一的 DataSource 接口就可以拿到 Connection,用户的数据操作都可以通过这个 Connection 来实现了;


(3)Statement 承载了具体的 SQL 命令,用户可以定义不同的 Statement 来向数据库发送指令;


(4)ResultSet 是用来承载 SQL 命令的执行结果。


至此,完成了 加载驱动 -> 建立连接 -> 执行命令 -> 返回结果 这样的和数据库交互的整个过程。如果把这个过程灵活的嵌入到 PTS 性能测试中,便可以解决前言提到的各种问题。


JDBC 在性能测试中的应用


数据库性能测试


  • 背景

大多数对数据库的操作都是通过 HTTP、FTP 或其他协议执行的,但是在某些情况下,绕开中间协议直接测试数据库也很有意义。例如我们希望不触发所有相关查询,而只测试特定 high-value 查询的性能;验证新数据库在高负载下的性能。2.验证某些数据库连接池参数,例如最大连接数  3.节省时间和资源。当我们想要优化 SQL 时,修改代码中的 SQL 语句和其他数据库操作非常繁琐,通过 JDBC 压测,我们可以避免侵入代码,集中精力在 SQL 调优上。


  • 步骤

1、创建场景。我们在 PTS 控制台的【压测中心】->【创建场景】中创建 PTS 压测场景;


4.png


2、场景配置。PTS 支持对 MySQL、PostgreSQL 等四种数据库发起压测。用户填写 JDBC URL、用户名、密码和 SQL 即可发起压测。同时,PTS 还支持提取 ResultSet 中的数据作为出参,给下游 API 使用;对响应进行断言。

3、压测中监控和压测报告。PTS 支持绑定阿里云 RDS 云资源监控,在压测过程中观察 RDS 实时性能指标。此外,PTS 还提供清晰完备的压测报告以及采样日志,供用户随时查看。


5.png6.pngimage.gif


压测数据构造


  • 背景

在模拟不同用户登录、压测业务参数传递等场景中,需要使用参数功能来实现压测的请求中各种动态操作。如果使用传统的 CSV 文件参数,会受到文件大小的限制,且手动创建耗费精力。使用 JDBC 来构造压测数据,可以避免以上问题。


  • 步骤

1、添加数据源。在场景编辑-数据源管理中,选择添加 DB 数据源,输入 URL、用户名、密码和 SQL。


7.png


2、添加参数。填写自定义参数名和列索引。


8.png


3、调试验证。点击调试场景,即可验证提取的结果集是否符合预期。接着,我们就可以在任意想要使用参数的
地方使用${}引用即可。


压测脏数据清理


  • 背景

针对写请求的压测,会在数据库中生成大量脏数据。如何在压测结束后自动清理?


  • 步骤

PTS 给用户提供了解决方案。PTS 支持对串联链路作逻辑上的顺序编排,即前置链路、普通链路和后置链路。执行顺序由先到后。设置某条串联链路为后置链路,填写循环次数即可。


9.png


更多交流,欢迎进钉钉群沟通,PTS 用户交流钉钉群号:11774967。


此外,PTS 近期对售卖方式做了全新升级,基础版价格直降 50%!5W 并发价格只需 199,免去自运维压测平台烦恼!更有新用户 0.99 体验版、VPC 压测专属版,欢迎大家选购!


10.png

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
1月前
|
敏捷开发 测试技术 持续交付
探索自动化测试在敏捷开发中的应用与挑战
本文深入探讨了自动化测试在现代软件开发流程,特别是敏捷开发环境中的重要作用和面临的挑战。通过分析自动化测试的基本原理、实施策略以及在实际项目中的应用案例,揭示了其在提高软件质量和加速产品交付方面的巨大潜力。同时,文章也指出了自动化测试实施过程中可能遇到的技术难题、成本考量及团队协作问题,并提出了相应的解决策略,为软件开发团队提供了有价值的参考和指导。
|
17天前
|
Java 测试技术 数据安全/隐私保护
软件测试中的自动化策略与工具应用
在软件开发的快速迭代中,自动化测试以其高效、稳定的特点成为了质量保证的重要手段。本文将深入探讨自动化测试的核心概念、常见工具的应用,以及如何设计有效的自动化测试策略,旨在为读者提供一套完整的自动化测试解决方案,帮助团队提升测试效率和软件质量。
|
1月前
|
编解码 测试技术 开发工具
测试 iPhone 应用在不同屏幕尺寸和分辨率下的响应式效果
【10月更文挑战第23天】测试 iPhone 应用在不同屏幕尺寸和分辨率下的响应式效果是确保应用质量和用户体验的重要环节。通过手动测试、自动化测试、视觉效果评估、性能测试、用户体验测试等多种方法的综合运用,能够全面地发现应用在响应式效果方面存在的问题,并及时进行解决和优化。同时,持续的测试和优化也是不断提升应用质量和用户满意度的关键。
|
1月前
|
jenkins 测试技术 持续交付
探索自动化测试在持续集成中的应用与挑战
本文深入探讨了自动化测试在现代软件开发流程,特别是持续集成(CI)环境中的关键作用。通过分析自动化测试的优势、实施策略以及面临的主要挑战,旨在为开发团队提供实用的指导和建议。文章不仅概述了自动化测试的基本原理和最佳实践,还详细讨论了如何克服实施过程中遇到的技术难题和管理障碍,以实现更高效、更可靠的软件交付。
|
23天前
|
机器学习/深度学习 人工智能 测试技术
探索自动化测试框架在软件开发中的应用与挑战##
本文将深入探讨自动化测试框架在现代软件开发过程中的应用,分析其优势与面临的挑战。通过具体案例分析,揭示如何有效整合自动化测试以提升软件质量和开发效率。 ##
|
20天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
49 1
|
1月前
|
敏捷开发 监控 jenkins
探索自动化测试框架在敏捷开发中的应用与优化##
本文深入探讨了自动化测试框架在现代敏捷软件开发流程中的关键作用,分析了其面临的挑战及优化策略。通过对比传统测试方法,阐述了自动化测试如何加速软件迭代周期,提升产品质量,并针对实施过程中的常见问题提出了解决方案。旨在为读者提供一套高效、可扩展的自动化测试实践指南。 ##
41 9
|
27天前
|
监控 安全 测试技术
如何在实际项目中应用Python Web开发的安全测试知识?
如何在实际项目中应用Python Web开发的安全测试知识?
28 4
|
25天前
|
监控 JavaScript 前端开发
如何在实际应用中测试和比较React和Vue的性能?
总之,通过多种方法的综合运用,可以相对客观地比较 React 和 Vue 在实际应用中的性能表现,为项目的选择和优化提供有力的依据。
32 1
|
1月前
|
自然语言处理 安全 测试技术
基于大模型的应用的测试的一些注意事项
大模型应用测试需注意三大冲突:时间敏感性冲突,即模型数据可能随时间变得过时;数据真实性冲突,指训练数据中可能存在虚假信息,影响模型准确性;数据一致性冲突,表现为模型对语义相同但句法不同的输入反应不一。测试时应针对这些问题设计用例,确保模型性能。
61 4

相关产品

  • 性能测试