Làm cách nào để giải quyết vấn đề nhóm kết nối giữa ASP.NET và SQL Server?


211

Vài ngày qua chúng tôi thấy thông báo lỗi này trong trang web của chúng tôi quá nhiều:

"Hết thời gian hết hạn. Thời gian chờ đã trôi qua trước khi có được kết nối từ nhóm. Điều này có thể xảy ra do tất cả các kết nối được sử dụng và đã đạt được kích thước nhóm tối đa."

Chúng tôi đã không thay đổi bất cứ điều gì trong mã của chúng tôi trong một thời gian. Tôi đã sửa đổi mã để kiểm tra các kết nối mở không đóng, nhưng thấy mọi thứ đều ổn.

  • Làm sao tôi có thể giải quyết việc này?

  • Tôi có cần chỉnh sửa nhóm này không?

  • Làm cách nào tôi có thể chỉnh sửa số lượng kết nối tối đa của nhóm này?

  • Giá trị đề xuất cho một trang web lưu lượng truy cập cao là gì?


Cập nhật:

Tôi có cần chỉnh sửa một cái gì đó trong IIS không?

Cập nhật:

Tôi thấy rằng số lượng kết nối hoạt động ở bất kỳ đâu từ 15 đến 31 và tôi thấy rằng số lượng kết nối được phép tối đa được cấu hình trong máy chủ SQL là hơn 3200 kết nối, là 31 quá nhiều hoặc tôi nên chỉnh sửa một cái gì đó trong cấu hình ASP.NET ?


Mặc định Kích thước nhóm tối đa là 100 nếu tôi nhớ chính xác. Hầu hết các trang web không sử dụng hơn 50 kết nối dưới tải nặng - phụ thuộc vào thời gian truy vấn của bạn mất bao lâu để hoàn thành. Khắc phục ngắn hạn trong Chuỗi kết nối: cố gắng đặt giá trị cao hơn trong chuỗi kết nối của bạn: "Kích thước nhóm tối đa = ..."
splattne

bao nhiêu? làm cho nó 200 chẳng hạn?
Amr Elgarhy 22/03/2016

3
Tôi nghĩ rằng bạn nên thực sự tìm kiếm những gì gây ra vấn đề. Các truy vấn của bạn (hoặc một số trong số họ) đang chạy rất lâu?
splattne

có thể đó là lý do thực sự, một truy vấn cần nhiều thời gian để thực hiện, tôi sẽ tìm kiếm trong đó, cảm ơn
Amr Elgarhy

1
Tôi hy vọng bạn có thể tìm thấy vấn đề. Một gợi ý: nếu bạn đang sử dụng SQL Server, hãy thử "SQL Profiler" và tìm kiếm các truy vấn dài: sql-server-performance.com/articles/per/ trộm
splattne

Câu trả lời:


218

Trong hầu hết các trường hợp, sự cố kết nối có liên quan đến "rò rỉ kết nối". Ứng dụng của bạn có thể không đóng các kết nối cơ sở dữ liệu chính xác và nhất quán. Khi bạn để các kết nối mở, chúng vẫn bị chặn cho đến khi trình thu gom rác .NET đóng chúng cho bạn bằng cách gọi Finalize()phương thức của chúng .

Bạn muốn chắc chắn rằng bạn đang thực sự đóng kết nối . Ví dụ: đoạn mã sau sẽ gây rò rỉ kết nối, nếu mã giữa .OpenCloseném ngoại lệ:

var connection = new SqlConnection(connectionString);
connection.Open();
// some code
connection.Close();                

Cách chính xác sẽ là thế này:

var connection = new SqlConnection(ConnectionString);
try
{
     connection.Open();
     someCall (connection);
}
finally
{
     connection.Close();                
}

hoặc là

using (SqlConnection connection = new SqlConnection(connectionString))
{
     connection.Open();
     someCall(connection);
}

Khi hàm của bạn trả về một kết nối từ một phương thức lớp, hãy đảm bảo bạn lưu trữ cục bộ và gọi Closephương thức đó. Bạn sẽ rò rỉ một kết nối bằng mã này chẳng hạn:

var command = new OleDbCommand(someUpdateQuery, getConnection());
result = command.ExecuteNonQuery();
connection().Close(); 

