引言
在现代企业级应用开发中,性能优化是确保系统稳定运行和用户体验的关键因素。Spring Boot作为主流的Java应用框架,结合MyBatis Plus强大的数据库操作能力,为开发者提供了高效的开发体验。然而,在高并发、大数据量的场景下,如何优化Spring Boot + MyBatis Plus项目的性能成为了一个重要课题。
本文将从多个维度系统梳理Spring Boot + MyBatis Plus项目的性能优化路径,涵盖数据库连接池配置、SQL执行优化、二级缓存使用、分页查询优化等实用技巧,帮助开发者显著提升应用响应速度和系统整体性能。
一、数据库连接池优化
1.1 连接池选择与配置
在Spring Boot项目中,常用的数据库连接池有HikariCP、Druid、Tomcat JDBC Pool等。其中HikariCP作为当前最流行的连接池,以其高性能和低延迟著称。
# application.yml 配置示例
spring:
datasource:
hikari:
# 连接池名称
pool-name: MyHikariCP
# 最小空闲连接数
minimum-idle: 10
# 最大连接数
maximum-pool-size: 50
# 连接超时时间
connection-timeout: 30000
# 空闲连接超时时间
idle-timeout: 600000
# 连接生命周期
max-lifetime: 1800000
# 测试连接有效性
validation-timeout: 5000
# 自动提交
auto-commit: true
1.2 连接池参数调优
合理的连接池配置对系统性能至关重要。以下是一些关键参数的调优建议:
- minimum-idle:设置为CPU核心数的2倍,确保有足够的空闲连接
- maximum-pool-size:根据数据库最大连接数和应用并发需求设置,一般建议设置为100-200
- connection-timeout:设置为30秒,避免长时间等待连接
- idle-timeout:设置为5-10分钟,及时回收空闲连接
// 连接池监控配置
@Component
public class DataSourceMonitor {
@Autowired
private HikariDataSource dataSource;
@Scheduled(fixedRate = 60000)
public void monitorPool() {
HikariPoolMXBean poolBean = dataSource.getHikariPoolMXBean();
System.out.println("Active connections: " + poolBean.getActiveConnections());
System.out.println("Idle connections: " + poolBean.getIdleConnections());
System.out.println("Total connections: " + poolBean.getTotalConnections());
}
}
二、SQL执行优化
2.1 SQL语句优化原则
基础查询优化
// ❌ 不推荐:全表扫描
@Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')")
List<User> selectUsersByName(String name);
// ✅ 推荐:使用索引优化
@Select("SELECT id, name, email FROM user WHERE name = #{name}")
User selectUserByName(String name);
复杂查询优化
// 使用MyBatis Plus的条件构造器进行优化
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> queryUsers(UserQueryDTO query) {
// 使用LambdaQueryWrapper避免SQL注入
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 精确查询
if (StringUtils.isNotBlank(query.getName())) {
wrapper.eq(User::getName, query.getName());
}
// 范围查询
if (query.getAgeFrom() != null) {
wrapper.ge(User::getAge, query.getAgeFrom());
}
// 排序和分页
wrapper.orderByDesc(User::getCreateTime);
return userMapper.selectList(wrapper);
}
}
2.2 批量操作优化
// 批量插入优化
@Service
public class BatchUserService {
@Autowired
private UserMapper userMapper;
// 使用批量插入提高效率
public void batchInsertUsers(List<User> users) {
// 方式1:使用MyBatis Plus的批量插入
userMapper.insertBatchSomeColumn(users);
// 方式2:自定义SQL批量插入
userMapper.batchInsert(users);
}
// 批量更新优化
public void batchUpdateUsers(List<User> users) {
userMapper.updateBatchById(users);
}
}
2.3 查询结果集优化
// 使用DTO映射减少不必要的字段传输
public class UserQueryDTO {
private String name;
private Integer age;
private Date createTime;
// getter/setter方法
}
// Mapper接口定义
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 查询特定字段
@Select("SELECT id, name, email FROM user WHERE status = 1")
List<UserDTO> selectActiveUsers();
// 使用XML配置复杂查询
List<UserDTO> selectUserWithDept(@Param("userId") Long userId);
}
三、二级缓存策略
3.1 MyBatis二级缓存基础配置
# application.yml
mybatis-plus:
configuration:
# 开启二级缓存
cache-enabled: true
# 开启本地缓存
local-cache-size: 1000
# 懒加载开关
lazy-loading-enabled: true
# 延迟加载的代理方式
aggressive-lazy-loading: false
3.2 实现自定义缓存策略
// 自定义缓存注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
String key() default "";
int expireTime() default 300; // 缓存过期时间(秒)
}
// 缓存管理器
@Component
public class CacheManager {
private final RedisTemplate<String, Object> redisTemplate;
public CacheManager(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public <T> T getCache(String key, Class<T> clazz) {
Object value = redisTemplate.opsForValue().get(key);
return value != null ? (T) value : null;
}
public void putCache(String key, Object value, int expireTime) {
redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
}
public void evictCache(String key) {
redisTemplate.delete(key);
}
}
// 缓存切面实现
@Aspect
@Component
public class CacheAspect {
@Autowired
private CacheManager cacheManager;
@Around("@annotation(cacheable)")
public Object around(ProceedingJoinPoint joinPoint, Cacheable cacheable) throws Throwable {
String key = generateKey(joinPoint, cacheable);
// 先从缓存获取
Object result = cacheManager.getCache(key, Object.class);
if (result != null) {
return result;
}
// 缓存未命中,执行方法
result = joinPoint.proceed();
// 将结果放入缓存
cacheManager.putCache(key, result, cacheable.expireTime());
return result;
}
private String generateKey(ProceedingJoinPoint joinPoint, Cacheable cacheable) {
String key = cacheable.key();
if (StringUtils.isBlank(key)) {
Object[] args = joinPoint.getArgs();
key = joinPoint.getSignature().toString() + Arrays.toString(args);
}
return "cache:" + key;
}
}
3.3 实体类缓存策略
// User实体类配置二级缓存
@CacheNamespace(
implementation = MyBatisRedisCache.class,
size = 1000,
flushInterval = 60000,
readOnly = true,
blocking = true
)
public class User {
private Long id;
private String name;
private String email;
private Integer age;
// getter/setter方法
}
// 自定义缓存实现
public class MyBatisRedisCache implements Cache {
private final String id;
private final RedisTemplate<String, Object> redisTemplate;
public MyBatisRedisCache(String id) {
this.id = id;
this.redisTemplate = SpringContextUtil.getBean(RedisTemplate.class);
}
@Override
public String getId() {
return id;
}
@Override
public void putObject(Object key, Object value) {
redisTemplate.opsForValue().set(getKey(key), value, 30, TimeUnit.MINUTES);
}
@Override
public Object getObject(Object key) {
return redisTemplate.opsForValue().get(getKey(key));
}
@Override
public Object removeObject(Object key) {
Object result = getObject(key);
redisTemplate.delete(getKey(key));
return result;
}
@Override
public void clear() {
redisTemplate.delete(id + ":*");
}
@Override
public int getSize() {
return 0;
}
private String getKey(Object key) {
return id + ":" + key.toString();
}
}
四、分页查询优化
4.1 分页插件配置
# MyBatis Plus分页插件配置
mybatis-plus:
configuration:
# 开启分页插件
pagination: true
global-config:
db-config:
# 主键策略
id-type: auto
page:
# 分页大小限制
max-size: 1000
4.2 高效分页查询实现
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 优化的分页查询
public IPage<UserDTO> queryUsersWithPage(UserQueryDTO query, Page<User> page) {
// 构建查询条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(query.getName())) {
wrapper.like(User::getName, query.getName());
}
if (query.getAgeFrom() != null) {
wrapper.ge(User::getAge, query.getAgeFrom());
}
// 添加排序
wrapper.orderByDesc(User::getCreateTime);
// 执行分页查询
IPage<User> userPage = userMapper.selectPage(page, wrapper);
// 转换为DTO
List<UserDTO> dtoList = userPage.getRecords().stream()
.map(this::convertToDTO)
.collect(Collectors.toList());
// 构造返回结果
IPage<UserDTO> result = new Page<>();
result.setRecords(dtoList);
result.setTotal(userPage.getTotal());
result.setSize(userPage.getSize());
result.setCurrent(userPage.getCurrent());
return result;
}
// 批量查询优化
public List<UserDTO> queryUsersBatch(Long[] userIds) {
if (CollectionUtils.isEmpty(Arrays.asList(userIds))) {
return Collections.emptyList();
}
// 使用IN查询优化
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(User::getId, Arrays.asList(userIds));
wrapper.orderByDesc(User::getCreateTime);
List<User> users = userMapper.selectList(wrapper);
return users.stream().map(this::convertToDTO).collect(Collectors.toList());
}
private UserDTO convertToDTO(User user) {
UserDTO dto = new UserDTO();
BeanUtils.copyProperties(user, dto);
return dto;
}
}
4.3 大数据量分页优化策略
// 流式分页查询实现
@Service
public class LargeDataUserService {
@Autowired
private UserMapper userMapper;
// 分批处理大数据量查询
public void processLargeUserList() {
int pageSize = 1000;
int currentPage = 1;
while (true) {
Page<User> page = new Page<>(currentPage, pageSize);
IPage<User> userPage = userMapper.selectPage(page, null);
List<User> users = userPage.getRecords();
if (CollectionUtils.isEmpty(users)) {
break;
}
// 处理当前批次数据
processBatchUsers(users);
if (users.size() < pageSize) {
break; // 最后一批数据
}
currentPage++;
}
}
private void processBatchUsers(List<User> users) {
// 批量处理逻辑
users.forEach(user -> {
// 处理单个用户
System.out.println("Processing user: " + user.getName());
});
}
}
五、JVM调优策略
5.1 JVM参数优化配置
# 启动脚本中的JVM参数优化
JAVA_OPTS="-server \
-Xms2g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+UseStringDeduplication \
-XX:+UseCompressedOops \
-Djava.awt.headless=true \
-Dfile.encoding=UTF-8"
# 应用启动命令
nohup java $JAVA_OPTS -jar app.jar > app.log 2>&1 &
5.2 内存监控与调优
// JVM内存监控工具类
@Component
public class MemoryMonitor {
private final MeterRegistry meterRegistry;
public MemoryMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
registerMetrics();
}
private void registerMetrics() {
// 注册内存使用率指标
Gauge.builder("jvm.memory.used")
.description("JVM memory used")
.register(meterRegistry, this, monitor ->
getMemoryUsage().getUsed());
Gauge.builder("jvm.memory.max")
.description("JVM memory max")
.register(meterRegistry, this, monitor ->
getMemoryUsage().getMax());
}
private MemoryUsage getMemoryUsage() {
return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
}
}
5.3 垃圾回收优化
# GC优化配置
spring:
jvm:
gc:
enabled: true
log:
level: INFO
options:
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+UseStringDeduplication
-XX:+UseCompressedOops
六、数据库索引优化
6.1 索引设计原则
-- 创建高效索引的示例
-- 用户表创建复合索引
CREATE INDEX idx_user_name_age ON user(name, age);
CREATE INDEX idx_user_email_status ON user(email, status);
-- 复合索引使用优化
SELECT * FROM user WHERE name = 'John' AND age > 25;
-- 这个查询可以有效利用 idx_user_name_age 索引
6.2 索引监控与分析
// 索引使用情况监控
@Service
public class IndexMonitorService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void analyzeIndexUsage() {
String sql = """
SELECT
table_name,
index_name,
rows_selected,
rows_inserted,
rows_updated,
rows_deleted
FROM performance_schema.table_statistics
WHERE table_name = 'user'
""";
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql);
results.forEach(System.out::println);
}
// 查询慢SQL分析
public void analyzeSlowQueries() {
String sql = """
SELECT
DIGEST_TEXT,
COUNT_STAR,
AVG_TIMER_WAIT,
SUM_ROWS_EXAMINED
FROM performance_schema.events_statements_summary_by_digest
WHERE AVG_TIMER_WAIT > 1000000000000
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10
""";
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql);
results.forEach(System.out::println);
}
}
七、缓存穿透与雪崩防护
7.1 缓存穿透防护
// 缓存空值防止穿透
@Service
public class UserService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public User getUserById(Long id) {
String key = "user:" + id;
// 先从缓存获取
Object cachedUser = redisTemplate.opsForValue().get(key);
if (cachedUser != null) {
return (User) cachedUser;
}
// 缓存未命中,查询数据库
User user = userMapper.selectById(id);
if (user == null) {
// 缓存空值,防止缓存穿透
redisTemplate.opsForValue().set(key, "", 300, TimeUnit.SECONDS);
return null;
}
// 缓存用户数据
redisTemplate.opsForValue().set(key, user, 3600, TimeUnit.SECONDS);
return user;
}
}
7.2 缓存雪崩防护
// 缓存随机过期时间防止雪崩
@Component
public class CacheProtectionService {
private final RedisTemplate<String, Object> redisTemplate;
public CacheProtectionService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void setCacheWithRandomExpire(String key, Object value, int baseExpireTime) {
// 添加随机时间防止集中过期
Random random = new Random();
int randomTime = random.nextInt(300) + 1; // 1-300秒随机值
int expireTime = baseExpireTime + randomTime;
redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
}
// 使用分布式锁防止缓存击穿
public User getUserWithLock(Long id) {
String key = "user:" + id;
String lockKey = "lock:user:" + id;
try {
// 获取分布式锁
if (redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS)) {
// 获取缓存
Object cachedUser = redisTemplate.opsForValue().get(key);
if (cachedUser != null) {
return (User) cachedUser;
}
// 缓存未命中,查询数据库
User user = userMapper.selectById(id);
if (user != null) {
redisTemplate.opsForValue().set(key, user, 3600, TimeUnit.SECONDS);
}
return user;
} else {
// 等待一段时间后重试
Thread.sleep(100);
return getUserWithLock(id);
}
} catch (Exception e) {
throw new RuntimeException("获取用户信息失败", e);
} finally {
// 释放锁
redisTemplate.delete(lockKey);
}
}
}
八、性能监控与调优工具
8.1 Prometheus + Grafana 监控配置
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
8.2 自定义监控指标
@Component
public class PerformanceMetrics {
private final MeterRegistry meterRegistry;
private final Counter requestCounter;
private final Timer responseTimer;
private final Gauge databaseConnectionGauge;
public PerformanceMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
// 请求计数器
this.requestCounter = Counter.builder("http.requests")
.description("HTTP request count")
.register(meterRegistry);
// 响应时间计时器
this.responseTimer = Timer.builder("http.response.time")
.description("HTTP response time")
.register(meterRegistry);
// 数据库连接数监控
this.databaseConnectionGauge = Gauge.builder("database.connections")
.description("Database connections count")
.register(meterRegistry, this, monitor ->
getDatabaseConnectionCount());
}
public void recordRequest(String method, String uri) {
requestCounter.increment();
}
public Timer.Sample startTimer() {
return Timer.start(meterRegistry);
}
private int getDatabaseConnectionCount() {
// 实现获取数据库连接数的逻辑
return 0;
}
}
结论
通过本文的详细介绍,我们可以看到Spring Boot + MyBatis Plus项目的性能优化是一个系统工程,需要从多个维度进行综合考虑和调优。合理的数据库连接池配置、高效的SQL执行优化、智能的缓存策略、以及完善的监控体系,都是提升应用性能的关键因素。
在实际项目中,建议根据具体的业务场景和性能瓶颈,有针对性地选择和实施这些优化策略。同时,建立完善的监控机制,持续跟踪系统性能指标,及时发现和解决潜在问题,才能确保系统的长期稳定运行。
性能优化是一个持续的过程,需要开发团队不断学习、实践和总结经验。希望本文提供的最佳实践能够帮助开发者构建更加高效、稳定的Spring Boot应用。

评论 (0)