許多開發者認為使用 PreparedStatement 總能提升性能,但在 SQL Server 環境下,若不當設置連接屬性,字符串參數查詢可能出現嚴重性能退化——百萬級數據量下簡單查詢耗時 6 秒以上,而同樣條件下整型參數僅需毫秒級。本文記錄一次真實排查過程,揭示根因並給出解決方案。
connection.setAutoCommit(false);
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setFetchSize(100);
pstmt.setString(1, "026011009004"); // 字符串參數
ResultSet rs = pstmt.executeQuery(); // 耗時 6 秒+
connection.commit();
setInt() 即恢復正常。查閱 SQL Server JDBC 驅動文檔發現關鍵連接屬性:
SendStringParametersAsUnicode(默認
true)
決定字符串參數以 Unicode 還是數據庫默認字符編碼發送給服務器。
true:發送 Unicode,服務器需隱式轉換為列編碼(如中文環境下的 GBK/CP936),導致索引失效、全表掃描。
false:直接按數據庫默認編碼發送,避免轉換,性能大幅提升。
在建立連接時顯式設置該屬性為 false:
// 方式一:連接 URL 參數
String url = "jdbc:sqlserver://host:1433;databaseName=xxx;SendStringParametersAsUnicode=false";
// 方式二:Properties 設置
Properties props = new Properties();
props.setProperty("user", "sa");
props.setProperty("password", "pwd");
props.setProperty("SendStringParametersAsUnicode", "false");
Connection conn = DriverManager.getConnection(url, props);
效果驗證:同一查詢從 6 秒降至 16 毫秒,性能提升近 400 倍。
false。PreparedStatement 防注入、利用計劃緩存,只需加上這個屬性。當 SendStringParametersAsUnicode=true 時,驅動將參數作為 NVARCHAR 傳遞,SQL Server 需將列值隱式轉換為 NVARCHAR 才能比較,導致 CONVERT_IMPLICIT 操作作用在列上,破壞 SARGability(搜索參數有效性),引擎無法使用索引定位,只能全表掃描。設置 false 後參數作為 VARCHAR 傳遞,類型匹配,索引生效。
這個「隱形殺手」在中文、日文等非 Unicode 默認編碼環境中尤為常見,排查時務必檢查驅動文檔中的字符編碼相關屬性。