Kết nối được trả về từ cuộc gọi đầu tiên đến getConnection()không bị đóng. Thay vì đóng kết nối của bạn, dòng này tạo một kết nối mới và cố gắng đóng nó.

Nếu bạn sử dụng SqlDataReaderhoặc a OleDbDataReader, đóng chúng. Mặc dù việc đóng kết nối dường như thực hiện thủ thuật, hãy nỗ lực hết sức để đóng các đối tượng đọc dữ liệu của bạn một cách rõ ràng khi bạn sử dụng chúng.


Bài viết này " Tại sao tràn bộ đệm kết nối? " Từ Tạp chí MSDN / SQL giải thích rất nhiều chi tiết và đề xuất một số chiến lược gỡ lỗi:

  • Chạy sp_whohay sp_who2. Các quy trình được lưu trữ hệ thống này trả về thông tin từ sysprocessesbảng hệ thống hiển thị trạng thái và thông tin về tất cả các quy trình làm việc. Nói chung, bạn sẽ thấy một ID tiến trình máy chủ (SPID) trên mỗi kết nối. Nếu bạn đặt tên cho kết nối của mình bằng cách sử dụng đối số Tên ứng dụng trong chuỗi kết nối, các kết nối làm việc của bạn sẽ dễ dàng tìm thấy.
  • Sử dụng SQL Server Profiler với TSQL_Replaymẫu SQLProfiler để theo dõi các kết nối mở. Nếu bạn quen thuộc với Profiler, phương pháp này dễ hơn bỏ phiếu bằng cách sử dụng sp_who.
  • Sử dụng Trình theo dõi hiệu suất để theo dõi các nhóm và kết nối. Tôi thảo luận về phương pháp này trong một thời điểm.
  • Giám sát hiệu suất quầy trong mã. Bạn có thể theo dõi sức khỏe của nhóm kết nối và số lượng kết nối được thiết lập bằng cách sử dụng các thường trình để trích xuất các bộ đếm hoặc bằng cách sử dụng các điều khiển .NET PerformanceCorer mới.

4
Một hiệu chỉnh nhỏ: GC không bao giờ gọi phương thức Dispose của đối tượng, chỉ có phần hoàn thiện của nó (nếu có). Sau đó, trình hoàn thiện có thể thực hiện lệnh gọi "dự phòng" để Loại bỏ, nếu cần, mặc dù tôi không chắc liệu SqlConnection có làm như vậy hay không.
LukeH

1
Có loại suy giảm hiệu suất nào không khi chúng tôi phải đặt Max Pool kích thước 50 và chúng tôi chỉ có ít người dùng.
mahesh sharma

37

Khi cài đặt .NET Framework v4.6.1, các kết nối của chúng tôi đến cơ sở dữ liệu từ xa ngay lập tức bắt đầu hết thời gian do thay đổi này .

Để khắc phục, chỉ cần thêm tham số TransparentNetworkIPResolutiontrong chuỗi kết nối và đặt thành sai :

Máy chủ = myServerName; Cơ sở dữ liệu = myDataBase; Trusted_Connection = True; Minh bạchNetworkIPResolution = Sai


Trong trường hợp này, vấn đề là "Khoảng thời gian chờ đã trôi qua trước khi có được kết nối từ nhóm". Đã sửa lỗi chuỗi kết nối của bạn giải quyết vấn đề này, hoặc nó là một vấn đề bắt tay riêng biệt?
FBryant87

1
Từ những gì tôi nhớ, đó là thông báo lỗi chính xác như trong câu hỏi. Nó xảy ra ngay sau khi cập nhật lên .NET Framework v4.6.1.
ajbeaven

Đây cũng là trường hợp của tôi, đã sửa nó cho tôi trên Dịch vụ ứng dụng tôi đang chạy trên Azure, kết nối với cơ sở dữ liệu Azure SQL. Tôi đã sử dụng Dapper và xử lý chính xác các kết nối, nhưng vẫn có thông báo lỗi "hết thời gian chờ trước khi nhận được kết nối từ nhóm". Nhưng không còn nữa, cảm ơn @ajbeaven
Steve Kennaird

13

