作者:京東零售 董方酉
引言
應用健康度是反饋應用健康程度的指標,它將系統指標分類為基礎資源、容器、應用、報警配置、鏈路這幾項,收集了一系列系統應用的指標,並對指標進行打分。
應用健康度的每一項指標顯示著系統在某一方面可能存在的隱患和安全問題;因此提高應用健康度對於系統監控具有重要意義。知其然需知其所以然,瞭解應用健康度中的指標背後的隱患,對於我們瞭解和提升系統安全性很有幫助。
筆者作為後端研發工程師,同時在推動組內應用健康度提高的同時,基於遇到的問題現象,結合應用健康度進行剖析,將逐一總結一系列應用健康度隱患剖析;
第一篇,我們來剖析下容易被人忽視的資料庫時區設定項可能導致的隱患。
一、應用健康度檢查項
資料庫連線池配置中,透過解析原始碼獲取,支援DBCP1.X,DBCP2.X,Ali Durid,HikariCP四種連線池;配置監測有以下幾項
風險示例如下圖所示:
connectTimeout 、SocketTimeout 和 時區 三個指標是連線池資料來源的 url 中解析得到, 如:mysql://xxx.jd.com:3358/jdddddb_0?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&connectTimeout=1000&socketTimeout=3000&serverTimezone=Asia/Shanghai
其中,時區設定容易被人忽視;忽略設定會帶來什麼樣的隱患呢?
二、遇到的問題
1、現象
在2023年3月12日(3月的第二個週日),系統UMP監控報警,提示如下
2、問題原因
Mysql 驅動:mysql-connector-java 升級到8版本後。將資料庫時間解析到java時間,需要獲取資料庫的時區。如果資料庫連線中指定時區,則會用該時區,否則可能會使用系統時區
可透過select @@time_zone語句查詢,如果返回SYSTEM,則說明資料庫沒有設定時區,使用select @@system_time_zone 語句可查詢得出系統預設時區,為CST。
CST時區為美國中部時間,由於美國有夏令時和非夏令時
CST非夏令時對應 UTC-06:00,夏令時對應 UTC-05:00 。
美國的夏令時,從每年3月第2個星期天凌晨開始,到每年11月第1個星期天凌晨結束。
以2023年為例:
夏令時開始時間調整前:2023年03月12日星期日 02:00:00,時間向前撥一小時.
調整後:2023年03月12日星期日 03:00:00
夏令時結束時間調整前:2023年11月05日星期日 02:00:00,時間往回撥一小時.
調整後:2023年11月05日星期日 01:00:00
這意味這:CST沒有2023-03-12 02:00:00~2023-03-12 03:00:00 這個區間的時間。會有兩個 2023-11-05 01:00:00~2023-11-05 02:00:00區間的時間。
因此,在獲取資訊時會丟擲“SQLException: HOUR_OF_DAY: 2 -> 3”異常。
3、修改方案
資料庫連線地址中設定資料時區:serverTimezone=Asia/Shanghai
三、時間相關的其他隱患
1、據研究實驗反饋,設定時區為預設時可能有效能問題,往往需要指定時區。
2、使用timestamp型別時需注意時間偏差:
timestamp型別的時間範圍between '1970-01-01 00:00:01' and '2038-01-19 03:14:07',超出這個範圍則值記錄為'0000-00-00 00:00:00',該型別的一個重要特點就是儲存的時間與時區密切相關,UTC(Universal Time Coordinated)標準,指的是經度0度上的標準時間,我國日常生活中時區以首都北京所處的東半球第8區為基準,統一使用東8區時間(俗稱北京時間),比UTC要早8個小時,時區設定也遵照此標準,因此對應過來timestamp的時間範圍則應校準為'1970-01-01 08:00:01' and '2038-01-19 11:14:07',也就是說東八區的1970-1-1 08:00:01等同於UTC1970-1-1 00:00:01。
3、儘量使用dateTime格式而非timestamp:
有一些情況需要注意不要使用 timestamp 儲存時間:
• 生日:生日肯定會有早於1970年的,會超出 timestamp 的範圍
• 有效期截止時間:timestamp 的最大時間是2038年,如果用來存類似身份證的有效期截止時間,營業執照的截止時間等就不合適。
• 業務生存時間:網際網路時代發展快,業務時間很可能在2038年還在繼續運營。
四、資料庫連線設定的其他隱患
1、連線數設定
(1) 介紹
資料庫連線池在初始化時將建立一定數量的資料庫連線放到連線池中,這些資料庫連線的數量是由最小資料庫連線數制約。無論這些資料庫連線是否被使用,連線池都將一直保證至少擁有這麼多的連線數量。連線池的最大資料庫連線數量限定了這個連線池能佔有的最大連線數,當應用程式向連線池請求的連線數超過最大連線數量時,這些請求將被加入到等待佇列中。
由此看來,當資料庫最大連線數設定不夠大時,則會出現某些報表或需要查詢資料庫的請求失敗,由於連線數不夠不能被處理,從而報錯。當出現大量併發的報表請求,且連線池的最大連線數不夠用時,一些使用者的請求就無法處理,這樣也就從另一個層面影響了整個專案處理吞吐量的能力,限制了專案的效能和效率。
(2)設定原則
既能保證專案正常使用時對資料庫連線數的要求,又能保護DBS的安全和穩定。
(3)查詢方式:
查詢最大連線數命令:show variables like'%max_connections%';
查詢當前資料庫已建立連線數:show status like 'Threads_connected';
(4)建議:
MYSQL官網給出了一個設定最大連線數的建議比例:
Max_used_connections / max_connections * 100% ≈ 85%
即已使用的連線數佔總上限的85%左右。
2、超時時間設定
(1)介紹
一次完整的請求包括三個階段:1、建立連線 2、資料傳輸 3、斷開連線
connect timeout:如果與伺服器(這裡指資料庫)請求建立連線的時間超過ConnectionTimeOut,就會拋 ConnectionTimeOutException,即伺服器連線超時,沒有在規定的時間內建立連線。 在資料庫連線設定中,connectTimeout表示等待和MySQL資料庫建立socket連結的超時時間,預設值0,表示不設定超時,單位毫秒。
socket timeout:如果與伺服器連線成功,就開始資料傳輸了。如果伺服器處理資料用時過長,超過了SocketTimeOut,就會丟擲SocketTimeOutExceptin,即伺服器響應超時,伺服器沒有在規定的時間內返回給客戶端資料。在資料庫連線設定中,socketTimeout表示客戶端和MySQL資料庫建立socket後,讀寫socket時的等待的超時時間,linux系統預設的socketTimeout為30分鐘。
(2)隱患
訪問資料庫超時間太長,訪問資料量大或者掃描的資料量太大,導致資料庫長時間無響應。連結被佔用無法釋放,會導致執行緒池被佔滿。因此,為了能夠及時釋放佔用連結,其他業務對資料庫訪問不受影響,所以要合理設定資料庫訪問超時時間。
JDBC的socket timeout在資料庫被突然停掉或是發生網路錯誤(由於裝置故障等原因)時十分重要。由於TCP/IP的結構原因,socket沒有辦法探測到網路錯誤,因此應用也無法主動發現資料庫連線斷開。如果沒有設定socket timeout的話,應用在資料庫返回結果前會無期限地等下去,這種連線被稱為dead connection。
為了避免dead connections,socket必須要有超時配置。socket timeout可以透過JDBC設定,socket timeout能夠避免應用在發生網路錯誤時產生無休止等待的情況,縮短服務失效的時間。
(3)建議
一般情況,建議配置connectTimeout=60000,單位毫秒。建議配置socketTimeout=60000,單位毫秒。具體配置因系統而異。
總結
容易被忽視的資料庫連線應用健康度檢查項,背後有著時區、超時時間、連線數設定不當可能帶來的隱患;根據應用實際情況並遵循設定原則進行合理設定,滿足應用健康度檢查項,才能防患於未然。