第一部分:数据库设计原则

掌握数据库设计的核心原则与最佳实践

学习目标

理解数据库设计范式,掌握ER模型设计,能够创建规范的数据库结构

第一范式(1NF)

确保每列都是原子的,不可再分

  • 消除重复的列
  • 为每列创建单独的表
  • 定义主键

第二范式(2NF)

满足1NF,且所有非主键字段完全依赖于主键

  • 消除部分依赖
  • 创建关系表
  • 使用外键关联

第三范式(3NF)

满足2NF,且消除传递依赖

  • 所有字段直接依赖于主键
  • 消除冗余数据
  • 提高数据一致性

实体关系模型

  • 实体:数据库中的对象
  • 属性:实体的特征
  • 关系:实体间的联系

主键设计原则

  • 唯一标识每条记录
  • 不可包含业务逻辑
  • 简单且稳定
  • 避免使用复合主键

索引策略

  • WHERE子句常用字段
  • JOIN操作关联字段
  • ORDER BY排序字段
  • 避免过度索引

设计小贴士

在数据库设计阶段,不要过度追求范式化。适当的反范式设计可以提升查询性能。同时,要为每个表添加创建时间(created_at)和更新时间(updated_at)字段,这对于数据追踪和审计非常重要。

第二部分:JDBC编程

使用JDBC API连接和操作数据库

学习目标

掌握JDBC核心接口使用,能够编写安全的数据库操作代码

MySQL驱动配置

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>

PostgreSQL驱动配置

<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.6.0</version> </dependency>

Oracle驱动配置

<dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc11</artifactId> <version>21.9.0.0</version> </dependency>

JDBC连接步骤

1. 加载驱动
2. 建立连接
3. 创建语句
4. 执行查询
5. 处理结果
6. 关闭资源
1

使用PreparedStatement

防止SQL注入的安全查询方式

