文章目录
所有池基本思想都差不多,这个我一会儿再来进行描述,我先提一个类型 BOLB,这个数据类型可以用来存储图像、音频、视频、文档等任意二进制数据
1.MySQL BLOB 类型
- MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。
- 插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
- MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
四种BLOB类型
- 实际使用中根据需要存入的数据大小定义不同的BLOB类型。
- 需要注意的是:如果存储的文件过大,数据库的性能会下降。
- 如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
1.在创建表时,可以使用下列语法指定BLOB列的类型:
CREATE TABLE table_name (
column_name BLOB
);
2.如果要指定特定长度的BLOB类型,可以使用以下语法:
CREATE TABLE table_name (
column_name BLOB(length)
);
结合IO流对数据库中的blob类型进行操作的案例
public class BlobDemo1 {
BaseDao dao = new BaseDao();
/**
* 添加文件
*
*/
@Test
public void test1() {
String sql = "insert into customers(name,email,birth, photo) values(?,?,?,?)";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
FileInputStream in = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1,"mickey");
ps.setString(2,"mickey@163.com");
ps.setDate(3,new Date(new java.util.Date().getTime()));
in = new FileInputStream("a.jpg");
ps.setBlob(4, in);
ps.executeUpdate(); //执行修改,返回语句影响的行数
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeResource(conn, ps, rs);
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* 查询文件
*/
@Test
public void test2() {
String sql = "select id, name, photo from customers where id = ?";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream in = null;
FileOutputStream out = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 20);
rs = ps.executeQuery();
if (rs.next()) {
System.out.println(rs.getInt("id"));
System.out.println(rs.getString(2));
//读取blob
Blob blob = rs.getBlob(3);
in = blob.getBinaryStream();
out = new FileOutputStream("a1.jpg");
int length = -1;
byte[] buffer = new byte[1024];
while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeResource(conn, ps, rs);
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* 修改BLOB
*
*/
@Test
public void test3() {
String sql = "update customers set photo = ? where id=?";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
FileInputStream in = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(2, 20);
in = new FileInputStream("b.jpg");
ps.setBlob(1, in);
ps.executeUpdate(); //执行修改,返回语句影响的行数
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.closeResource(conn, ps, rs);
try {
in.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
这里 JdbcUtil是我自己写的工具类 有需要可以看上一个博客。
2. 数据的批量插入
配置文件要把参数修改了!!!!
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
也就是rewriteBatchedStatements=true
这个参数
事务的开启与否对批量插入的调优是微乎其微的。
rewriteBatchedStatements=true不设置这个参数会导致什么问题
rewriteBatchedStatements=true
是 MySQL 数据库驱动程序的一个参数,用于启用批处理语句重写。如果不设置该参数,可能会导致以下问题:
1.性能问题:默认情况下,MySQL 客户端会将批量插入语句拆分为单个 SQL 语句并逐个执行,这会增加网络开销和服务器端的处理负担。启用 rewriteBatchedStatements=true 参数后,MySQL 客户端将尝试将多个 SQL 语句合并成一个更大的批处理语句,从而减少了通信次数和服务器端的执行次数,提高性能。
2.网络流量问题:在默认情况下,MySQL 客户端每次发送单个 SQL 语句给服务器,这会导致不必要的网络开销。启用 rewriteBatchedStatements=true 参数后,MySQL 客户端可以将多个 SQL 语句合并为一个较大的批处理语句,从而减少了网络传输的数据量,降低了网络流量消耗。
总之,如果不设置 rewriteBatchedStatements=true 参数,可能导致性能下降和网络流量增加。当需要执行大批量的 SQL 语句时,启用该参数可以显著提升性能,特别是在批量插入操作中。然而,具体效果还取决于数据库的配置和硬件性能。
实战
当需要成批插入或者更新记录时,可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率
JDBC的批量处理语句包括下面三个方法:
addBatch(String)
:添加需要批量处理的SQL语句或是参数;executeBatch()
:执行批量处理语句;clearBatch()
:清空缓存的数据
通常我们会遇到两种批量执行SQL语句的情况:- 多条SQL语句的批量处理;
- 一个SQL语句的批量传参;
public class BatchDemo1 {
@Test
public void test1() throws Exception {
long start = System.currentTimeMillis();
Connection conn = JdbcUtil.getConnection();
conn.setAutoCommit(false);//取消事务的自动提交,在后面需要用conn.commit()来手动提交
String sql = "insert into goods(name) values(?)";
PreparedStatement ps = conn.prepareStatement(sql);
for(int i = 1; i <= 200000; i++){
ps.setString(1, "name_" + i);
ps.executeUpdate();
}
conn.commit();
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//182340
JdbcUtil.closeResource(conn, ps, null);
}
@Test
public void test2() throws Exception {
long start = System.currentTimeMillis();
Connection conn = JdbcUtil.getConnection();
conn.setAutoCommit(false);
String sql = "insert into goods(name) values(?)";
PreparedStatement ps = conn.prepareStatement(sql);
for(int i = 1; i <= 200000; i++){
ps.setString(1, "name_" + i);
// ps.executeUpdate();
ps.addBatch(); //添加语句到Batch,攒sql语句
if (i % 100000 == 0) {
ps.executeBatch(); //每500个语句,批量执行一次
ps.clearBatch(); //清空批量
}
}
conn.commit();
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//82340
JdbcUtil.closeResource(conn, ps, null);
}
}
当不使用 addBatch(String)
方法时,每个 SQL 语句都会单独发送给数据库并执行。这意味着每次执行 SQL 语句都需要与数据库建立连接、发送请求、等待响应,并且涉及往返的网络通信。如果有大量的 SQL 语句需要执行,这将导致大量的网络开销和延迟。
相反,当你使用 addBatch(String)
方法时 并且这个参数调为rewriteBatchedStatements=true
,你可以将多个 SQL 语句一起添加到批处理中。然后,通过执行批处理,所有的 SQL 语句都会一次性发送给数据库执行,减少了网络开销和延迟。这样可以显著提高性能,尤其是在需要执行大量 SQL 语句时。
虽然使用 addBatch(String) 方法可以提高性能,但也需要注意以下几点:
- 1.所有的 SQL 语句在逻辑上应该是相互独立的,互不影响的。否则,如果其中一个 SQL 语句失败,整个批处理将会回滚。
- 2.如果批处理中的 SQL 语句过多,超出了数据库的处理能力范围,也可能导致性能下降。
- 3.需要根据具体场景和应用需求进行性能测试和优化,以确定最佳的批处理大小和方式。
总之,使用 addBatch(String) 方法可以通过批处理方式一次性执行多个 SQL 语句,从而减少了与数据库的交互次数,提高了性能。但需要根据实际情况进行合理的优化和测试,以获得最佳性能。
3.数据库连接池
1.JDBC数据库连接池的必要性
- 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤:
- 在主程序(如servlet、beans)中建立数据库连接
- 进行sql操作
- 断开数据库连接
- 这种模式开发,存在的问题:
- 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。**数据库的连接资源并没有得到很好的重复利用。**若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
- 对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。(回忆:何为Java的内存泄漏?)
- 这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
2.数据库连接池技术
- 数据库连接池的基本思想:就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
- 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中
工作原理与数据库连接池的参数
1.初始连接数(Initial Pool Size)
:连接池在初始化时创建的初始连接数。
2.最小空闲连接数(Min Idle Connections)
:连接池中保持的最小空闲连接数。当连接池中的连接数低于此值时,连接池会创建新的连接以满足最小空闲连接数的要求。
3.最大连接数(Max Pool Size)
:连接池允许存在的最大连接数。当连接池中的连接已达到最大连接数时,后续的连接请求可能需要等待或被拒绝。
4.连接超时时间(Connection Timeout)
:获取连接的最大等待时间,超过该时间仍未获取到可用连接,则会抛出超时异常。
5.连接最大存活时间(Max Connection Lifetime)
:连接在连接池中的最大存活时间。当连接存活时间超过该阈值时,连接将会被关闭并从连接池中移除。
6.连接回收策略(Connection Reclaim Policy)
:确定何时对连接进行检查、验证和回收的策略。可以根据连接的空闲时间、使用次数等因素来触发回收操作。
7.验证查询(Validation Query)
:用于验证连接是否有效的 SQL 查询语句。连接池会定期执行该查询来检测连接是否可用。
8.连接验证超时时间(Validation Timeout)
:连接验证查询的超时时间,如果查询在该时间内未完成,则将认为连接无效。
9.连接重建策略(Connection Rebuild Policy)
:在连接池中出现故障的连接时,决定是否关闭该连接并重新创建新的连接。
数据库连接池技术的优点:
- 资源重用由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
- 更快的系统反应速度 数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间
- 新的资源分配手段对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源
- 统一的连接管理,避免数据库连接泄漏在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露
3.多种开源数据库连接池
-
JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
- DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
- C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。hibernate官方推荐使用
- Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快
-
DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
-
DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
-
特别注意:
- 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
- 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
1.C3P0数据库连接池
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2.1</version>
</dependency>
其中,resources下的配置文件为:【c3p0-config.xml】
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<named-config name="helloc3p0">
<!-- 获取连接的4个基本信息 -->
<property name="user">root</property>
<property name="password">abc123</property>
<property name="jdbcUrl">jdbc:mysql:///test</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- 涉及到数据库连接池的管理的相关属性的设置 -->
<!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">5</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">10</property>
<!-- C3P0 数据库连接池可以维护的 Statement 的个数 -->
<property name="maxStatements">20</property>
<!-- 每个连接同时可以使用的 Statement 对象的个数 -->
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
C3P0数据库连接池案例
public class C3p0Demo1 {
@Test
//使用C3P0数据库连接池的方式,获取数据库的连接:不推荐
public void test1() throws Exception{
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test");
cpds.setUser("root");
cpds.setPassword("123456");
// cpds.setMaxPoolSize(100);
Connection conn = cpds.getConnection();
System.out.println(conn);
}
@Test
public void test2() throws SQLException {
DataSource cpds = new ComboPooledDataSource("hellc3p0");
Connection conn = cpds.getConnection();
System.out.println(conn);
}
}
2.DBCP数据库连接池
- DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool。如需使用该连接池实现,应在系统中增加如下两个 jar 文件:
- Commons-dbcp.jar:连接池的实现
- Commons-pool.jar:连接池实现的依赖库
- Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
- 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
- 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
配置属性说明
属性 | 默认值 | 说明 |
---|---|---|
initialSize | 0 | 连接池启动时创建的初始化连接数量 |
maxActive | 8 | 连接池中可同时连接的最大的连接数 |
maxIdle | 8 | 连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制 |
minIdle | 0 | 连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。 |
maxWait | 无限制 | 最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待 |
poolPreparedStatements | false | 开启池的Statement是否为preparedStatement |
maxOpenPreparedStatements | 无限制 | 开启池的preparedStatement 后的同时最大连接数 |
minEvictableIdleTimeMillis | 30分钟 | 连接池中连接,在时间段内一直空闲, 被逐出连接池的时间 |
removeAbandonedTimeout | 300 秒 | 超过时间限制,回收没有用(废弃)的连接 |
removeAbandoned | removeAbandoned false | 超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收 |
pom.xml
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>dbcp</artifactId>
<version>6.0.29</version>
</dependency>
其中,resources下的配置文件为:【dbcp.properties】
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=123456
initialSize=10
案例
public class DBCPTest {
//方式一:不推荐
@Test
public void testGetConnection() throws SQLException{
//创建了DBCP的数据库连接池
BasicDataSource source = new BasicDataSource();
//设置基本信息
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql:///test");
source.setUsername("root");
source.setPassword("123456");
//还可以设置其他涉及数据库连接池管理的相关属性:
source.setInitialSize(10);
source.setMaxActive(10);
//。。。
Connection conn = source.getConnection();
System.out.println(conn);
}
//方式二:推荐:使用配置文件
@Test
public void testGetConnection1() throws Exception{
Properties pros = new Properties();
//方式1:
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
//方式2:
// FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
pros.load(is);
DataSource source = BasicDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
3.Druid(德鲁伊)数据库连接池
Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
其中,src下的配置文件为:【druid.properties】
url=jdbc:mysql:///test
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=10
案例
public class DruidTest {
@Test
public void test1() throws Exception{
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
DataSource source = DruidDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
Druid 是一个开源的数据库连接池,提供了大量可配置的参数用于优化和控制连接池的行为。以下是一些常见的 Druid 连接池的配置参数:
- url:数据库连接的 URL。
- driverClassName:数据库驱动类名。
- username:数据库用户名。
- password:数据库密码。
- initialSize:连接池在初始化时创建的初始连接数。
- minIdle:连接池中保持的最小空闲连接数。
- maxActive:连接池允许存在的最大活动连接数。
- maxWait:获取连接的最大等待时间。
- validationQuery:用于验证连接是否有效的 SQL 查询语句。
- testOnBorrow:从连接池中获取连接时,是否执行验证查询。
- testOnReturn:将连接归还到连接池时,是否执行验证查询。
- testWhileIdle:连接池中的连接是否定期执行验证查询。
- timeBetweenEvictionRunsMillis:运行空闲连接回收器的时间间隔。
- minEvictableIdleTimeMillis:连接在连接池中保持空闲的最短时间。
- removeAbandoned:是否移除长时间未使用的废弃连接。
- removeAbandonedTimeout:废弃连接的超时时间。
- logAbandoned:是否记录移除废弃连接的日志