【问题标题】:SQL statement much slower from ODBC than in T-SQLODBC 的 SQL 语句比 T-SQL 慢得多
【发布时间】:2016-02-19 02:12:36
【问题描述】:

我有一个 C/C++ DLL,它连接到 SQL 并在循环中快速发出大量 ODBC 查询。唯一的问题是,使用 ODBC DLL 比在 Management Studio 中从 T-SQL 运行查询要慢得多。慢很多数量级。

起初我以为这可能是查询本身,但后来我将其简化为一个简单的“select NULL”,仍然得到相同的结果。

我想知道这是否是预期的,或者是否有一些我丢失或出错的 ODBC 设置?

首先我像这样连接(为简洁起见,我省略了所有错误检查,但是,retcode 在所有情况下都返回 SQL_SUCCESS):

char *connString = "Driver={SQL Server};Server=.\\ENT2012;uid=myuser;pwd=mypwd";
...
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_UINTEGER);
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (void*)5, 0);
retcode = SQLDriverConnect(
    hdbc, 
    0,
    (SQLTCHAR*) connString,
    SQL_NTS, 
    connStringOut,
    MAX_PATH, 
    (SQLSMALLINT*)&connLen, 
    SQL_DRIVER_COMPLETE);

然后我准备语句,绑定一个参数(在本例中未使用),并像这样绑定一个列:

char queryString = "select NULL;";
SQLLEN g_int32 = 4;
SQLLEN bytesRead = 0;
...
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt)
retcode = SQLPrepare(hstmt, queryString, SQL_NTS);
retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, 
    SQL_C_LONG, SQL_INTEGER, sizeof(int), 0, spid, 0, (SQLLEN*)&g_int32))
retcode = SQLBindCol(hstmt, 1, SQL_C_CHAR, col_1, 32, &bytesRead);

最后,我在这样的循环中重复调用查询(例如,10000 次):

retcode = SQLExecute(hstmt);
retcode = SQLFetch(hstmt);
SQLCloseCursor(hstmt);

在 ODBC DLL 中运行 10000 次大约需要 90 秒。在运行 SQL 2012 x64 的 4 核 Windows 2008 R2 Server 上进行测试。

另一方面,如果我在 Management Studio 中运行,在我看来是一个等效的测试,它只需要不到一秒钟的时间:

declare @sql varchar(128), @repeat int;
set @repeat = 10000;
set @sql = 'select NULL;';
while @repeat > 0 begin
    exec(@sql);
    set @repeat = @repeat - 1;
end;

有人能指出我忽略的东西吗?我的逻辑有问题?

谢谢。

尼尔·韦彻

www.netlib.com

【问题讨论】:

  • 你的循环是包含连接还是只包含执行?
  • 在上下文中,您正在测量使用 ODBC 的 C++ DLL 大约 9 毫秒的开销——几个额外的代码层。这并不必然看起来不合理,尽管我希望会更小一些。这个问题可能更适合 dba.stackexchange.com。
  • @RemusRusanu - 循环只包括执行。连接和准备在开始时完成一次。
  • @GordonLinoff - 你会建议我删除问题并将其移到那里吗?
  • 顺便说一句 - Driver={SQL Server} 的选择与它有什么关系吗?

标签: c++ sql sql-server performance odbc


【解决方案1】:

评论太长了

declare @sql varchar(128), @repeat int;
set @repeat = 10000;
set @sql = 'select NULL;';
while @repeat > 0 begin
    exec(@sql);
    set @repeat = @repeat - 1;
end;

实际上并不能模拟 10000 次远程调用。 exec 绕过了很多设置请求的内部结构。要模拟 10000 次调用,请在 SSMS 中执行以下操作:

select NULL;
go 10000

并测量。可能应该使用文本输出来避免 SSMS 网格显示的计时。

【讨论】:

  • 感谢您的建议。我会试试的。
【解决方案2】:

我对 T-SQL 的东西不是很熟悉,但这里有几件事需要考虑。

您的 ODBC 驱动程序必须通过您的网络传输数据,我怀疑 T-SQL 执行不会。除了磁盘 IO,在网络上传输数据是 ODBC 驱动程序必须做的最慢的事情之一。您可能会发现驱动程序花费了大量时间,要么等待数据传输,要么从网络上清除数据。

此外,我不清楚您的 T-SQL 示例是否实际移动和数据,但您的 ODBC 示例在调用 SQLFetch 时会移动。 T-SQL 可能只是在执行查询,从不获取任何数据。因此,从循环中删除 SQLFetch 可能是一个更平等的比较。

要查看数据传输是否是您的限制因素,请估计您使用 ODBC 获取的所有记录中将包含多少数据,并尝试使用 FTP 在两台机器之间移动这些数据。 ODBC 驱动程序永远无法比简单的原始数据传输更快地获取数据。我看到您只是在结果集中获取 NULL,但驱动程序和数据库仍在它们之间传输数据以服务此请求。每次执行\fetch 可能是几百字节。

【讨论】:

  • 感谢您的反馈。两个测试都在本地机器上进行。您是否在 ODBC 设置中看到任何可能产生影响的内容?例如,Driver= 的选择是否会有所不同?
  • 通常情况下,ODBC 驱动程序仍然通过 TCP 进行通信,即使它们与数据库位于同一台机器上。我在 Oracle 自己的 ODBC 驱动程序(用于 Oracle)中看到了一些例外情况。 SQL Server 还允许通过命名管道进行连接。我已经搞砸了这么多,但我认为本地命名管道可能会为您的场景提供和优势。
  • 就连接字符串的 Driver= 部分而言,它指定了您正在使用的 ODBC 驱动程序。我猜你的驱动程序使用 Microsoft 的驱动程序,但“Driver=DataDirect 7.1 SQL Server Wire Protocol”会指定 DataDirect 驱动程序,假设它当然已安装。这可能会产生影响,因为并非所有驱动程序都是平等的;有些表现比其他人更好。你可以在这里阅读我们的:ow.ly/ACHDu
  • 请记住我之前关于获取数据的评论。在 yoru T-SQL 示例中,我没有看到任何消耗数据的东西。 SQLFetch 将告诉驱动程序从线路中提取数据并将其复制到应用程序。这将花费更多时间执行查询并忽略结果。
  • 谢谢,我会试一试。我是否像以前一样使用连接字符串?
【解决方案3】:

我遇到了同样的问题。我通过将 ODBC 驱动程序(通过 ODBC 管理器工具)的 DSN 的“光标默认模式”设置从“READ_ONLY”更改为“READ_ONLY_STREAMING”来解决这个问题。仅此一项就将我的应用程序(查询数据并将它们写入文件)的速度从使用 Java 32 位的 260 秒提高到了 51 秒,使用 C++ 从 1234 秒提高到了 11 秒。 看到这个帖子:http://www.etl-tools.com/forum/visual-importer/1587-question-about-data-transformation-memory-usage?start=6

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 2012-06-20
    • 1970-01-01
    相关资源
    最近更新 更多