數據庫連接數過萬的真相,原來我們錯怪阿裏雲RDS了
在一次數據庫連接問題中,我們堅持認為數據庫連接數過萬是阿裏雲RDS的問題,但後來阿裏雲提供了當時的數據庫連接情況,讓我們動搖了自己的想法。

上麵這5個帳戶產生了10030個數據庫連接,當看前4個帳戶(產生了9511個連接)的名稱時,我們打了一個寒顫 —— 這些都是運行 Linux 上的 ASP.NET Core 站點。。。這不是巧合,其中必有蹊蹺。隨後,我們觀察了主備庫切換後的 RDS 中數據庫連接情況。有一個運行在 Linux 上的 ASP.NET Core 站點,用了3台服務器,卻產生了1528個數據庫連接。
-
SELECT * FROM sys.sysprocesses
-
WHERE loginame='xxx'
重啟其中1台服務器上的站點,連接數立馬從1528降到了391。什麼情況?數據庫連接池發飆了?繼續觀察,當前數據庫中大量的連接都是由運行在 Linux 上的 ASP.NET Core 站點產生的,而且會隨著時間的推移保持增長。
數據庫連接泄漏了,這還是第1次遇到!可我們在 APS.NET Core 應用中所有的數據庫操作都用的是Entity Framework Core,不存在沒有及時關閉數據庫連接的情況,唯一可以懷疑的對象是在 System.Data.SqlClient 中實現的 ADO.NET 數據庫連接池。
數據庫連接池究竟出什麼狀況了?我們在數據庫連接字符串中沒有另外設置連接池,用的是默認設置(Min_Pool_Size = 0; 與 Max_Pool_Size = 100;)。而且更奇怪的是 Max_Pool_Size 的限製沒起作用,不然隻會報下麵的錯誤,不會連接數一直增長。
-
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
我們想來想去,唯一能想得通的解釋是 .NET Core 的數據庫連接池發生了這樣的狀況 —— 連接池中已經創建的連接無法被重用,不僅如此,而且它們直接被 SqlClient 給無視了,都沒有被計算在 Pool Size 中,所以根本觸發不了 Max_Pool_Size 的限製,造成連接無限製,任由 SqlClient 建。更要命的是,這些被無視的連接卻一直在保持著與數據庫的連接。於是,連接泄露成了命中注定。
在有了這個唯一想得通的猜測後,我們今天開始在測試環境中進行驗證。部署一個 ASP.NET Core 站點,創建一個專用數據庫連接帳戶,然後用下麵的 SQL 語句查看數據庫連接是否被重用,同時在測試服務器用 tcpdump 進行抓包,並且分別用阿裏雲 RDS 與我們自己搭建的 SQL Server 服務器進行測試。
-
SELECT * from sys.sysprocesses where loginame='測試專用帳戶'
如果連接池正常工作,第1次訪問,新建所需的數據庫連接;第2次訪問同樣的頁麵,應該重用已有的數據庫連接,不會創建新的數據庫連接。開始測試時,不管連接阿裏雲 RDS 還是我們自己的 SQL Server,連接池都工作正常,連接能被重用。後來分析了一下,雖然生產環境中連接數一直在增長,但增長速度不是很快,可能問題的發生需要一定的時間間隔,或許連接閑置超過一定時間之後才不會被重用。
於是,我們間隔了10分鍾左右進行訪問測試,問題重現了!比如其中的一次測試,同一個頁麵第1次訪問,產生了5個連接;過10分鍾左右再訪問,會新建3個連接變成8個連接;再過10分鍾左右訪問,連接增長到11個。這種連接不能被重用的情況通過 tcp 抓包也可以看出來。如果在很短的時間內訪問,連接數保持不變(連接被重用)。

這個問題不僅在阿裏雲 RDS (SQL Server 2008 R2)可以重現,而且在我們自己搭建的 SQL Server 2014 也能重現,問題的真相隨之水落石出:數據庫連接數過萬問題不是阿裏雲 RDS 的問題,而是 .NET Core 中 System.Data.SqlClient 的連接池在 Linux 上的實現問題,我們錯怪了阿裏雲,輕信了微軟。這是我們使用阿裏雲以來對阿裏雲最大的一次誤會,這是我們 .NET Core 遷移過程中遇到的最大的一個坑。為什麼最近才出現這個問題?是因為我們最近將更多站點遷移到了 ASP.NET Core ,而且將之前一些跑在 Windows 上的 ASP.NET Core 站點切換到了 Linux 。
如何解決這個問題?我們會察看一下 System.Data.SqlClient 的實現代碼,看能否找到實現層麵的線索。阿裏雲會進一步驗證這個問題,如果確認是微軟實現上的問題,會與微軟溝通解決。我們在 Windows 上進行對比測試發現,在 Windows 上連接池中閑置的數據庫連接過段時間會被自動關閉,與上麵 Linux 同樣的測試場景,間隔10分鍾後查看,數據庫連接全消失了。
來源:博客園
最後更新:2017-04-07 21:05:52