繁体   English   中英

ADO.NET SQL Server性能瓶颈

[英]ADO.NET SQL Server Performance bottleneck

我有一个sql连接,我必须每秒从500到10,000次访问数据库。 大约每秒250次之后,事情开始变慢,然后应用程序崩溃了很多。

我当时正在考虑将数据库放入字典中。 我需要我能获得的最快的性能。 当前ado.net大约需要1到2毫秒,但是发生某些事情会导致瓶颈。

每秒10k查询的以下语法有什么问题吗? 字典上班了吗? 我们谈论的是1200万条记录,我需要能够在1到5毫秒内进行搜索。 我在数据库中还有另一个集合,该集合具有5000万条记录,因此我不确定如何存储它。 任何建议都会很棒。

SQL数据库具有128 GB内存和80个处理器,并且该应用程序位于Sql Server 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);
    }

数据读取器下面的代码是

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. 不要关闭并重新打开连接,可以在请求之间保持打开状态。 即使已打开连接池,也存在一定的开销,其中包括一个简短的关键部分,以防止从池中捕获连接时出现并发问题。 也可以避免这种情况。

  2. 确保您的存储过程的SET NOCOUNT ON减少了聊天次数。

  3. 确保您正在使用可以摆脱的最低事务隔离级别,例如,脏读又称为NOLOCK。 您可以在客户端的连接级别或存储过程本身中进行设置,以使您更满意。

  4. 剖析这些事务以确保瓶颈在客户端上。 可以在数据库服务器上或在网络上。

  5. 如果这是一个多线程应用程序(例如,在Web上),请检查您的连接池设置并确保它足够大。 这里有一个PerfMon计数器。

  6. 使用强类型的getter(例如GetString(0)或GetInt32(3))按序访问您的字段。

  7. 从存储过程和索引中调整bejesus。 可以为此写一本书。

  8. 在停机期间重新索引表,如果这是一个相当静态的表,请填满索引页面。

  9. 如果存储过程的目的是检索单行,请尝试将TOP 1添加到查询中,以便在找到第一行后停止查找。 另外,请考虑使用输出参数而不是结果集,这样会减少开销。

  10. 字典可能会起作用,但是它取决于数据的性质,搜索方式以及行的宽度。 如果您使用更多信息更新您的问题,我将编辑我的答案。

如果要循环访问DataReader,则应该在循环外部找到索引,然后在循环内部使用它们。 您也可以使用强类型的附件更好。

好吧,如果您已经测量过ADO命令只需要几毫秒的时间,那么另一个可能的延迟原因就是字符串。

我会尝试删除每个调用的string.Format

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

相反,假设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。 这篇非常有用的文章解释了为什么使用AddWithValue传递字符串会破坏Sql Server优化程序所做的工作。 (简而言之,优化器会为您的命令计算并存储一个查询计划,如果它收到另一个相同的命令,则会重新使用所计算的查询计划。但是,如果您传递带有addwithvalue的字符串,则每次都会根据优化程序无法重用查询计划并重新计算和存储新的字符串)

我认为问题不在于string.format

结果是:

格式为108毫秒

开放时间为1416毫秒

5176毫秒用于执行

整个过程是6891 ms

运行这个, 非常简单的测试!

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)

“我需要能够获得的最快的性能。”

如果您还没有这样做,请查看您的业务需求以及应用程序与数据仓库的交互方式。 如果您已经完成此操作,请忽略此发布。


根据我的经验,

  1. 您甚至要对数据库执行SQL查询,这意味着您要花很多钱-查询会花费时间/ CPU /内存。
  2. 如果查询包含写操作,则价格甚至更高。

省钱的最简单方法就是不花钱! 因此,寻找以下方法:

  1. 避免首先查询数据库
  2. 确保查询尽快执行

策略

  • 确保正确使用数据库索引。
  • 避免进行全表扫描的SQL查询。
  • 使用连接池。
  • 如果要将数据插入数据库,请使用批量上传。
  • 在适当的地方使用缓存。 选项包括:
    • 缓存导致内存(即RAM)
    • 将结果缓存到磁盘
    • 预先渲染结果并读取它们,而不是执行新的查询
    • 与其在每个查询中挖掘原始数据,不如考虑生成可以查询的摘要数据。
  • 对数据进行分区。 这可以在几个级别上发生:
    • 大多数企业数据库支持分区策略
    • 通过查看您的业务模型,您可以跨多个数据库对数据进行分区(即,对一个数据库的读/写操作,对另一个数据库的写操作)。
  • 查看您的应用程序的设计,然后测量响应时间,以确认瓶颈确实在您认为的位置。

教练技术

免责声明:我不是数据库管理员(DBA)。

如果您要处理数百万条记录,并且每秒访问数据库500到10,000次。 我建议创建用于数据检索的处理程序文件(API),您可以找到负载测试工具来测试API性能。

通过使用内存缓存,可以提高性能,以下是实现内存缓存的步骤

  1. 您必须创建一个窗口服务,该窗口服务将从数据库中检索数据并以(键值对)JSON格式存储在内存缓存中。

  2. 对于网站,请创建一个处理程序文件作为API,该文件将从内存缓存中检索数据并显示结果。

我已经在我的一个项目中实现了此功能,它可以在几毫秒内检索数千个数据

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM