可配置重試邏輯(CRL)是一種基於規則的機制,能根據你選擇的 SQL Server 錯誤編號,並以你控制的時間參數,自動重試失敗的語句或初始連線嘗試。 CRL 於 Microsoft JDBC 驅動程式 12.10 for SQL Server 中引入。
CRL 與 閒置連線彈性及 connectRetryCount / connectRetryInterval 屬性是分開的。 閒置韌性透明地恢復中斷的連線,並 connectRetryCount 依固定排程重試初始驗證,針對內建的暫態錯誤清單。 CRL 讓你決定 哪些 錯誤可以重試、重複多少 次,以及每次嘗試之間要 等多久 。 你可以同時使用這三種機制。
CRL 重試的內容
CRL 處理兩種不同的情境,每種情境皆由其獨立的連接特性控制:
| Scenario | 房產 | 當重試執行時 | 觸發方式 |
|---|---|---|---|
| 陳述句執行失敗 | retryExec |
執行陳述式時(例如 executeQuery、executeUpdate、execute 或批次執行) |
一個 SQLServerException 錯誤編號與已設定語句規則相符的 |
| 初始連線或認證失敗 | retryConn |
在驅動程式的連線重試迴圈內(該迴圈受 connectRetryCount 和 loginTimeout 控制) |
一個 SQLServerException,在驗證期間發生,其錯誤編號符合已設定的連線規則,或在預設情況下,屬於內建重試清單已涵蓋的任何暫時性錯誤 |
對於語句,驅動程式只重試失敗的指令。 驅動程式不會重置目前的交易狀態,所以規則要圍繞那些讓會話仍可用的錯誤來設計,例如死鎖受害者(1205)或鎖定逾時(1222)。
對於連線,CRL 會擴充或取代驅動程式內建的暫態連線錯誤清單。 請參見 連線重試規則 以了解 + 前綴語意。
啟用 CRL
CRL 有兩層:
- 連線重試層預設開啟:只要(
connectRetryCount > 0預設值為 1),驅動 程式會重試內建的暫態連線錯誤清單。 - 自訂層(你自己的
retryExec和retryConn規則)預設為停用。 除非你設定它們,否則這兩個屬性都是空字串。 你可以透過 JDBC URL、Properties物件或SQLServerDataSource來設定它們。 駕駛員會將三種形式的可選{...}包裝件剝離。
本文中的 Java 片段為了簡潔起見,省略了匯入和類別包裝器。
在 JDBC URL 中
每條規則(或整個規則清單)都必須以大括號({...})括住,因為 JDBC URL 使用 ; 作為分隔符:
jdbc:sqlserver://server;databaseName=db;retryExec={1205,1222:3,2*2:select,update}
jdbc:sqlserver://server;databaseName=db;retryConn={+<customErrorNumber>}
使用 Properties 物件
Properties props = new Properties();
props.setProperty("user", "...");
props.setProperty("password", "...");
props.setProperty("retryExec", "1205,1222:3,2*2:select,update");
props.setProperty("retryConn", "+<customErrorNumber>");
Connection c = DriverManager.getConnection("jdbc:sqlserver://server;databaseName=db", props);
使用 SQLServerDataSource
ISQLServerDataSource 介面上也有相同的設定器:
SQLServerDataSource ds = new SQLServerDataSource();
ds.setServerName("server");
ds.setDatabaseName("db");
ds.setRetryExec("1205,1222:3,2*2:select,update");
ds.setRetryConn("+<customErrorNumber>");
規則語法
一條規則最多包含三個冒號分隔區段:
<errorNumbers> : <retryTimings> : <queryFilter>
| 章節 | Required? | Meaning |
|---|---|---|
errorNumbers |
Yes | 一個 SQL Server 錯誤編號,或多個以逗號分隔的錯誤號碼(例如 1205 ,或 1205,1222)。 對於連線規則,前置的選用 `+` 會控制是否保留現有的暫態錯誤。 |
retryTimings |
陳述式規則所必需。 連線規則略略。 |
retryCount[,initialRetryTime[<op>retryChange]] 其中 <op> 是 + (加法)或 * (乘法)。 |
queryFilter |
可選,僅限陳述規則 | 逗號分隔的 SQL 關鍵字列表。 驅動程式會在解析時將該值轉為小寫,並在執行階段將先前執行的 SQL 轉為小寫。 當加入的過濾清單包含已執行 SQL 的第一個標記時,規則就會觸發。 省略第三節以關閉過濾功能。 |
若要在同一屬性中使用多個規則,將其放入 JDBC URL 時,請以 ; 分隔,並以 {...} 包住每個規則。
時序參數
對於具有時間設定的陳述規則 retryCount, initialRetryTime <op> retryChange:
-
retryCount:駕駛員在第一次失敗後所進行的額外嘗試次數。 值為0時,會停用重試。 負值則無效。 -
initialRetryTime:第一次重試前等待的秒數。 預設值為0。 -
<op>:算符,可以是+或*。 預設值為+。 -
retryChange:計算後續等待時間所應用的金額。 預設值為2。 當運算元為*,且規則中省略了retryChange時,驅動程式會設定retryChange = initialRetryTime。
Important
如果您提供 initialRetryTime 但未明確指定運算元(例如 3,5),驅動程式會使用該運算元和 retryChange 的預設值(+ 和 2)。 等待時間並不固定。 每次重試都會增加 2。 若要設定固定的等待時間,請使用顯式形式 retryCount,N+0(例如 3,5+0)。
驅動程式會在解析時,計算第 i 次嘗試(從 0 開始計數)的等待時間:
| 運算元 | 等待第 i 次嘗試 |
|---|---|
+ (加法) |
initialRetryTime + (retryChange * i) |
* (乘法) |
initialRetryTime * (retryChange ^ i) |
定時串的範例:
| String | retryCount | 初始重試時間 | 運算元 | retryChange | 等待順序(秒) |
|---|---|---|---|---|---|
3 |
3 | 0 (預設值) |
+ (預設值) |
2(預設) | 0, 2, 4 |
3,5 |
3 | 5 |
+ (預設值) |
2(預設) | 5, 7, 9 |
3,5+5 |
3 | 5 | + |
5 | 5, 10, 15 |
3,2*2 |
3 | 2 | * |
2 | 2, 4, 8 |
4,1* |
4 | 1 | * |
1(等於initialRetryTime,因為運算元為*,且省略了retryChange) |
1, 1, 1, 1 |
一個 retryTimings 章節最多只能包含一個逗號。 超過一個逗號會引發 R_invalidParameterNumber。
語句重試規則(retryExec)
語句規則會重試失敗的語句執行。 當陳述式擲回 SQLServerException 時,驅動程式:
- 查詢解析後語句規則集中的失敗錯誤編號。
- 如果規則存在且目前嘗試次數小
retryCount於 ,則會選擇性地將最後執行的 SQL 與該規則的queryFilter進行檢查。 - 如果所有條件都吻合,驅動程式會
waitTimes[retryAttempt]等待幾秒鐘(但受queryTimeout條件限制,請參見與 queryTimeout 及 connectRetryCount 的互動)並重新執行該語句。 - 若沒有任何規則符合,驅動程式會再次拋出例外。
格式(陳述式)
{errorNumber(s):retryCount[,initialRetryTime[<op>retryChange]][:queryFilter]}
語句規則 必須 包含計時部分。
retryCount 屬於必要項目。 僅包含錯誤數的規則被解釋為 連接 規則,因此對於陳述,總是至少提供 retryCount。
範例(陳述)
| 規則 | 影響 |
|---|---|
{1205:3} |
重試死關受害者(1205分)最多三次,且重試間無等待。 |
{1205,1222:3,5+5} |
重試死鎖受害者和鎖定超時最多3次,等待5、10和15秒。 |
{2714:2,1*2} |
重複「物件已存在」最多兩次,等待1秒和2秒。 |
{1205:4,2+2:select,update} |
只有當失敗語句以 select 或 update開頭時才重試。 |
{1205:3,5+5};{1222:2,2} |
兩條獨立規則,以 ; 分隔。 |
列出數個錯誤代碼(例如 1205,1222)是一種簡寫方式。 驅動程式會將規則展開,為每個錯誤建立一個項目,且全部共用相同的時間設定與查詢篩選條件。
連線重試規則(retryConn)
連線規則可與現有的連接-重試迴路相配合。 此迴圈僅在 ( connectRetryCount > 0 預設為 1)時才會啟動。 迴圈已會針對內建的暫時性連線錯誤清單進行重試,每隔 connectRetryInterval 秒一次,最多額外重試 connectRetryCount 次,並受限於 loginTimeout。
連線規則僅提供錯誤編號區塊。 它沒有時間或查詢篩選器:
{[+]errorNumber(s)}
- 若沒有
+,配置規則 會取代 內建的暫態錯誤清單。 只有你列出的錯誤會重試。 - 使用
+(例如{+4060})時,已設定的規則會新增至內建清單。 您的錯誤和驅動程式預設值都會重試。
替換或附加模式會套用至整個 retryConn 值。 若該值中有任何規則遺 +漏,驅動程式將切換為替換該值中所有規則的模式。 例如, retryConn={+4060};{40143} 不會將 4060 和 40143 附加到內建清單中。
40143 規則省略了 +,因此內建清單被捨棄,且只會重試 4060 和 40143。 若要同時加上兩者,請寫 retryConn={+4060};{+40143} (或 retryConn={+4060,40143})。
連接迴圈繼續使用 connectRetryInterval 和 connectRetryCount 來控制節奏並設定界限。 CRL 規則會擴充或替換可重試的錯誤集合。
範例(連接)
| 規則 | 影響 |
|---|---|
{+<customErrorNumber>} |
在內建的瞬態錯誤清單中新增自訂錯誤編號。 |
{+<customError1>,<customError2>} |
在內建的瞬態錯誤清單中加入多個自訂錯誤號碼。 |
{4060} |
僅重試 4060 錯誤。 內建的瞬態錯誤不再由 CRL 重試。 |
Note
retryConn 語意不會改變 loginTimeout 。 現有的連接-重試迴圈仍限制總經過時間,若下一個 connectRetryInterval 迴圈會將經過時間推過 loginTimeout,則會提前放棄。
內建暫態連線錯誤清單
connect-retry 迴圈即使在沒有 CRL 設定的情況下,也會重試下列錯誤,只要 connectRetryCount > 0。 在含有 + 的 retryConn 規則中列出這些錯誤不會有任何效果(因為這些錯誤已涵蓋)。 當你需要新增清單中沒有列出的錯誤時,請使用 retryConn 規則;或者,當你需要透過 no-+ replace 形式完全不使用該清單時,也請使用 retryConn 規則。
Note
你不需要附加常見的 Azure SQL 臨時連線錯誤,例如 40197、40501、40613、49918、49919 或 49920。 內建的清單已經會自動重試這些項目。
| 錯誤 | Message | Troubleshooting |
|---|---|---|
| 64 | 已順利建立與伺服器的連接,但隨後在登入過程中發生錯誤。 (提供者:TCP 提供者,錯誤:0 - 指定的網路名稱不再可用。) | TCP 連線在握手過程中中斷。 不是憑證故障。 如果問題持續,請檢查是否有用戶端網路不穩定、網卡卸載錯誤,或是中介裝置中斷尚未建立的連線。 |
| 233 | 客戶端因登入前連線初始化過程中發生錯誤,無法建立連線。 | 登入前傳輸或 TLS 失敗。 當伺服器無法接受連線(資源耗盡、連線數達最大或客戶端不支援)時,通常會回傳此訊息。 不是憑證故障。 確認伺服器健康狀況,然後檢查 loginTimeout、 TLS 設定,以及客戶端/伺服器 TLS 版本相容性。 |
| 4060 | 無法開啟登入所要求的資料庫 database_name。 登入失敗。 | 登入已驗證,但無法開啟所要求的資料庫。 暫時性原因包括資料庫處於過渡期(故障轉移、還原、擴展)或自動暫停。 持續性原因(資料庫不存在、登入無法存取)不會透過重試解決;檢查資料庫名稱、登入映射和資料庫狀態。 |
| 4221 | 因在 HADR_DATABASE_WAIT_FOR_TRANSITION_TO_VERSIONING 上等待過久,登入讀取次要節點失敗。 |
可讀取的次要複本無法接受登入要求,因為副本回收時,執行中交易的資料列版本仍然遺失。 可透過避免在主節點上執行長時間的寫入交易來緩解;一旦主節點提交或回滾未完成的交易,重試通常就會成功。 |
| 10053 | 將要求傳送至伺服器時,發生傳輸等級錯誤。 (提供者:TCP 提供者,錯誤:0 - 主機軟體終止了已建立的連線。) |
本機端已中止連線(Windows Sockets WSAECONNABORTED)。 通常是因為 keepalive 失敗,或本機網路堆疊中止了閒置或半開的連線。 檢查用戶端網路連線狀況、作業系統 keepalive 計時器,以及任何本機防火牆或 VPN 用戶端。 |
| 10054 | 將要求傳送至伺服器時,發生傳輸等級錯誤。 (提供者:TCP 提供者,錯誤:0 - 遠端主機強行關閉現有的連線。 |
遠端傳送了 TCP 重設(Windows Sockets WSAECONNRESET)。 常見原因包括:對等程序當機、防火牆注入重置,或 Azure SQL 閘道關閉閒置連線。 對於閒置後重置的情況,請在用戶端啟用 TCP keepalive,或縮短連線集區的閒置逾時時間。 |
| 10928 | 資源識別碼: N。資料庫的 限制類型 限制為 N ,且已達成。 請參閱 sys.dm_exec_sessions 使用說明。 |
資料庫的資源治理(會話、工作者或請求)已達到限制。 從訊息中識別限制型態,然後減少並行性、擴充資料庫規模,或縮短長期執行的資源操作。 |
| 10929 | 資源識別碼: N。 限制型態 的最小保證為 N,最大限制為 N ,且資料庫目前的使用量為 N。然而,伺服器目前因需求過於繁忙,無法支援該資料庫的請求超過 N 。 | 資料庫已超出其最低保證門檻,且底層伺服器正在節流。 當鄰居負載下降時,重試通常會成功。 持續發生的情況表示你需要更高的服務層級或較少噪音的環境。 |
| 40020 40143 40166 40540 |
在容錯移轉期間,於錯誤 40197 的 Error code %d 欄位中回報。 |
子碼嵌入 40197 故障轉移訊息中,部分路徑會以頂層錯誤編號浮現。 驅動程式會將每一種形式分別列出,因此能對任一種形式重試。 把他們當成40197一樣對待。 |
| 40197 | 服務處理您的要求時發生錯誤。 請重試。 錯誤代碼 N。 | 軟體升級、硬體故障或其他 Azure SQL 故障轉移事件。 重新連接會引導你到健康的複製品。 嵌入的錯誤代碼用來識別故障轉移類型。 持續發生的情況應以會話追蹤 ID 報告。 |
| 40501 | 服務目前忙線中。 在 10 秒後重試要求。 事件識別碼:guid。 代碼: N。 | Azure SQL 引擎節流 建議的底線是10秒後退。 持續發生節流表示您已超出 DTU/vCore 配額;請向上調整規模或降低並行程度。 |
| 40613 | 伺服器server_name上的資料庫database_name目前無法使用。 請稍後重試連接。 如果問題持續,請聯絡客服,並提供 guid 的會話追蹤 ID。 | 資料庫無法使用,通常發生在故障轉移期間,或在調整規模作業期間短暫發生。 在後退時重試;如果問題持續超過幾分鐘,擷取會話追蹤 ID,並開啟支援案件。 |
| 42108 | 無法連線到 SQL 集區,因為它已暫停。 請先恢復 SQL 集區,然後再試一次。 | 專用的 SQL 池(Synapse)處於暫停狀態。 只有在有某個程序並行地重新啟用集區時,重試才有幫助。 明確恢復集區,或在恢復後再排程工作負載。 |
| 42109 | SQL 集區正在升溫。 請重試。 | 專用的 SQL 池正在恢復運作。 以退避方式重試,直到恢復連線;預熱通常需要幾分鐘。 |
| 49918 | 無法處理要求。 資源不足,無法處理要求。 | 控制平面目前無法分配資源來處理這個請求。 在後退時重試。 持續發生則顯示區域容量壓力。 |
| 49919 | 無法處理建立或更新要求。 訂用帳戶 N 目前有太多建立或更新作業正在進行中。 | 管理操作的訂閱層級並行限制。 減少並行的建立/更新呼叫,或將其錯開執行。 |
| 49920 | 無法處理要求。 訂用帳戶 N 目前有太多作業正在進行中。 | 飛行中操作的訂閱級並行限制。 降低並行度,或等待進行中的作業完成。 |
驅動程式的標準清單是 SQLServerError.java 中的 TransientError 列舉。 錯誤訊息內容來自 Azure SQL 暫時性連線錯誤。 語句層級錯誤(如死鎖受害者 1205 或鎖請求逾時 1222)不在此列表中,因為連線-重試迴圈僅在初始連線期間觸發。 要重試那些錯誤,請使用 retryExec 規則。
從屬性檔案載入規則
如果你沒有在連線上設定 retryExec 或 retryConn ,CRL 會尋找在類別路徑上 JAR 驅動程式旁邊命名 mssql-jdbc.properties 的檔案。 該檔案使用基本 key=value 的解析。 以 retryExec= 或 retryConn= 開頭的行會被擷取。 值會使用本文所述的相同語法,並以 ; 分隔多個規則。
使用精確的鍵名(retryExec 和 retryConn),且不設前置空白。 這個檔案並沒有被解析成完整的 Java 屬性檔案。 驅動程式會對每一行逐字檢查 startsWith,所以:
- 以
#或任何其他非retryExec/retryConn前綴開頭的行會被忽略。 - 其鍵僅以
retryExec或retryConn開頭的行(例如,retryExec2=...)會被視為相符的屬性,並可能產生解析錯誤。 不要引入自訂變體。
範例 mssql-jdbc.properties:
retryExec=1205:3,5+5;1222:2,2
retryConn=+4060,40143
如果檔案遺失,CRL 會在 com.microsoft.sqlserver.jdbc.ConfigurableRetryLogic 記錄器下記錄一則 FINE 訊息,並在沒有任何規則的情況下繼續執行。 查詢所用的檔案路徑包含在該日誌訊息中。
連接字串的值優先。 如果在連線中,retryExec 或 retryConn 為非空,驅動程式就不會針對該屬性查閱該檔案。
規則重新整理行為
CRL 維護一套統一的 JVM 規則集。 建造完成後,駕駛會懶散地刷新規則:
- 驅動程式在敘述執行與連線重試期間評估刷新機會。
- 其實只有在上一次讀取過後 30 秒才會進行刷新。
- 若規則原本來自
mssql-jdbc.properties,驅動程式會將檔案最後修改的時間戳與前一次讀取時記錄的時間戳比較。 如果檔案有變動,驅動程式會重新解析它。 - 如果規則原本來自 連接字串,驅動程式會重新套用先前儲存的 connection-string 值。
這種行為意味著編輯 mssql-jdbc.properties 會在約 30 秒內自動被接納,且不需要重新啟動應用程式。
Important
因為規則集是 JVM 範圍內的單例,所以開啟第二個連線時,若其設定了不同的 retryExec 或 retryConn 值,也會取代第一個連線所使用的規則。 當同一 JVM 中的多個連線不一致時,請將 CRL 配置視為程序層級設定,而非每個連線的設定。
queryTimeout 與 connectRetryCount 之間的互動
語句重試與 queryTimeout
當語句規則觸發時,驅動程式會將下一個等待時間與連線層 queryTimeout 級的值進行比較:
- 若
queryTimeout >= 0andtimeToWait > queryTimeout,則驅動程式會引發R_InvalidRetryInterval,而不會重試。 驅動程式不會重新拋出原始錯誤。 它會丟出設定錯誤。 -
queryTimeout連線屬性預設為-1,因此預設跳過比較,允許任何等待時間。 - 設定
queryTimeout=0不會關閉這個檢查,因為0 >= 0是真的。 任何timeToWait > 0都會提高R_InvalidRetryInterval的薪資。
當你設定 queryTimeout 為正值時,保持 initialRetryTime + (retryCount - 1) * retryChange (加法) initialRetryTime * retryChange^(retryCount-1) 或乘法值低於該值。
連線重試與 connectRetryCount 與 loginTimeout
retryConn 它本身不會啟用認證重試。 現有資產仍由其管理:
-
connectRetryCount(預設 1,範圍 0-255)是額外的驗證嘗試次數。 設定為0以停用認證重試。retryConn當connectRetryCount = 0時 不影響,因為驅動程式會觸發第一個故障。 -
connectRetryInterval(預設 10 秒,範圍 1-60)是兩次嘗試之間的等待時間。 第一次重試會立即進行。 -
loginTimeout是整體界限。 如果下一個間隔會使經過的時間超過loginTimeout,驅動程式就會提早停止。
欲了解更多資訊,請參閱連結韌性(JDBC)。
Examples
在寫入時存活死鎖與鎖定超時
jdbc:sqlserver://server;databaseName=db;retryExec={1205,1222:4,2*2:insert,update,delete,merge}
針對死鎖受害者(1205)或鎖定逾時(1222),最多可重試四次,並採用 2、4、8 和 16 秒的退避間隔,但僅適用於寫入語句。
在線上作業下重新執行結構描述建立
retryExec={2714:2,1+1};{3702:2,1+1}
重試錯誤 2714(object already exists)和 3702cannot drop database currently in use()各兩次,等待時間為 1 秒和 2 秒。
在暫態錯誤清單中新增自訂錯誤
retryConn={+<customErrorNumber>}
新增一個自訂錯誤編號,這些錯誤數字還不在內建清單中。 如果你加上內建的 Azure SQL 暫態錯誤,例如 40197、40501、40613、49918、49919 或 49920,驅動程式已經重試了,什麼都不會改變。
透過屬性檔案設定 CRL
將 mssql-jdbc.properties 放在驅動程式 JAR 檔旁邊:
retryExec=1205:3,5+5:select,update
retryConn=+<customErrorNumber>
不要在連線上設定 retryExec 或 retryConn。 驅動程式會從檔案中讀取規則,並在每次修改後重新閱讀(每 30 秒檢查一次)。
CRL 故障排除
在 com.microsoft.sqlserver.jdbc.ConfigurableRetryLogic 記錄器上啟用 FINE(或更詳細)記錄,以查看檔案讀取嘗試與解析決策:
com.microsoft.sqlserver.jdbc.ConfigurableRetryLogic.level=FINE
常見的配置錯誤:
| 錯誤訊息鍵 | 原因 |
|---|---|
R_invalidParameterNumber |
當駕駛者預期錯誤號碼或時序參數,或 retryTimings 包含多個逗號時,會出現非數字標記。 |
R_InvalidRuleFormat |
該規則有超過三個以冒號分隔的區段。 |
R_InvalidRetryInterval |
陳述式規則的計算等待時間超過 queryTimeout。 縮短等待時間或加薪 queryTimeout。 |
R_PathInvalid 或 R_URLInvalid |
驅動程式無法解析出用來尋找 mssql-jdbc.properties 的路徑。 |
R_errorReadingStream |
讀取 mssql-jdbc.properties時 I/O 錯誤。 |
Note
R_invalidParameterNumber 訊息的文字為 參數編號 {0} 無效,這與驅動程式用於預備陳述式參數繫結錯誤的是相同的資源字串。 當 CRL 拋出時,問題值是你的重試規則標記(例如錯誤編號或非數字的時間元素),而非 PreparedStatement 參數索引。
當規則未觸發時需要檢查的事項:
- 該例外中的
SQLServerError.getErrorNumber()實際上符合你規則中的數字。 SQL Server 會根據上下文將某些失敗包裝成不同的數字(例如死鎖與鎖定逾時)。 - 對於含有
queryFilter的陳述式規則,你所執行的 SQL 中以空白分隔的第一個詞元(轉為小寫後)會出現在過濾清單中。 註解和WITHCTE 會改變第一個標記。 -
retryCount重試是 額外的 嘗試。 第一次處決不算。 - 在連線規則中,
connectRetryCount大於 0,且loginTimeout還留有至少一個額外connectRetryInterval的空間。 - 這條規則的形式是正確的。 陳述規則需要時間設定區段。 連線規則則不應該。