第16章 性能优化
学习 MyBatis 性能优化技巧和最佳实践
学习进度:64% 代码演示按钮 💻 查看完整代码 - 在线IDE体验
🎯 学习目标
📚
理解性能优化核心概念
掌握 MyBatis 性能优化的基本原理和策略
🔧
掌握优化实践技能
学会 SQL 优化、缓存配置和批量操作等技巧
💡
了解监控与调优
明确性能监控指标和调优最佳实践
1. 性能优化概述
1.1 性能优化的重要性
MyBatis性能优化是提升应用程序响应速度和处理能力的关键环节,主要包括:
- 响应时间优化:减少SQL执行时间
- 吞吐量提升:增加并发处理能力
- 资源利用率:优化内存和CPU使用
- 用户体验:提升应用响应速度
1.2 性能优化策略
性能优化金字塔:数据库层面:索引优化、SQL优化框架层面:缓存机制、批量操作应用层面:连接池配置、代码优化系统层面:硬件配置、网络优化
2. SQL优化技巧
2.1 索引优化
-- 创建合适的索引
CREATE INDEX idx_user_age ON users(age);
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_order_user_status ON orders(user_id, status);
CREATE INDEX idx_order_create_time ON orders(create_time);
-- 复合索引优化
CREATE INDEX idx_user_age_create_time ON users(age, create_time);
CREATE INDEX idx_order_status_create_time ON orders(status, create_time);
2.2 查询优化
<!-- 优化前:全表扫描 -->
<select id="getUsersByAgeRangeUnoptimized" resultType="User">
SELECT * FROM users WHERE age >= #{minAge} AND age <= #{maxAge}
</select>
<!-- 优化后:使用BETWEEN和索引 -->
<select id="getUsersByAgeRange" resultType="User">
SELECT id, username, email, age, create_time
FROM users
WHERE age BETWEEN #{minAge} AND #{maxAge}
ORDER BY create_time DESC
LIMIT #{limit}
</select>
2.3 分页查询优化
<!-- 深分页优化 -->
<select id="selectUsersByPageOptimized" resultMap="UserResultMap">
SELECT u.id, u.username, u.email, u.age, u.create_time
FROM users u
WHERE u.id > #{lastId}
ORDER BY u.id
LIMIT #{pageSize}
</select>
<!-- 使用覆盖索引 -->
<select id="selectUsersWithCoveringIndex" resultType="User">
SELECT id, username, email FROM users
WHERE age BETWEEN #{minAge} AND #{maxAge}
ORDER BY age, id
LIMIT #{offset}, #{limit}
</select>
3. 缓存机制优化
3.1 一级缓存(SqlSession级别)
// 一级缓存默认开启,同一个SqlSession内有效
public void testFirstLevelCache() {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 第一次查询,从数据库获取
User user1 = mapper.selectUserById(1L);
// 第二次查询,从一级缓存获取
User user2 = mapper.selectUserById(1L);
// user1 == user2 为 true
sqlSession.close();
}
3.2 二级缓存(Mapper级别)
<!-- 在Mapper XML中配置二级缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
<!-- 查询语句使用缓存 -->
<select id="selectUserById" resultType="User" useCache="true">
SELECT * FROM users WHERE id = #{id}
</select>
3.3 Spring Cache集成
@Service
public class UserServiceImpl implements UserService {
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
return userMapper.selectUserById(id);
}
@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
userMapper.updateUser(user);
}
@CacheEvict(value = "users", allEntries = true)
public void clearAllCache() {
// 清空所有缓存
}
}
4. 批量操作优化
4.1 批量插入
<!-- 批量插入优化 -->
<insert id="batchInsertUsers" parameterType="list">
INSERT INTO users (username, email, age, create_time, update_time) VALUES
<foreach collection="users" item="user" separator=",">
(#{user.username}, #{user.email}, #{user.age}, NOW(), NOW())
</foreach>
</insert>
// Java代码
@Transactional
public void batchInsertUsers(List<User> users) {
int batchSize = 1000;
for (int i = 0; i < users.size(); i += batchSize) {
int end = Math.min(i + batchSize, users.size());
List<User> batch = users.subList(i, end);
userMapper.batchInsertUsers(batch);
}
}
4.2 批量更新
<!-- 批量更新优化 -->
<update id="batchUpdateUsers" parameterType="list">
<foreach collection="users" item="user" separator=";">
UPDATE users SET
username = #{user.username},
email = #{user.email},
age = #{user.age},
update_time = NOW()
WHERE id = #{user.id}
</foreach>
</update>
4.3 性能对比
| 操作方式 | 1000条记录耗时 | 性能提升 | 适用场景 |
|---|---|---|---|
| 逐条插入 | 2000ms | 基准 | 少量数据 |
| 批量插入 | 200ms | 10倍 | 大量数据 |
| 批量+事务 | 150ms | 13倍 | 大量数据+一致性 |
5. 连接池优化
5.1 Druid连接池配置
# application.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 初始连接数
initial-size: 10
# 最小空闲连接数
min-idle: 10
# 最大活跃连接数
max-active: 50
# 获取连接等待超时时间
max-wait: 60000
# 检测间隔时间
time-between-eviction-runs-millis: 60000
# 最小生存时间
min-evictable-idle-time-millis: 300000
# 验证查询
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 预处理语句缓存
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
5.2 连接池监控
# 启用Druid监控
spring:
datasource:
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: false
login-username: admin
login-password: admin
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
6. 延迟加载优化
6.1 关联查询优化
<!-- 延迟加载配置 -->
<resultMap id="UserWithOrdersResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
<collection property="orders" ofType="Order"
select="selectOrdersByUserId"
column="id"
fetchType="lazy"/>
</resultMap>
<select id="selectUsersWithOrders" resultMap="UserWithOrdersResultMap">
SELECT id, username, email FROM users
</select>
6.2 避免N+1查询问题
<!-- 使用JOIN避免N+1问题 -->
<select id="selectUsersWithOrdersOptimized" resultMap="UserOrderResultMap">
SELECT
u.id as user_id, u.username, u.email,
o.id as order_id, o.order_no, o.amount, o.status
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id IN
<foreach collection="userIds" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
7. MyBatis配置优化
7.1 核心配置优化
# mybatis配置优化
mybatis:
configuration:
# 开启缓存
cache-enabled: true
# 延迟加载
lazy-loading-enabled: true
aggressive-lazy-loading: false
# 执行器类型
default-executor-type: reuse
# 语句超时时间
default-statement-timeout: 25
# 默认获取数量
default-fetch-size: 100
# 本地缓存范围
local-cache-scope: session
# 自动映射行为
auto-mapping-behavior: partial
7.2 插件配置
@Configuration
public class MyBatisConfig {
// SQL性能分析插件
@Bean
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor interceptor = new PerformanceInterceptor();
interceptor.setMaxTime(1000); // 设置SQL最大执行时间
interceptor.setFormat(true); // 格式化SQL
return interceptor;
}
// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor interceptor = new PaginationInterceptor();
interceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return interceptor;
}
}
8. 性能监控与分析
8.1 SQL执行时间监控
@Component
public class PerformanceTestService {
public void performanceComparison() {
// 查询性能测试
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
userService.getUsersByAgeRange(20, 30);
}
long optimizedTime = System.currentTimeMillis() - startTime;
System.out.println("优化查询100次耗时: " + optimizedTime + "ms");
// 批量操作性能测试
startTime = System.currentTimeMillis();
performanceTestService.batchInsertBatch(users);
long batchTime = System.currentTimeMillis() - startTime;
System.out.println("批量插入耗时: " + batchTime + "ms");
}
}
8.2 性能指标监控
| 监控指标 | 正常范围 | 优化建议 |
|---|---|---|
| SQL执行时间 | < 100ms | 优化SQL、添加索引 |
| 连接池使用率 | < 80% | 调整连接池大小 |
| 缓存命中率 | > 80% | 优化缓存策略 |
| QPS | 根据业务 | 水平扩展、读写分离 |
9. 最佳实践
9.1 开发建议
性能优化最佳实践:索引设计:为经常查询的字段创建合适的索引SQL优化:避免SELECT *,使用具体字段名批量操作:大量数据操作时使用批量方法缓存策略:合理使用一级和二级缓存连接池配置:根据并发量调整连接池参数分页优化:避免深分页,使用游标分页延迟加载:合理配置关联查询的加载策略监控告警:建立性能监控和告警机制
9.2 性能调优流程
- 性能基准测试:建立性能基准线
- 瓶颈识别:使用工具识别性能瓶颈
- 优化实施:按优先级实施优化方案
- 效果验证:测试优化效果
- 持续监控:建立长期监控机制
10. 常见问题与解决方案
10.1 性能问题诊断
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 查询响应慢 | 缺少索引、SQL不优化 | 添加索引、优化SQL |
| 内存占用高 | 缓存配置不当 | 调整缓存大小和策略 |
| 连接池耗尽 | 连接泄露、配置不当 | 检查连接关闭、调整配置 |
| 批量操作慢 | 批次大小不当、无事务 | 调整批次大小、使用事务 |
10.2 优化注意事项
注意事项:性能优化要基于实际测试数据,不要盲目优化缓存使用要考虑数据一致性问题批量操作要控制批次大小,避免内存溢出索引不是越多越好,要考虑写入性能连接池配置要根据实际并发量调整
11. 实践练习
练习任务:配置Druid连接池并启用监控实现用户批量插入功能并测试性能配置MyBatis二级缓存优化用户查询SQL并添加索引实现延迟加载的用户订单关联查询编写性能测试代码对比优化效果
12. 运行示例
运行步骤:确保MySQL数据库已安装并运行创建数据库:CREATE DATABASE mybatis_performance;修改application.yml中的数据库连接信息运行项目:mvn spring-boot:run查看控制台输出的性能测试结果访问Druid监控页面:http://localhost:8080/performance-demo/druid/观察SQL执行情况和连接池状态
性能测试结果示例:=== MyBatis 性能优化演示 ===
- 批量操作性能对比
逐条插入1000条记录耗时: 2156ms
批量插入1000条记录耗时: 234ms
性能提升: 9.2倍- 缓存机制演示
首次查询耗时: 45ms
二次查询耗时: 2ms
缓存命中,性能提升: 22.5倍- SQL优化演示
优化后查询耗时: 12ms, 结果数量: 156
未优化查询耗时: 89ms, 结果数量: 156
SQL优化性能提升: 7.4倍
**下一章预告:**第17章将学习MyBatis的分布式应用,包括读写分离、分库分表等高级特性。
本章小结
📖 本章小结
本章我们学习了 MyBatis 性能优化 的相关内容,包括:
- 性能优化策略:从数据库、框架、应用、系统四个层面进行优化
- SQL优化技巧:索引优化、查询优化、分页查询优化
- 缓存机制:一级缓存、二级缓存、Spring Cache集成
- 批量操作:批量插入、批量更新、性能对比分析
- 连接池优化:Druid配置、监控设置
- 延迟加载:关联查询优化、避免N+1问题
- 配置优化:MyBatis核心配置、插件配置
- 性能监控:监控指标、性能分析
下一章节 🎉
恭喜完成第16章学习!
你已经掌握了 MyBatis 性能优化的核心知识
第17章
分布式应用
学习 MyBatis 在分布式环境下的应用,包括读写分离、分库分表等高级特性
⏱️ 预计 35 分钟📊 中等难度开始学习 → ← 第15章 MyBatis Plus 📚 课程目录 第17章 分布式应用 →