String sql = "SELECT * FROM users WHERE username = ? AND password = ?"; try (Connection conn = DriverManager.getConnection(url, user, pass); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, username); pstmt.setString(2, password); try (ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { // 处理结果 } } }
2

批量操作

提高数据插入/更新效率

String sql = "INSERT INTO orders (product_id, quantity) VALUES (?, ?)"; try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { for (Order order : orders) { pstmt.setInt(1, order.getProductId()); pstmt.setInt(2, order.getQuantity()); pstmt.addBatch(); // 添加到批处理 if (i % BATCH_SIZE == 0) { pstmt.executeBatch(); // 执行批处理 } } pstmt.executeBatch(); // 执行剩余批处理 }

动手练习

  • 创建一个用户表(user)包含id、username、email字段
  • 使用JDBC实现用户CRUD操作
  • 实现分页查询功能
  • 添加事务处理逻辑

第三部分:连接池技术

优化数据库连接管理,提高应用性能

学习目标

理解连接池原理,掌握常用连接池框架配置与使用

HikariCP

高性能JDBC连接池

  • 轻量级(130KB)
  • 无锁设计
  • Spring Boot默认连接池

Druid

阿里开源的数据库连接池

  • 强大的监控功能
  • 防御SQL注入
  • 内置加密功能

Apache DBCP

成熟的连接池实现

  • 稳定可靠
  • 配置简单
  • 适合传统项目
1

HikariCP配置

Spring Boot应用配置

spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: securepass driver-class-name: com.mysql.cj.jdbc.Driver hikari: connection-timeout: 30000 maximum-pool-size: 20 minimum-idle: 5 idle-timeout: 600000 max-lifetime: 1800000
2

Druid监控配置

启用内置监控页面

@Configuration public class DruidConfig { @Bean public ServletRegistrationBean statViewServlet() { ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); // 设置监控页面访问参数 Map params = new HashMap<>(); params.put("loginUsername", "admin"); params.put("loginPassword", "admin123"); params.put("allow", ""); // 允许所有访问 bean.setInitParameters(params); return bean; } }

连接池优化建议

连接池大小设置公式:Tn × (Tm + Tc - 1) ≤ Tmax

其中:

  • Tn:线程数
  • Tm:每个线程平均持有连接时间
  • Tc:连接最长等待时间
  • Tmax:最大连接数

第四部分:ORM框架

使用对象关系映射简化数据库操作

学习目标

掌握MyBatis和Hibernate核心功能,选择合适的ORM解决方案

特性 MyBatis Hibernate JPA
学习曲线 平缓 陡峭 中等
SQL控制 完全控制 有限控制 有限控制
性能 中等 中等
缓存机制 二级缓存 一级/二级/查询缓存 一级/二级缓存
适用场景 复杂SQL/存储过程 快速开发/简单CRUD 标准规范/更换数据库
1

MyBatis动态SQL

灵活的条件查询

<select id="findUsers" parameterType="map" resultType="User"> SELECT * FROM users <where> <if test="username != null"> AND username = #{username} </if> <if test="email != null"> AND email LIKE CONCAT(#{email}, '%') </if> <if test="statusList != null and statusList.size() > 0"> AND status IN <foreach item="status" collection="statusList" open="(" separator="," close=")"> #{status} </foreach> </if> </where> ORDER BY create_time DESC </select>
2

Hibernate实体映射

对象-关系映射配置

@Entity @Table(name = "employees") public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "full_name", nullable = false, length = 100) private String fullName; @Column(unique = true) private String email; @Enumerated(EnumType.STRING) private Department department; @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL) private List projects = new ArrayList<>(); // Getters and setters }

实践项目

  • 使用MyBatis实现多表联查
  • 使用Hibernate实现一对多关系映射
  • 实现乐观锁控制
  • 配置二级缓存

第五部分:事务管理

确保数据一致性与完整性

学习目标

理解事务特性,掌握编程式和声明式事务实现

ACID原则

事务的四个基本特性

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

隔离级别

控制事务并发影响

  • 读未提交(Read Uncommitted)
  • 读已提交(Read Committed)
  • 可重复读(Repeatable Read)
  • 串行化(Serializable)

传播行为

事务方法调用规则

  • REQUIRED(默认)
  • REQUIRES_NEW
  • NESTED
  • SUPPORTS
1

声明式事务配置

使用Spring @Transactional注解

@Service public class OrderService { @Autowired private OrderRepository orderRepository; @Autowired private InventoryService inventoryService; @Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {BusinessException.class, SQLException.class}, timeout = 30 ) public void placeOrder(Order order) { // 1. 创建订单 orderRepository.save(order); // 2. 扣减库存 inventoryService.reduceStock(order.getProductId(), order.getQuantity()); // 3. 其他业务操作... } }
2

事务边界控制

避免事务过长

  • 在事务方法中避免远程调用
  • 减少事务中的IO操作
  • 避免在事务中处理大量数据
  • 设置合理的事务超时时间
  • 使用读已提交隔离级别

事务设计建议

在设计事务时,要遵守"小事务"原则:

  • 事务内操作应少于5个
  • 执行时间应小于50ms
  • 更新行数应少于100行
  • 避免在事务中执行耗时操作

第六部分:性能优化

提升数据库访问效率与响应速度

学习目标

掌握SQL优化技巧,合理使用缓存和索引

查询优化

  • 避免使用SELECT *
  • 使用LIMIT分页
  • 合理使用JOIN替代子查询
  • 避免在WHERE子句中使用函数
EXPLAIN分析
索引优化
查询缓存

索引优化

  • 为WHERE条件字段加索引
  • 使用组合索引最左前缀原则
  • 避免在索引列上使用函数
  • 定期分析索引使用情况
B+树
覆盖索引
索引下推

缓存策略

  • 使用Redis缓存热点数据
  • 合理设置缓存过期时间
  • 使用缓存穿透解决方案
  • 多级缓存架构
Redis
Memcached
Caffeine
1

慢查询分析

MySQL慢查询日志配置

# my.cnf配置 [mysqld] slow_query_log = ON slow_query_log_file = /var/log/mysql/slow.log long_query_time = 1 # 超过1秒记录 log_queries_not_using_indexes = ON
2

读写分离

使用ShardingSphere实现读写分离

spring: shardingsphere: datasource: names: master, slave1, slave2 master: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://master-host:3306/db username: root password: masterpass slave1: # ...类似配置 slave2: # ...类似配置 rules: readwrite-splitting: data-sources: readwrite_ds: write-data-source-name: master read-data-source-names: slave1, slave2 load-balancer-name: round_robin load-balancers: round_robin: type: ROUND_ROBIN

性能优化原则

优化前先测量,不要盲目优化。使用监控工具(如Prometheus+Grafana)持续跟踪数据库性能指标:

  • QPS(每秒查询数)
  • TPS(每秒事务数)
  • 连接数使用率
  • 慢查询比例
  • 缓存命中率

第七部分:常见错误与解决方案

识别并解决数据库开发中的常见问题

学习目标

识别常见数据库问题,掌握解决方案和预防措施

错误类型 现象 解决方案 预防措施
N+1查询问题 一次查询触发大量额外查询 使用JOIN FETCH/批量查询 ORM配置优化
连接泄露 连接耗尽,应用无法访问DB 检查未关闭的连接 使用try-with-resources
死锁 事务相互等待,无法继续 分析死锁日志,调整事务顺序 统一资源访问顺序
慢查询 响应时间过长,CPU占用高 EXPLAIN分析,添加索引 定期优化SQL
主键冲突 违反唯一约束,插入失败 捕获异常,重试或提示 使用UUID或雪花算法
1

事务失效场景

  • 方法非public修饰
  • 方法内部调用(@Transactional无效)
  • 异常类型不匹配
  • 多线程环境下事务传播
  • 数据库引擎不支持事务
2

连接池配置问题

  • 连接泄露:未正确关闭连接
  • 连接超时:timeout设置过短
  • 连接数不足:maxPoolSize设置过小
  • 验证失败:数据库密码变更
  • 网络中断:数据库服务器不可达

典型错误示例

// 错误:在循环中执行SQL查询 for (Long id : ids) { User user = userDao.findById(id); // 每次循环都执行一次查询 // ...处理用户 } // 正确:批量查询 List users = userDao.findByIds(ids); // 一次查询获取所有数据 for (User user : users) { // ...处理用户 }

上述错误会导致N+1查询问题,当ids有1000个元素时,会产生1001次查询(1次获取ID列表+1000次单条查询)

错误预防建议

在开发过程中建立防护措施:

  • 使用SonarQube等静态代码分析工具
  • 编写数据库访问单元测试
  • 使用连接池监控和SQL监控
  • 在生产环境开启慢查询日志
  • 定期进行SQL审计