数据库持久层性能测试报告
1. 前言
本测试目的在于对比Hibernate数据库框架和传统的JDBC框架之间性能差异,并从性能方面证明在高性能、复杂系统开发中,传统JDBC框架比hibernate更具备优势。
2. 测试环境
Pentium® Dual-Core CPU E5300 @ 2.60GHz
2.59GHz, 2.00 GB内存
GhostXP_SP2电脑公司特别版 版本: 8.0
300G 硬盘
MySQL Ver 14.14 Distrib 5.1.48, for Win32 <ia32>
MyEclipse 6.6.0 Build id: 6.0.0-20081015
Java build 1.5.0_11-b03, mixed mode
Mysql-connector-java-5.1.13-bin.jar
3. 测试概述
Hibernate:
Hibernate是一个免费的开源Java包,它使得与关系数据库打交道变得十分轻松,就像您的数据库中包含每天使用的普通Java对象一样,同时不必考虑如何把它们从神秘的数据库表中取出(或放回到数据库表中)。
纯JDBC(Pure JDBC)
JDBC 为工具/数据库开发人员提供了一个标准的 API,使他们能够用纯Java API 来编写数据库应用程序。
封装的JDBC框架(JDBC Framework)
在Java提供的JDBC基础上进一步封装,对数据库表结构进行通用建模,并提供了连接池设计;方便了SQL拼接、数据表的增删改查。
多线程操作(MULTI THREAD)
建立多条线程同时对数据库操作
批量操作(BATCH)
开启数据库的事务功能,批量写入、更新操作数据库。仅针对数据库的更新、插入操作。
自增主键
使用数据库管理主键值自增。
自定义主键:
使用java提供的UUID自定义表的主键值。
测试对象包括:
l Hibernate
l 纯JDBC
l 封装的JDBC框架
其他测试说明:
l 除查询,其他测试每次测试前,首先清空数据库,在零数据量的条件下进行测试。
l 不同测试项目在不同的java进程中执行,而不会在相同进程中执行,防止相互影响。
4. 测试内容
4.1 测试表结构设计
自增主键表结构:
Table Create Table
--------- ---------------------------------------------
tc_autopk CREATE TABLE `tc_autopk` (
`COL_PK` char(36) NOT NULL,
`COL_VARCHAR1` varchar(255) DEFAULT NULL,
`COL_VARCHAR2` varchar(255) DEFAULT NULL,
`COL_VARCHAR3` varchar(255) DEFAULT NULL,
`COL_VARCHAR4` varchar(255) DEFAULT NULL,
`COL_DATETIME1` datetime DEFAULT NULL,
`COL_DATETIME2` datetime DEFAULT NULL,
`COL_INT1` int(11) DEFAULT NULL,
`COL_INT2` int(11) DEFAULT NULL,
`COL_DOUBLE1` double DEFAULT NULL,
`COL_DOUBLE2` double DEFAULT NULL,
`COL_BLOB` blob,
`COL_INDEX` varchar(255) DEFAULT NULL,
PRIMARY KEY (`COL_PK`),
KEY `NewIndex1` (`COL_INDEX`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
自定义主键表结构:
Table Create Table
----------- ---------------------------------------------
tc_manualpk CREATE TABLE `tc_manualpk` (
`COL_PK` int(11) NOT NULL AUTO_INCREMENT,
`COL_VARCHAR1` varchar(255) DEFAULT NULL,
`COL_VARCHAR2` varchar(255) DEFAULT NULL,
`COL_VARCHAR3` varchar(255) DEFAULT NULL,
`COL_VARCHAR4` varchar(255) DEFAULT NULL,
`COL_DATETIME1` datetime DEFAULT NULL,
`COL_DATETIME2` datetime DEFAULT NULL,
`COL_INT1` int(11) DEFAULT NULL,
`COL_INT2` int(11) DEFAULT NULL,
`COL_DOUBLE1` double DEFAULT NULL,
`COL_DOUBLE2` double DEFAULT NULL,
`COL_BLOB` blob,
`COL_INDEX` varchar(255) DEFAULT NULL,
PRIMARY KEY (`COL_PK`),
KEY `NewIndex1` (`COL_INDEX`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
外键表结构:
Table Create Table
-------------------- ------------------------------------------------------------------------------------------------------------
tc_manual_foreignkey CREATE TABLE `tc_manual_foreignkey` (
`COL_PK` char(36) NOT NULL,
`COL_VARCHAR1` varchar(255) DEFAULT NULL,
`COL_VARCHAR2` varchar(255) DEFAULT NULL,
`COL_VARCHAR3` varchar(255) DEFAULT NULL,
`COL_VARCHAR4` varchar(255) DEFAULT NULL,
`COL_DATETIME1` datetime DEFAULT NULL,
`COL_DATETIME2` datetime DEFAULT NULL,
`COL_INT1` int(11) DEFAULT NULL,
`COL_INT2` int(11) DEFAULT NULL,
`COL_DOUBLE1` double DEFAULT NULL,
`COL_DOUBLE2` double DEFAULT NULL,
`COL_BLOB` blob,
`COL_FK_TO_INDEX` varchar(255) DEFAULT NULL,
`COL_FK_TO_NONINDEX` varchar(255) DEFAULT NULL,
PRIMARY KEY (`COL_PK`),
KEY `FK_tc_auto_foreignkey2` (`COL_FK_TO_INDEX`),
CONSTRAINT `FK_tc_manual_foreignkey` FOREIGN KEY (`COL_FK_TO_INDEX`) REFERENCES `tc_manualpk` (`COL_PK`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
4.2 单表插入测试
自定义主键插入测试
数据压力(条) |
Hibernate |
Pure JDBC |
JDBC Framework BATCH |
100 |
1742.2 |
1698.4 |
53.4 |
300 |
5065.6 |
5092.2 |
107.6 |
500 |
8487.5 |
8450 |
145.5 |
1000 |
17131.3 |
17132.8 |
233 |
时间:毫秒
自增主键插入测试
数据压力(条) |
Hibernate |
Pure JDBC |
JDBC Framework BATCH |
100 |
1734.4 |
1767.2 |
59.4 |
300 |
5034.4 |
5061 |
90.6 |
500 |
8556.3 |
8454.7 |
139.1 |
1000 |
16896.8 |
17278.2 |
257.7 |
时间:毫秒
4.3 单表更新测试
说明:
l 表更新仅对一条记录进行更新,查看性能。
l 循环次数为1000次。
单表更新测试
更新条目数 |
Hibernate |
JDBC Framework |
JDBC Framework BATCH |
1000 |
17356.2 |
17128.2 |
315.6 |
时间:毫秒
4.4 单表查询测试
测试说明:
l 数据库初始化数据量=1000条数据
l 除主键查询外,其余查询返回记录量 等于 数据库初始化的数据量(即等于全查询)
l 主键查询:指定主键值查询(WHERE PK = :PK)
l 字段查询:指定非索引字段值,使用等号查询(WHERE COLUMN = :COLUMN)
l 范围查询:指定非索引字段值,使用大于号、小于号查询(WHERE DATETIME > :DATETIME)
单表查询测试
查询策略 |
Hibernate |
JDBC |
JDBC Framework |
JDBC Framework MULTI THREAD |
主键查询 |
153 |
437.6 |
71.8 |
78.2 |
字段查询 |
6478.2 |
4903 |
4484.2 |
2772 |
范围查询 |
6384.4 |
4843.8 |
4412.8 |
2381 |
时间:毫秒
4.5 自定义连表查询测试
测试说明:
l hibernate使用HQL操作连接表查询
select tcManualForeignkey from TcManualForeignkey tcManualForeignkey,TcManualpk tcManualpk where tcManualpk.colPk = tcManualForeignkey.colFkToNonindex
l JDBC使用inner join操作连接表查询
SELECT TC_MANUAL_FOREIGNKEY.* FROM TC_MANUAL_FOREIGNKEY INNER JOIN TC_MANUALPK ON TC_MANUAL_FOREIGNKEY.COL_FK_TO_NONINDEX = TC_MANUALPK.COL_PK
多表查询测试
查询次数 |
Hibernate |
JDBC |
JDBC Framework |
100 |
6378.2 |
5375 |
446.8 |
时间:毫秒
5. 附录
5.1 Hibernate代码概述
测试代码使用Hibernate + Spring的架构。要点包括:
l 数据库设计完毕后,使用MyEclipse的DBBrowser自动生成Hibernate的xml配置文件和实体类
l 配置Hibernate-daos.xml,让spring加载hibernate。
l 使用继承了HibernateDaoSupport实现对实体类的增删改查操作。
l Spring关于hibernate配置如下,具体请看测试代码:
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="SpringContextUtil" class="com.xtar.common.tool.SpringContextUtil" scope="singleton" lazy-init="false" />
<!--start 数据库配置 -->
<bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">
<property name="driver">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="driverUrl">
<value>jdbc:mysql://${baseService.database.ip}:${baseService.database.port}/${baseService.database.instance}?useUnicode=true&characterEncoding=utf-8</value>
</property>
<property name="user">
<value>${baseService.database.username}</value>
</property>
<property name="password">
<value>${baseService.database.password}</value>
</property>
<property name="alias">
<value>spring</value>
</property>
<property name="prototypeCount">
<value>5</value>
</property>
<property name="minimumConnectionCount">
<value>5</value>
</property>
<property name="maximumConnectionCount">
<value>10</value>
</property>
<property name="maximumActiveTime">
<value>60000</value>
</property>
<property name="houseKeepingTestSql">
<value>select 1</value>
</property>
<property name="houseKeepingSleepTime">
<value>60000</value>
</property>
<property name="simultaneousBuildThrottle">
<value>100</value>
</property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources">
<list>
<value>com/xtar/biz/domain/CityBuilding.hbm.xml</value>
<value>com/xtar/biz/domain/CityBuildingSetting.hbm.xml</value>
<value>com/xtar/biz/domain/MysqlAutopk.hbm.xml</value>
<value>com/xtar/biz/domain/MysqlBinarytype.hbm.xml</value>
<value>com/xtar/biz/domain/MysqlBlobtype.hbm.xml</value>
<value>com/xtar/biz/domain/MysqlDatetype.hbm.xml</value>
<value>com/xtar/biz/domain/MysqlPrimarykey.hbm.xml</value>
<value>com/xtar/biz/domain/MysqlSchema.hbm.xml</value>
<value>com/xtar/biz/domain/MysqlValuetype.hbm.xml</value>
<value>com/xtar/biz/domain/UsrBuilding.hbm.xml</value>
<value>com/xtar/biz/domain/UsrBuildingStatus.hbm.xml</value>
<value>com/xtar/biz/domain/UsrProfile.hbm.xml</value>
<value>com/xtar/biz/domain/UsrQueue.hbm.xml</value>
<value>com/xtar/biz/domain/UsrTrigger.hbm.xml</value>
<value>com/xtar/biz/domain/VipSetting.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<!--设置数据库方言(不同的数据库不一样) -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<!--是否在(控制台)显示SQL语句(可以查看hibernate生成的sql) -->
<prop key="hibernate.show_sql">false</prop>
<!--hibernate的统计信息-->
<prop key="hibernate.generate_statistics">false</prop>
<!--
JDBC Statement一次提取的记录数量(貌似hibernate不支持)
<prop key="hibernate.jdbc.fetch_size">50</prop>
批量insert、update、delete时的批量大小
<prop key="hibernate.jdbc.batch_size">50</prop>
-->
<prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
<prop key="hibernate.max_fetch_depth">1</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
<prop key="net.sf.ehcache.configurationResourceName">classpath:ehcache.xml</prop>
<prop key="hibernate.cglib.use_reflection_optimizer">false</prop>
</props>
</property>
</bean>
<!--end 数据库配置 -->
</beans>
5.2 封装JDBC代码设计概述
封装的jDBC框架仅仅在SQL拼接方面提高编程效率,其余的直接使用原始JDBC类,要点包括:
l 使用自定义的数据结构DataTable映射数据库实体(本质是键值对,映射数据库字段和值关系)。
l 操作过程根据DataTable的结构自动生成对应的SQL。
l 默认自动更新的SQL是在当前表主键的条件下对所有字段进行更新。
l 默认自动删除的SQL是在当前表主键的条件下删除数据。
l 内置了数据连接池,对事务链接和普通链接区分对待。