2014-02-14 111 views
0

我有一個sql連接,我必須每秒從500到10,000次訪問數據庫。每秒大約250次後,事情開始放緩,然後應用程序遠遠落後於崩潰。ADO.NET SQL Server性能瓶頸

我在考慮將數據庫放入字典中。我需要獲得最快的表現。目前,ado.net需要大約1到2毫秒,但是有些事情會導致瓶頸。

每秒10k查詢的下面語法有什麼問題嗎?是一本正在工作的字典嗎?我們正在談論大約1200萬條記錄,我需要能夠在1到5毫秒內搜索它。我在數據庫中還有另外一個擁有5000萬條記錄的集合,所以我不確定如何存儲它。任何建議都會很棒。

的SQL分貝具有128GB的存儲和80個處理器和應用程序是在SQL服務器上的同一個服務器上2012

using (SqlConnection sqlconn = new SqlConnection(sqlConnection.SqlConnectionString())) 
    { 
     using (SqlCommand sqlcmd = new SqlCommand("", sqlconn)) 
     { 
      sqlcmd.CommandType = System.Data.CommandType.StoredProcedure; 
      sqlcmd.Parameters.Clear(); 
      sqlcmd.CommandTimeout = 1; 
      sqlconn.Open(); 
      using (SqlDataReader sqlDR = sqlcmd.ExecuteReader(CommandBehavior.CloseConnection)) 


    public static string SqlConnectionString() 
    { 
     return string.Format("Data Source={0},{1};Initial Catalog={2};User ID={3};Password={4};Application Name={5};Asynchronous Processing=true;MultipleActiveResultSets=true;Max Pool Size=524;Pooling=true;", 
        DataIP, port, Database, username, password, IntanceID); 
    } 

DataReader的下面的代碼是

r.CustomerInfo = new CustomerVariable(); 
r.GatewayRoute = new List<RoutingGateway>(); 
while (sqlDR.Read() == true) 
{ 

    if (sqlDR["RateTableID"] != null) 
     r.CustomerInfo.RateTable = sqlDR["RateTableID"].ToString(); 
    if (sqlDR["EndUserCost"] != null) 
     r.CustomerInfo.IngressCost = sqlDR["EndUserCost"].ToString(); 
    if (sqlDR["Jurisdiction"] != null) 
     r.CustomerInfo.Jurisdiction = sqlDR["Jurisdiction"].ToString(); 
    if (sqlDR["MinTime"] != null) 
     r.CustomerInfo.MinTime = sqlDR["MinTime"].ToString(); 
    if (sqlDR["interval"] != null) 
     r.CustomerInfo.interval = sqlDR["interval"].ToString(); 
    if (sqlDR["code"] != null) 
     r.CustomerInfo.code = sqlDR["code"].ToString(); 
    if (sqlDR["BillBy"] != null) 
     r.CustomerInfo.BillBy = sqlDR["BillBy"].ToString(); 
    if (sqlDR["RoundBill"] != null) 
     r.CustomerInfo.RoundBill = sqlDR["RoundBill"].ToString(); 

} 
sqlDR.NextResult(); 
+1

這爲的String.format每個連接,你需要打開可優化,做到了一次。不知道它是否會有所作爲。你可以測試它。 – Steve

+1

是的可能將該連接字符串轉換爲常量 – HOKBONG

+0

存儲過程中的SQL邏輯如何?它會加入表格還是簡單的選擇?最好將其納入分析案例。 – HOKBONG

回答

0

那麼,如果您已經測量出ADO命令只需要幾毫秒,另一個可能的延遲原因是字符串。構建連接字符串的格式

我會嘗試來移除呼籲每

using(SqlConnection cn = new SqlConnection(sqlConnection.SqlConnectionString())) 

代替的String.format,假設SqlConnectionString是在一個單獨的類,你可以寫

private static string conString = string.Empty; 
public static string SqlConnectionString() 
{ 
    if(conString == "") 
     conString = string.Format("............"); 
    return conString; 
} 

當然,基準可以排除這一點,但我我非常肯定,像這樣的字符串操作是昂貴的

看到您的意見下面另一件事是非常重要的補充是正確的聲明你的參數。除了使用AddWithValue的(方便,但棘手的副作用)用正確的大小,當您需要的性能每毫秒擠聲明你的參數不建議

using (SqlCommand sqlcmd = new SqlCommand("", sqlconn)) 
{ 
    sqlcmd.CommandType = System.Data.CommandType.StoredProcedure; 
    sqlcmd.CommandText = mySql.GetLCR(); 
    SqlParameter p1 = new SqlParameter("@GatewayID", SqlDbType.NVarChar, 20).Value = GatewayID; 
    SqlParameter p2 = new SqlParameter("@DialNumber", SqlDbType.NVarChar, 20).Value = dialnumber; 
    sqlCmd.Parameters.AddRange(new SqlParameter[] {p1, p2}); 
    sqlcmd.CommandTimeout = 1; 
    sqlconn.Open(); 
    ..... 
} 

的AddWithValue。 This very useful article解釋了爲什麼使用AddWithValue傳遞字符串會破壞Sql Server優化器所做的工作。 (簡而言之,優化器將計算並存儲您的命令的查詢計劃,如果它接收到另一個相同的命令,它將重新使用計算出的查詢計劃。但是,如果您傳遞帶有addwithvalue的字符串,則會每次計算參數的大小對於實際傳遞的字符串長度,優化器不能重用查詢計劃並重新計算並存儲新的查詢計劃)

+0

我發佈的答案是string.format的基準。我會說string.format不應該是這裏的問題。 – Fredou

+0

是的,你可能是對的,我想知道在這裏創建DataReader – Steve

+0

後丟失的代碼是代碼的下一部分: sqlcmd.CommandText = mySql.GetLCR(); sqlcmd.Parameters.AddWithValue(「@ GatewayID」,GatewayID); sqlcmd.Parameters.AddWithValue(「@ DialNumber」,dialnumber); while(sqlDR.Read()== true) { 整個過程需要大約1到3毫秒,但每個毫秒數 –

0

我不認爲問題是字符串。格式

結果是:

108毫秒的格式

1416毫秒開放

5176 ms爲單位執行

,整個事情是6891毫秒

運行此,非常簡單測試

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     private static string DataIP;   
     private static string Database;   
     private static string IntanceID; 

     static void Main(string[] args) 
     { 
      DataIP = @"FREDOU-PC\SQLEXPRESS"; Database = "testing"; IntanceID = "123"; 
      int count = 0; 
      System.Diagnostics.Stopwatch swWholeThing = System.Diagnostics.Stopwatch.StartNew(); 

      System.Diagnostics.Stopwatch swFormat = new System.Diagnostics.Stopwatch(); 
      System.Diagnostics.Stopwatch swOpen = new System.Diagnostics.Stopwatch(); 
      System.Diagnostics.Stopwatch swExecute = new System.Diagnostics.Stopwatch(); 

      for (int i = 0; i < 100000; ++i) 
      { 
       using (System.Data.SqlClient.SqlConnection sqlconn = new System.Data.SqlClient.SqlConnection(SqlConnectionString(ref swFormat))) 
       { 
        using (System.Data.SqlClient.SqlCommand sqlcmd = new System.Data.SqlClient.SqlCommand("dbo.counttable1", sqlconn)) 
        { 
         sqlcmd.CommandType = System.Data.CommandType.StoredProcedure; 
         sqlcmd.Parameters.Clear(); 
         swOpen.Start(); 
         sqlconn.Open(); 
         swOpen.Stop(); 
         swExecute.Start(); 

         using (System.Data.SqlClient.SqlDataReader sqlDR = sqlcmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection)) 
         { 
          if (sqlDR.Read()) 
          count += sqlDR.GetInt32(0); 
         } 

         swExecute.Stop(); 
        } 
       }     
      } 

      swWholeThing.Stop(); 
      System.Console.WriteLine("swFormat: " + swFormat.ElapsedMilliseconds); 
      System.Console.WriteLine("swOpen: " + swOpen.ElapsedMilliseconds); 
      System.Console.WriteLine("swExecute: " + swExecute.ElapsedMilliseconds); 
      System.Console.WriteLine("swWholeThing: " + swWholeThing.ElapsedMilliseconds + " " + count); 

      System.Console.ReadKey(); 
     } 

     public static string SqlConnectionString(ref System.Diagnostics.Stopwatch swFormat) 
     { 
      swFormat.Start(); 
      var str = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=True;Application Name={2};Asynchronous Processing=true;MultipleActiveResultSets=true;Max Pool Size=524;Pooling=true;", 
         DataIP, Database, IntanceID); 
      swFormat.Stop(); 
      return str; 
     } 
    } 
} 

