懂事务与数据库连接池
在 Java 开发中,事务和数据库连接池是保证数据一致性与系统性能的核心技术。本文结合实战案例,详细拆解事务的原理、用法,以及 Druid 连接池的配置与优化,适合 Java 开发者入门学习。
一、事务:数据一致性的保障
1. 事务的核心概念
事务是数据库提供的核心特性,指由一系列数据操作组成的执行单元,要么全部成功,要么全部失败。最典型的场景就是转账:A 给 B 转 1000 元,必须保证 A 账户扣款成功和 B 账户到账成功同时发生,任何一步失败都要回滚到初始状态。
2. 事务的四大特性(ACID)
- 原子性:事务中的操作不可分割,要么全执行,要么全不执行。
- 一致性:事务执行前后,数据需符合业务规则(如转账前后两人余额总和不变)。
- 隔离性:并发场景下,不同事务互不干扰。
- 持久性:事务提交后,数据永久保存到数据库,即使数据库崩溃也能恢复。
3. MySQL 中操作事务的两种方式
(1)命令行手动控制
适合测试和学习,步骤如下:
-- 1. 开启事务
start transaction;
-- 2. 执行业务操作(如转账)
update t_account set money = money - 1000 where username = '冠希';
update t_account set money = money + 1000 where username = '美美';
-- 3. 提交事务(数据永久生效)
commit;
-- 或回滚事务(恢复到初始状态)
rollback;
(2)关闭自动提交模式
MySQL 默认每条 SQL 自动提交事务,可手动关闭自动提交,批量执行后统一提交:
-- 查看自动提交状态(默认ON)
show variables like '%commit%';
-- 关闭自动提交
set autocommit = off;
-- 执行多条SQL
update t_account set money = money - 1000 where username = '冠希';
update t_account set money = money + 1000 where username = '美美';
-- 手动提交或回滚
commit; -- 或 rollback;
4. 事务隔离级别与并发问题
(1)不考虑隔离性的三大问题
- 脏读:一个事务读取到另一个事务未提交的数据(如 B 转账未提交,A 读到了未确认的余额)。
- 不可重复读:同一事务内多次查询同一数据,结果不一致(因其他事务修改并提交)。
- 幻读:同一事务内多次查询,结果行数不一致(因其他事务插入或删除数据)。
(2)四种隔离级别(从低到高)
| 隔离级别 | 解决问题 | 效率 | 适用场景 |
|---|---|---|---|
| Read uncommitted | 无 | 最高 | 非核心业务 |
| Read committed | 避免脏读 | 较高 | 一般业务场景 |
| Repeatable read | 避免脏读、不可重复读 | 中等 | MySQL 默认级别 |
| Serializable | 避免所有并发问题 | 最低 | 金融等核心场景 |
MySQL 设置隔离级别命令:
set session transaction isolation level repeatable read;
二、数据库连接池:系统性能优化关键
1. 连接池的核心作用
传统 JDBC 开发中,每次操作数据库都要创建和销毁连接,耗时且耗资源。连接池的核心是连接复用:提前创建一定数量的连接,用的时候从池里拿,用完后归还,避免重复创建销毁的性能损耗。
2. 连接池核心参数(必懂)
无论哪种开源连接池,都需要配置以下核心参数:
- 初始大小:连接池启动时创建的连接数(默认通常 10 个)。
- 最大连接数:连接池能容纳的最大连接数(避免连接过多导致服务器压力)。
- 最小空闲连接数:连接池保持的最小空闲连接,避免频繁创建连接。
- 最大等待时间:获取连接的超时时间(超时未获取则抛出异常)。
- 四大基础参数:驱动类名、数据库 URL、用户名、密码(连接数据库的必备信息)。
3. Druid 连接池实战(阿里巴巴开源)
Druid 是目前性能最优的连接池,支持监控、加密等功能,阿里内部大规模使用,推荐生产环境首选。
(1)导入依赖(Maven)
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version>
</dependency>
(2)配置文件方式使用(推荐)
创建druid.properties配置文件(放在 resources 目录):
# 四大基础参数
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///spring_db
username=root
password=root
# 连接池参数
initialSize=5
maxActive=10
maxWait=3000
minIdle=3
maxIdle=6
(3)编写工具类(复用连接与关闭资源)
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;public class JdbcUtils {// 连接池对象(全局唯一)private static DataSource DATA_SOURCE;static {try {// 加载配置文件Properties props = new Properties();InputStream is = JdbcUtils.class.getResourceAsStream("/druid.properties");props.load(is);// 初始化连接池DATA_SOURCE = DruidDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}}// 获取连接public static Connection getConnection() {try {return DATA_SOURCE.getConnection();} catch (Exception e) {throw new RuntimeException("获取连接失败", e);}}// 关闭资源(归还连接到池)public static void close(Connection conn, Statement stmt, ResultSet rs) {try {if (rs != null) rs.close();if (stmt != null) stmt.close();if (conn != null) conn.close(); // 归还连接,而非销毁} catch (Exception e) {e.printStackTrace();}}// 重载:无ResultSet时关闭public static void close(Connection conn, Statement stmt) {close(conn, stmt, null);}
}
(4)使用示例
public class DruidTest {public static void main(String[] args) {Connection conn = null;Statement stmt = null;try {// 1. 获取连接(从连接池)conn = JdbcUtils.getConnection();// 2. 执行SQLString sql = "insert into t_account values (null, '小苍', 10000)";stmt = conn.createStatement();stmt.executeUpdate(sql);System.out.println("插入成功");} catch (Exception e) {e.printStackTrace();} finally {// 3. 关闭资源(归还连接)JdbcUtils.close(conn, stmt);}}
}
三、事务与连接池整合注意事项
- 事务与连接的关系:同一事务必须使用同一数据库连接,否则事务无法生效(可通过 ThreadLocal 存储当前线程连接)。
- 连接归还时机:事务提交或回滚后,再归还连接到池,避免提前归还导致事务丢失。
- 隔离级别配置:连接池可统一配置事务隔离级别,避免重复设置。
- 监控功能:Druid 自带监控面板,可配置后查看连接使用情况、SQL 执行效率等(生产环境必备)。
四、总结
- 事务保证数据一致性,核心是 ACID 特性和隔离级别配置,实际开发中需结合 JDBC 或框架(Spring)使用。
- 连接池优化系统性能,Druid 是最优选择,通过配置文件 + 工具类可快速集成。
- 两者结合时,需重点关注连接的唯一性和归还时机,避免事务失效或连接泄漏。
掌握这两项技术,能解决大部分数据库相关的性能和数据一致性问题,是 Java 后端开发的必备技能。
