懂事务与数据库连接池

在 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. 执行SQL
            String 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);
        }
    }
}

三、事务与连接池整合注意事项

  1. 事务与连接的关系:同一事务必须使用同一数据库连接,否则事务无法生效(可通过 ThreadLocal 存储当前线程连接)。
  2. 连接归还时机:事务提交或回滚后,再归还连接到池,避免提前归还导致事务丢失。
  3. 隔离级别配置:连接池可统一配置事务隔离级别,避免重复设置。
  4. 监控功能:Druid 自带监控面板,可配置后查看连接使用情况、SQL 执行效率等(生产环境必备)。

四、总结

  • 事务保证数据一致性,核心是 ACID 特性和隔离级别配置,实际开发中需结合 JDBC 或框架(Spring)使用。
  • 连接池优化系统性能,Druid 是最优选择,通过配置文件 + 工具类可快速集成。
  • 两者结合时,需重点关注连接的唯一性和归还时机,避免事务失效或连接泄漏。

掌握这两项技术,能解决大部分数据库相关的性能和数据一致性问题,是 Java 后端开发的必备技能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值