dbo.counttable1存儲過程:

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
create PROCEDURE dbo.counttable1 
AS 
BEGIN 
    SET NOCOUNT ON; 
    SELECT count(*) as cnt from dbo.Table_1 
END 
GO 

dbo.table_1

USE [testing] 
GO 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[Table_1](
    [id] [int] NOT NULL, 
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED 
(
    [id] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 

內容:

insert into dbo.Table_1 (id) values (1) 
insert into dbo.Table_1 (id) values (2) 
insert into dbo.Table_1 (id) values (3) 
insert into dbo.Table_1 (id) values (4) 
insert into dbo.Table_1 (id) values (5) 
insert into dbo.Table_1 (id) values (6) 
insert into dbo.Table_1 (id) values (7) 
insert into dbo.Table_1 (id) values (8) 
insert into dbo.Table_1 (id) values (9) 
insert into dbo.Table_1 (id) values (10) 
+0

我會嘗試刪除格式並將其設置爲常量,以查看是否有幫助。 –

1

如果你將要訪問的DataReader的一個循環,那麼你應該找到索引在循環外部,然後在循環內使用它們。你也可以更好地使用強類型的accesors。

2
  1. 不要關閉並重新打開連接,可以在請求之間保持打開狀態。即使您打開了連接池,也存在一定的開銷,包括一個簡短的關鍵部分,以防止從池中獲取連接時出現併發問題。也可以避免這一點。

  2. 確保您的存儲過程已啓用SET NOCOUNT以減少惡意行爲。

  3. 確保您使用的是最小的事務隔離級別,例如,髒讀a.k.a NOLOCK。您可以在連接級別或存儲過程本身的客戶端進行設置,這更符合您的需求。

  4. 描述這些交易以確保客戶端的瓶頸。可能位於數據庫服務器或網絡上。

  5. 如果這是一個多線程應用程序(例如在Web上),請檢查您的連接池設置並確保它足夠大。這裏有一個PerfMon計數器。

  6. 通過使用強類型獲得者的序號訪問你的字段,例如, GetString(0)或GetInt32(3)。

  7. 調整存儲過程和索引中的bejesus。可以寫一本關於此的書。

  8. 在停機期間對錶格重新編制索引,並填充索引頁面(如果這是一個相當靜態的表格)。

  9. 如果存儲過程的目的是檢索單個行,請嘗試將TOP 1添加到查詢中,以便在找到第一行後停止loking。此外,請考慮使用輸出參數而不是結果集,這會導致少量開銷。

  10. 字典可能工作,但它取決於數據的性質,您如何搜索它以及行的寬度。如果你用更多的信息更新你的問題,我會編輯我的答案。

0

「我需要最快的性能我能得到。」

如果您還沒有這樣做,請查看您的業務需求以及應用程序與數據倉庫的交互方式。如果你已經這樣做了,那麼請忽略這個帖子。


據我的經驗是:

  1. ,你甚至執行對數據庫的SQL查詢,這意味着你有一個費用 - 查詢花費時間/ CPU /內存。
  2. 如果查詢包含寫入操作,查詢會更加昂貴。

最簡單的省錢方法,就是不花錢!因此,尋找方法來:

  1. 避免在首位
  2. 查詢數據庫,確保查詢,快速執行儘可能

策略

  • 確保您正確使用數據庫索引。
  • 避免導致全表掃描的SQL查詢。
  • 使用連接池。
  • 如果您要將數據插入數據庫,請使用批量上傳。
  • 在適當的地方使用緩存。選項包括:內存
    • 緩存的結果(即RAM)
    • 緩存結果到磁盤
    • 預渲染結果的時間提前的閱讀他們,而不是執行一個新的查詢
    • ,而不是挖掘與原始數據每個查詢都要考慮生成可以查詢的摘要數據。
  • 分區您的數據。這可以在幾個層次上發生:
    • 大多數企業數據庫的支持分區策略
    • 通過檢查你的商業模式,可以跨多個數據庫分區的數據(即讀對一個DB /寫操作,寫操作對另一個DB) 。
  • 查看您的應用程序的設計,然後測量響應時間以確認瓶頸實際上是您相信的地方。

緩存技術

免責聲明:我不是一個數據庫ADMI nistrator(DBA)。

0

如果您正在處理數百萬條記錄並且每秒從500到10,000次的任何地方觸擊數據庫。我會建議爲數據檢索創建處理程序文件(API),並且您可以找到負載測試工具來測試API性能。

通過使用內存緩存性能可以提高,下面是步驟來實現內存緩存

  1. 你要創建一個窗口服務,將在JSON格式檢索內存緩存從數據庫和存儲數據(關鍵值對)。

  2. 對於網站創建一個處理程序文件作爲API,它將從memcache中檢索數據並顯示結果。

我已經在我的項目之一實現這一點,檢索成千上萬的數據以毫秒爲單位