Trừ khi việc sử dụng của bạn tăng lên rất nhiều, có vẻ như không có công việc tồn đọng. IMO, tùy chọn rất có thể là một cái gì đó đang sử dụng các kết nối và không giải phóng chúng kịp thời. Bạn có chắc chắn rằng bạn đang sử dụng usingtrong mọi trường hợp? Hoặc (thông qua bất kỳ cơ chế) phát hành các kết nối?


13

Bạn đã kiểm tra DataReaders chưa được đóng và answer.redirects trước khi đóng kết nối hoặc bộ dữ liệu. Các kết nối vẫn mở khi bạn không đóng chúng trước khi chuyển hướng.


3
+1 - Hoặc các hàm trả về DataReaders - Kết nối sẽ không bao giờ đóng ngoài chức năng bạn đã tạo ...
splattne

1
Nếu chức năng của bạn trả về SqlDataReader, bạn nên chuyển đổi nó thành DataTable hơn là tăng kích thước nhóm tối đa
live-love

10

Thỉnh thoảng chúng tôi cũng gặp phải vấn đề này trên trang web của mình. Thủ phạm trong trường hợp của chúng tôi là số liệu thống kê / chỉ số của chúng tôi đã lỗi thời. Điều này khiến một truy vấn chạy nhanh trước đó (cuối cùng) trở nên chậm và hết thời gian.

Hãy thử cập nhật số liệu thống kê và / hoặc xây dựng lại các chỉ mục trên các bảng bị ảnh hưởng bởi truy vấn và xem điều đó có giúp ích không.


3
Tôi nghĩ rằng điều này sẽ giải thích tại sao một truy vấn có thể hết thời gian, nhưng tôi không nghĩ rằng điều này sẽ giải thích tại sao thời gian chờ lại có kinh nghiệm trong khi cố gắng để có được kết nối.
DrGriff

1
Có thể là với một chỉ mục xấu, các truy vấn mất nhiều thời gian hơn và nhiều kết nối được sử dụng cùng một lúc.
LosManos 29/07/2015

1
Đã làm việc cho tôi sau khi cập nhật tất cả các số liệu thống kê với sp_updatestats trên db: EXEC sp_updatestats;
boateng

6

Bạn có thể chỉ định kích thước nhóm tối thiểu và tối đa bằng cách chỉ định MinPoolSize=xyzvà / hoặc MaxPoolSize=xyztrong chuỗi kết nối. Nguyên nhân của vấn đề này có thể là một điều khác nhau tuy nhiên.


3
MaxPoolSize được đề xuất là gì?
Amr Elgarhy

2
Có lẽ, cách tốt nhất để đi là không chỉ định nó trừ khi bạn có yêu cầu đặc biệt và bạn biết kích thước nhóm tốt cho trường hợp cụ thể của mình .
Mehrdad Afshari

1
Lưu ý: tôi đã kiểm tra và tôi thấy rằng các kết nối hoạt động với db của tôi là khoảng 22 trực tiếp, có quá nhiều không?
Amr Elgarhy 22/03/2016

Tôi không nghĩ vậy. Kích thước nhóm mặc định là 100 kết nối tôi nghĩ. Nó phụ thuộc vào tải của từng kết nối trên mạng và máy chủ SQL. Nếu đó là những truy vấn nặng, nó có thể gây ra vấn đề. Ngoài ra, các sự cố mạng có thể xảy ra khi bắt đầu một kết nối mới và có thể gây ra ngoại lệ đó.
Mehrdad Afshari

5

Tôi cũng gặp phải vấn đề này, khi sử dụng một số lớp dữ liệu của bên thứ 3 trong một trong các ứng dụng .NET của tôi. Vấn đề là lớp không đóng các kết nối đúng cách.

Chúng tôi đã ném ra lớp và tự tạo một lớp, chúng luôn đóng và xử lý các kết nối. Kể từ đó, chúng tôi không nhận được lỗi nữa.


Chúng tôi đang sử dụng LLBL và trang web đang hoạt động từ 2 năm và chỉ vài ngày gần đây bắt đầu hoạt động như thế này.
Amr Elgarhy 22/03/2016

5

Bạn cũng có thể thử điều đó để giải quyết vấn đề thời gian chờ:

Nếu bạn không thêm httpR.78 vào cấu hình web của mình, hãy thêm nó vào <system.web>thẻ

