Kev*_*vin 7 c# sql-server performance ssms sqldatareader
我正在做一个简单的SQL查询来获取大量数据.查询的复杂性不是问题.执行大约需要200ms.但是,数据量似乎是个问题.
我们检索大约40k行.每行有8列,每行数据量约为几百千字节.比如说,我们为此查询共下载15megs.
让我难以置信的是:当我从一个基本的C#代码执行查询时,需要1分钟和44秒.但是当我从SSMS做到它需要10秒.当然我是从同一台机器上做的,而且我使用的是同一个数据库.我清楚地看到UI和实时填充的行.在10secs中,整个数据表已满.
我们尝试了:
它没有改变任何东西.有道理:它的读取速度很慢.不是查询(恕我直言).
这while(reader.Read())需要时间.而且,我们尝试了一个空的while循环.所以这不包括装箱/拆箱的东西或将结果放在内存中.
这是一个测试程序,我们弄清楚它是Read()需要时间:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Transactions;
namespace SqlPerfTest
{
class Program
{
const int GroupId = 1234;
static readonly DateTime DateBegin = new DateTime(2017, 6, 19, 0, 0, 0, DateTimeKind.Utc);
static readonly DateTime DateEnd = new DateTime(2017, 10, 20, 0, 0, 0, DateTimeKind.Utc);
const string ConnectionString = "CENSORED";
static void Main(string[] args)
{
TransactionOptions transactionOptions = new TransactionOptions
{
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted
};
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
SetOptimizations(connection);
ShowUserOptions(connection);
DoPhatQuery(connection).Wait(TimeSpan.FromDays(1));
}
transactionScope.Complete();
}
}
static void SetOptimizations(SqlConnection connection)
{
SqlCommand cmd = connection.CreateCommand();
Console.WriteLine("===================================");
cmd.CommandText = "SET QUOTED_IDENTIFIER ON";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET ANSI_NULL_DFLT_ON ON";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET ANSI_PADDING ON";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET ANSI_WARNINGS ON";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET ANSI_NULLS ON";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET CONCAT_NULL_YIELDS_NULL ON";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET ARITHABORT ON";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET DEADLOCK_PRIORITY -1";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET QUERY_GOVERNOR_COST_LIMIT 0";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
cmd.CommandText = "SET TEXTSIZE 2147483647";
cmd.ExecuteNonQuery();
Console.WriteLine(cmd.CommandText);
}
static void ShowUserOptions(SqlConnection connection)
{
SqlCommand cmd = connection.CreateCommand();
Console.WriteLine("===================================");
cmd.CommandText = "DBCC USEROPTIONS";
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
Console.WriteLine(cmd.CommandText);
while (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0} = {1}", reader.GetString(0), reader.GetString(1));
}
reader.NextResult();
}
}
}
static async Task DoPhatQuery(SqlConnection connection)
{
Console.WriteLine("===================================");
SqlCommand cmd = connection.CreateCommand();
cmd.CommandText =
@"SELECT
p.[Id],
p.[UserId],
p.[Text],
FROM [dbo].[Post] AS p WITH (NOLOCK)
WHERE p.[Visibility] = @visibility
AND p.[GroupId] = @groupId
AND p.[DatePosted] >= @dateBegin
AND p.[DatePosted] < @dateEnd
ORDER BY p.[DatePosted] DESC
OPTION(RECOMPILE)";
cmd.Parameters.Add("@visibility", SqlDbType.Int).Value = 0;
cmd.Parameters.Add("@groupId", SqlDbType.Int).Value = GroupId;
cmd.Parameters.Add("@dateBegin", SqlDbType.DateTime).Value = DateBegin;
cmd.Parameters.Add("@dateEnd", SqlDbType.DateTime).Value = DateEnd;
Console.WriteLine(cmd.CommandText);
Console.WriteLine("===================================");
DateTime beforeCommit = DateTime.UtcNow;
using (SqlDataReader reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection))
{
DateTime afterCommit = DateTime.UtcNow;
Console.WriteLine("Query time = {0}", afterCommit - beforeCommit);
DateTime beforeRead = DateTime.UtcNow;
int currentRow = 0;
while (reader.HasRows)
{
while (await reader.ReadAsync())
{
if (currentRow++ % 1000 == 0)
Console.WriteLine("[{0}] Rows read = {1}", DateTime.UtcNow, currentRow);
}
await reader.NextResultAsync();
}
Console.WriteLine("[{0}] Rows read = {1}", DateTime.UtcNow, currentRow);
DateTime afterRead = DateTime.UtcNow;
Console.WriteLine("Read time = {0}", afterRead - beforeRead);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我们重现与SSMS相同的SET内容.我们还尝试了人类已知的所有技巧来加速一切.使用异步的东西.使用WITH(NOLOCK),NO RECOMPILE,在连接字符串中定义更大的PacketSize没有帮助,并使用顺序读取器.SSMS的速度仍然快50倍.
更多信息
我们的数据库是Azure数据库.我们实际上有2个数据库,一个在欧洲,一个在美国西部.由于我们位于欧洲,因此当我们使用欧洲数据库时,相同的查询会更快.但它仍然像30秒,在SSMS中就像是瞬间.数据传输速度确实会影响这一点,但这不是主要问题.
我们还可以通过减少列数来减少数据传输的时间.它确实加快了Read()迭代过程.假设我们只检索我们的ID列:那么我们有一个while(Read())持续5秒.但它不是一个选项,因为我们需要所有这些列.
我们知道如何"解决"这个问题:我们可以以不同的方式处理我们的问题,并且每天进行小型查询并将这些结果缓存在Azure表或其他内容中.但我们想知道为什么 SSMS更快.有什么诀窍.
我们在C#中使用了Entity Framework,在C#中使用了Dapper,上面的示例就像本地C#.我在interwebz中看到了一些可能存在类似问题的人.对我来说,感觉就像SqlDataReader是那么慢.比如,它不会使用多个连接或其他东西来管理行的下载.
题
所以我的问题是:管理工作室如何设法下载查询结果的速度快50倍?有什么诀窍?
多谢你们.
| 归档时间: |
|
| 查看次数: |
968 次 |
| 最近记录: |