同時註冊兩個用戶,第一個成功,第二個無反應/失敗。錯誤日志顯示數據庫連接已關閉。
// 典型錯誤模式
public void register(User user) {
Connection conn = getConnection(); // 獲取連接
try {
// ... 執行 SQL 插入 ...
conn.close(); // ❌ 在方法內關閉連接!
} catch (SQLException e) {
e.printStackTrace();
}
}
// 併發調用時:
// 線程1: getConnection() → 執行 → close() 連接池歸還/關閉
// 線程2: getConnection() → 可能獲取到已關閉的連接 → 執行失敗
close() 連接,導致連接池異常或連接提前歸還
@Service
@Transactional // 方法級事務,Spring 自動管理連接獲取/提交/關閉
public class UserService {
@Autowired
private UserMapper userMapper; // MyBatis Mapper
public void register(User user) {
// 無需手動獲取/關閉連接
userMapper.insert(user);
// 異常拋出自動回滾,正常自動提交並歸還連接
}
}
public void register(User user) {
Connection conn = null;
try {
conn = dataSource.getConnection(); // 從池獲取
conn.setAutoCommit(false); // 開啟事務
// 業務操作
userDao.insert(conn, user);
conn.commit(); // 提交事務
} catch (SQLException e) {
if (conn != null) try { conn.rollback(); } catch (SQLException ex) {}
throw new RuntimeException("註冊失敗", e);
} finally {
if (conn != null) try { conn.close(); } catch (SQLException e) {} // 歸還池
}
}
# HikariCP 推薦配置
spring.datasource.hikari:
maximum-pool-size: 20 # 最大連接數
minimum-idle: 5 # 最小空閒
connection-timeout: 30000 # 獲取連接超時 30s
idle-timeout: 600000 # 空閒超時 10min
max-lifetime: 1800000 # 最大壽命 30min
connection-test-query: SELECT 1 # 心跳檢查
validation-timeout: 5000 # 驗證超時
| 問題 | 現象 | 解決 |
|---|---|---|
| 唯一索引衝突 | Duplicate entry 'username' for key 'uk_username' | 捕獲异常返回友好提示、或 INSERT IGNORE/ON DUPLICATE KEY |
| 事務隔離級別 | 幻讀導致重複檢查失效 | REPEATABLE READ + 唯一索引保證 |
| 連接泄漏 | 池耗盡、後續請求卡死 | finally 必須 close、啟用泄漏檢測 |