2014-02-14 77 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中检索数据并显示结果。

我已经在我的项目之一实现这一点,检索成千上万的数据以毫秒为单位