<sytem.web>
     <httpRuntime maxRequestLength="20000" executionTimeout="999999"/>
</system.web>

Sửa đổi chuỗi kết nối của bạn như thế này;

 <add name="connstring" connectionString="Data Source=DSourceName;Initial Catalog=DBName;Integrated Security=True;Max Pool Size=50000;Pooling=True;" providerName="System.Data.SqlClient" />

Ở lần sử dụng cuối cùng

    try
    {...} 
    catch
    {...} 
    finaly
    {
     connection.close();
    }

3

Điều này chủ yếu là do kết nối không được đóng trong ứng dụng. Sử dụng "MinPoolSize" và "MaxPoolSize" trong chuỗi kết nối.


3

Trong trường hợp của tôi, tôi đã không đóng đối tượng DataReader.

        using (SqlCommand dbCmd = new SqlCommand("*StoredProcedureName*"))
        using (dbCmd.Connection = new SqlConnection(WebConfigurationAccess.ConnectionString))
            {
            dbCmd.CommandType = CommandType.StoredProcedure;

            //Add parametres
            dbCmd.Parameters.Add(new SqlParameter("@ID", SqlDbType.Int)).Value = ID;
.....
.....
            dbCmd.Connection.Open();
            var dr = dbCmd.ExecuteReader(); //created a Data reader here
            dr.Close();    //gotta close the data reader
            //dbCmd.Connection.Close(); //don't need this as 'using' statement should take care of this in its implicit dispose method.
            }

2

Nếu bạn đang làm việc với mã kế thừa phức tạp trong đó việc sử dụng (..) {..} đơn giản là không thể - như tôi đã từng - bạn có thể muốn kiểm tra đoạn mã tôi đã đăng trong câu hỏi SO này để tìm cách xác định ngăn xếp cuộc gọi của việc tạo kết nối khi kết nối có khả năng bị rò rỉ (không bị đóng sau khi hết thời gian đặt). Điều này làm cho nó khá dễ dàng để phát hiện nguyên nhân của rò rỉ.


2

Đừng khởi tạo kết nối sql quá nhiều lần. Mở một hoặc hai kết nối và sử dụng chúng cho tất cả các hoạt động sql tiếp theo.

Có vẻ như ngay cả khi Disposeing các kết nối, ngoại lệ được ném.


2

Ngoài các giải pháp được đăng ....

Để xử lý 1000 trang mã kế thừa, mỗi lần gọi một GetRS chung nhiều lần, đây là một cách khác để khắc phục sự cố:

Trong một DLL phổ biến hiện có, chúng tôi đã thêm tùy chọn CommandBehavior.CloseConnection :

    static public IDataReader GetRS(String Sql)
    {
        SqlConnection dbconn = new SqlConnection(DB.GetDBConn());
        dbconn.Open();
        SqlCommand cmd = new SqlCommand(Sql, dbconn);
        return cmd.ExecuteReader(CommandBehavior.CloseConnection);   
    }

Sau đó, trong mỗi trang, miễn là bạn đóng trình đọc dữ liệu, kết nối cũng được tự động đóng để tránh rò rỉ kết nối.

    IDataReader rs = CommonDLL.GetRS("select * from table");
    while (rs.Read())
    {
        // do something
    }
    rs.Close();   // this also closes the connection

2

Tôi cũng gặp vấn đề tương tự và muốn chia sẻ điều gì đã giúp tôi tìm nguồn: Thêm tên Ứng dụng vào chuỗi kết nối của bạn và sau đó tạo ra kết nối mở cho Máy chủ SQL

select st.text,
    es.*, 
    ec.*
from sys.dm_exec_sessions as es
    inner join sys.dm_exec_connections as ec on es.session_id = ec.session_id
    cross apply sys.dm_exec_sql_text(ec.most_recent_sql_handle) st
where es.program_name = '<your app name here>'

1

Vấn đề này tôi đã có trong mã của tôi. Tôi sẽ dán một số mã ví dụ tôi đã gặp lỗi dưới đây. Khoảng thời gian chờ đã trôi qua trước khi có được kết nối từ nhóm. Điều này có thể đã xảy ra vì tất cả các kết nối được sử dụng và kích thước nhóm tối đa đã đạt được.

 String query = "insert into STATION2(ID,CITY,STATE,LAT_N,LONG_W) values('" + a1 + "','" + b1 + "','" + c1 + "','" + d1 + "','" + f1 + "')";
    //,'" + d1 + "','" + f1 + "','" + g1 + "'

    SqlConnection con = new SqlConnection(mycon);
    con.Open();
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = query;
    cmd.Connection = con;
    cmd.ExecuteNonQuery();
    **con.Close();**

