SpringBoot 的這些默認配置不改,100%會踩坑!
彼時 SpringBoot 初興,萬象更新,號稱“開箱即用”“約定優于配置”,一時間風靡四方。
開發者趨之若鶩,紛紛稱快,仿佛自此架構之重可卸、配置之繁可省,一行 main() 即可氣定神閑、縱橫沙場。
然則時光久遠,方知此言非虛,卻也未盡其真。所謂默認,不過是你未曾開口,框架自作主張。表面無礙,實則步步殺機,線上事故十有八九,皆因“未曾配置”的“默認”。
回首往昔,實堪自嘲。曾自詡熟稔底層、精通原理,然于這些藏于陰影處的默認設定,竟茫然不覺。故障一起,倉皇失措,耗時良久,方才發現,不過是框架做了一個并不適合的決定。
是以今日提筆,將過往種種記錄于此,只盼后來者少走彎路。
Tomcat連接池
SpringBoot默認使用Tomcat作為Web容器,但默認的連接池配置在高并發場景下會成為瓶頸。
默認配置下,Tomcat的最大連接數只有200,最大線程數也只有200。這意味著當并發請求超過200時,后續請求就會排隊等待。在生產環境中,這個配置明顯不夠用。
server:
tomcat:
max-connections: 10000 # 最大連接數
threads:
max: 800 # 最大工作線程數
min-spare: 100 # 最小空閑線程數
accept-count: 100 # 等待隊列長度
connection-timeout: 20000更坑的是,SpringBoot的默認超時時間是無限長。這會導致連接一直占用,直到客戶端主動斷開。
在網絡不穩定的環境下,大量連接會一直掛著不釋放,最終耗盡服務器資源。
數據庫連接池
SpringBoot默認使用HikariCP作為數據庫連接池,但默認的連接池配置在生產環境下會成為瓶頸。默認最大連接數只有10個,對于稍微復雜一點的應用來說根本不夠用。
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000特別要注意leak-detection-threshold這個配置。默認情況下這個檢測是關閉的,如果代碼中存在連接泄漏問題,根本發現不了。
開啟后,HikariCP會監控連接的使用時間,超過閾值就會打印警告日志。
JPA懶加載
SpringBoot集成JPA時,默認開啟了懶加載。這個設計初衷是好的,但在實際使用中經常會導致N+1查詢問題。
@Entity
public class User {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY) // 默認就是LAZY
private List<Order> orders;
}當查詢用戶列表時,每訪問一次orders屬性,就會觸發一次數據庫查詢。
如果有100個用戶,就會執行101次SQL。
這種情況下,要么使用@EntityGraph指定加載策略,要么在Repository中使用JOIN FETCH。
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();Jackson時區序列化
SpringBoot默認使用Jackson處理JSON序列化,但時區處理經常出問題。
默認情況下,Jackson會使用系統時區,這在分布式部署時會導致不一致的問題。
spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
serialization:
write-dates-as-timestamps: false更要命的是,如果你的應用部署在不同時區的服務器上,同樣的時間可能會被序列化成不同的值。
這個問題在國際化應用中特別突出。
日志配置
SpringBoot默認使用Logback,但默認配置下沒有對日志文件進行滾動和清理。
長時間運行的應用會產生巨大的日志文件,最終占滿磁盤空間。
logging:
file:
name: app.log
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 30
total-size-cap: 3GB另外,默認的日志級別是INFO,在生產環境中會產生大量不必要的日志。
合理設置日志級別可以顯著提升性能。
緩存配置
SpringBoot的@Cacheable注解默認使用ConcurrentHashMap作為緩存實現,但這個實現沒有過期機制,也沒有大小限制。在高并發場景下,緩存會無限增長,最終導致內存溢出。
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=10000,expireAfterWrite=600s可以考慮使用Caffeine替代默認實現,可以提供更好的性能和內存管理能力。
監控端點
監控端點
SpringBoot Actuator默認暴露了很多監控端點,包括健康檢查、配置信息、環境變量等。
這些信息在開發環境中很有用,但在生產環境中可能泄漏敏感信息。
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: when-authorized只暴露必要的端點,并且配置適當的安全策略,避免信息泄漏。
文件上傳大小限制
SpringBoot默認的文件上傳限制非常小,單個文件只能上傳1MB,整個請求大小限制10MB。
在實際業務中,這個限制經常不夠用,用戶上傳稍大一點的文件就會報錯。
這個屬于是比較常見的問題,因為開發環境測試時通常用小文件,一切正常。等到用戶上傳幾MB的PDF文檔或者高清圖片時,系統就開始報 MaxUploadSizeExceededException 異常。
這個異常往往還發生在文件傳輸完成之后,用戶等了半天上傳完畢,結果被告知文件過大,體驗極差。
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
file-size-threshold: 2KB
location: /tmp
resolve-lazily: falsefile-size-threshold 這個參數也很重要,它決定了多大的文件會直接寫入內存。如果設置過大,大量并發上傳會占用過多內存;設置過小,小文件也要寫磁盤,影響性能。一般設置為幾KB比較合適。
異步線程池配置
使用@Async注解時,SpringBoot默認使用SimpleAsyncTaskExecutor,這個執行器每次都會創建新線程,沒有線程池復用機制。高并發情況下會創建大量線程,最終導致系統資源耗盡。
這個問題在開發階段很難發現,因為異步任務通常不多。但在生產環境,如果有大量異步任務執行,比如發送短信、推送、記錄日志等,系統會不斷創建新線程。每個線程默認占用1MB的??臻g,創建幾千個線程就是幾GB內存。
更嚴重的是線程切換的開銷,CPU大部分時間都在做上下文切換,真正的業務邏輯反而執行很慢。
spring:
task:
execution:
pool:
core-size:8
max-size:16
queue-capacity:100
keep-alive:60s
thread-name-prefix:async-task-
scheduling:
pool:
size:4
thread-name-prefix: scheduling-線程池大小的設置也有講究。
如果是CPU密集型任務,線程數設置為CPU核心數就夠了;如果是IO密集型任務,可以設置為CPU核心數的2-3倍。
queue-capacity設置了任務隊列長度,當線程池滿了之后,新任務會放到隊列里等待執行。
靜態資源緩存策略
SpringBoot默認不為靜態資源設置HTTP緩存頭,這意味著瀏覽器每次都會重新請求CSS、JS、圖片等靜態文件,嚴重影響頁面加載性能。
用戶每次訪問頁面,瀏覽器都要重新下載所有靜態資源,即使這些文件根本沒有變化。對于資源較多的單頁應用來說,這個問題特別明顯。用戶看到的就是頁面加載慢,特別是網絡條件不好的時候,體驗很差。
spring:
web:
resources:
cache:
cachecontrol:
max-age:365d
cache-public:true
chain:
strategy:
content:
enabled:true
paths:/**
cache:true
static-locations: classpath:/static/開啟內容版本化策略后,SpringBoot會根據文件內容生成MD5哈希值作為版本號,文件名變成style-abc123.css這樣的格式。當文件內容發生變化時,哈希值也會變化,瀏覽器會認為這是新文件重新下載;如果文件沒變化,瀏覽器就直接使用緩存,有效提升頁面加載速度。
數據庫事務超時
@Transactional注解默認沒有設置超時時間,長時間運行的事務會一直持有數據庫鎖,影響其他操作的執行。特別是在批量數據處理時,很容易出現鎖表問題。
這個問題在并發量不高的時候不明顯,但隨著業務增長,長事務的危害就暴露出來了。
比如一個數據導入任務需要處理幾萬條記錄,如果放在一個事務里,可能要運行幾分鐘甚至更長時間。在這期間,相關的表都被鎖住,其他用戶的操作只能等待,系統響應變得很慢。
@Transactional(timeout = 30, rollbackFor = Exception.class)
publicvoidbatchProcess(List<Data> dataList) {
// 分批處理,避免長事務
intbatchSize=100;
for (inti=0; i < dataList.size(); i += batchSize) {
List<Data> batch = dataList.subList(i,
Math.min(i + batchSize, dataList.size()));
processBatch(batch);
}
}對于大批量數據處理,建議分成多個小事務,每個事務處理少量數據。這樣即使某個小事務失敗,也不會影響整體進度,而且可以及時釋放數據庫鎖,提高系統并發性能。
同時再加上rollbackFor = Exception.class確保所有異常都會觸發回滾,避免數據不一致。
寫在最后
Spring Boot 的“約定優于配置”確實省心,但省的是開發者的心,不是系統的責任。每一項默認配置背后,其實都藏著設計者的假設和權衡,而這些假設,在我們的業務場景中也許未必成立。
這些坑我幾乎都踩過,有些甚至反復踩了好幾次。
愿你讀到這里,能少走幾步彎路,可不能拿生產事故去交學費。
提前優化配置,是對系統負責,也是對自己負責。





