Bạn muốn đóng kết nối mọi lúc. Trước đó tôi đã không cho chúng tôi kết nối chặt chẽ do điều này tôi đã gặp lỗi. Sau khi thêm tuyên bố gần tôi đã vượt qua lỗi này


0

Bạn đã bị rò rỉ kết nối trên mã của bạn. Bạn có thể thử sử dụng để xác nhận rằng bạn đang đóng chúng.

 Using (SqlConnection sqlconnection1 = new SqlConnection(“Server=.\\SQLEXPRESS ;Integrated security=sspi;connection timeout=5”)) {
                          sqlconnection1.Open();
                          SqlCommand sqlcommand1 = sqlconnection1.CreateCommand();
                          sqlcommand1.CommandText = raiserror (‘This is a fake exception’, 17,1)”;
                          sqlcommand1.ExecuteNonQuery();  //this throws a SqlException every time it is called.
                          sqlconnection1.Close(); //Still never gets called.
              } // Here sqlconnection1.Dispose is _guaranteed_

https://bloss.msdn.microsoft.com/angelsb/2004/08/25/connection-pooling-and-the-timeout- expired-exception-faq/


0

Vấn đề này tôi đã gặp phải trước đây. Nó đã trở thành một vấn đề với tường lửa. Tôi vừa thêm một quy tắc vào tường lửa. Tôi đã phải mở cổng 1433để máy chủ SQL có thể kết nối với máy chủ.


0

Dùng cái này:

finally
{
    connection.Close();
    connection.Dispose();
    SqlConnection.ClearPool();
}

12
Có thể tôi đang thiếu quan điểm SqlConnection.ClearPool, nhưng doe chỉ ngăn kết nối hiện tại của bạn được phát hành trở lại nhóm kết nối? Tôi nghĩ ý tưởng của nhóm kết nối là cho phép kết nối nhanh hơn. Chắc chắn sẽ giải phóng kết nối từ nhóm mỗi khi kết thúc với nghĩa là sẽ cần tạo kết nối MỚI MỌI THỜI GIAN, thay vì kéo một phụ tùng từ hồ bơi? Hãy giải thích làm thế nào và tại sao kỹ thuật này là hữu ích.
Dib

0

Có, có một cách để thay đổi cấu hình. Nếu bạn đang ở trên một máy chủ chuyên dụng và chỉ cần nhiều kết nối SQL hơn, bạn có thể cập nhật các mục "kích thước nhóm tối đa" trong cả hai chuỗi kết nối bằng cách làm theo các hướng dẫn sau:

  1. Đăng nhập vào máy chủ của bạn bằng Remote Desktop
  2. Mở My Computer (Windows - E) và truy cập C: \ inetpub \ vhosts [domain] \ httpdocs
  3. Nhấp đúp chuột vào tập tin web.config. Điều này có thể chỉ được liệt kê dưới dạng web nếu cấu trúc tệp được đặt để ẩn tiện ích mở rộng. Điều này sẽ mở ra Visual Basic hoặc trình soạn thảo tương tự.
  4. Tìm Chuỗi kết nối của bạn, chúng sẽ trông giống như các ví dụ bên dưới:

    "add name =" SiteSqlServer "ConnectionString =" server = (local); cơ sở dữ liệu = dbname; uid = dbuser; pwd = dbpassword; pooling = true; Connection life = 120; max pool size = 25; ""

5. Thay đổi kích thước nhóm tối đa = giá trị X thành kích thước nhóm yêu cầu.

  1. Lưu và đóng tệp web.config của bạn.


0

Trong trường hợp của tôi, tôi đã có vòng lặp vô hạn (từ một thuộc tính get đang cố lấy giá trị từ cơ sở dữ liệu), nó tiếp tục mở hàng trăm kết nối Sql.

Để tái tạo vấn đề, hãy thử điều này:

while (true)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        someCall(connection);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.