极具威力的 DBPROP_CLIENTCURSOR

9 篇文章 0 订阅
3 篇文章 0 订阅

今天做了一个测试,结果令我非常吃惊,没想到会有这么大的差异。

原本是想测试 C# 的 ADO.NET 的性能和 C++ 的 OLEDB 的性能差异如何的。原以为应该是 OLEDB 优胜于 ADO.NET 的,但是结果却相反。

数据库是 SQL Server 2005 的,数据库和测试机是同一局域网的两台高性能机器,之间是千兆网连接的。测试是用 C# 使用 SqlDataReader 遍历一个有 38 万条记录的表,只读取 3 个小字段;而 C++ 则使用 OLEDB Templates 做同样的事情。

结果非常悬殊,ADO.NET 打开数据集用时平均在 100 毫秒左右,而 OLEDB 则是 1200 毫秒左右,差不多 12 倍的差别,读取时间 ADO.NET 是 700 毫秒左右,而 OLEDB 则是 110 多秒左右,有 150 多倍的差别。

这样的结果,感觉不可思议,怎么 C++ 的性能还不如 C# 吗?而且还相差这么悬殊?是哪里出的问题呢?

想了一下,估计和 OLEDB 的使用方式有关系。C++ 的 OLEDB 访问代码是直接用向导生成的,记得以前使用都要加一些处理,找回以前的代码看了一下,发现是以前的代码都有使用 DBPROP_CLIENTCURSOR 优化的。加了一下再测试。

结果打开数据集的时间变成 1000 毫秒左右,读取时间则是 600 毫秒左右,和之前比较差别非常明显。以前也知道使用客户端游标能够提高访问性能,但是没想到的是这样大的差别。

而且测试还反映了另一个问题,就是 ADO.NET 使用 SqlDataReader 比 OLEDB 还快一点点。估计是由于 SqlDataReader 内部是直接使用 SQL Server 专门的 API 优化了访问的,所以有这样的微小性能差别。

这个测试同时还说明了,有时候设想的情况并不见得就是对的,特别是性能的优化,一定是要进行实际测试和分析的,不能想当然。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是用ADO更新BLOB字段的C代码: ``` #include <windows.h> #include <oledb.h> #include <stdio.h> void update_blob_field() { HRESULT hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { // Connection string for your database char connStr[256] = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\myDatabase.mdb;Persist Security Info=False;"; // Initialize ADO objects IDBInitialize *pInitialize = NULL; IDBCreateSession *pCreateSession = NULL; IDBCreateCommand *pCreateCommand = NULL; hr = CoCreateInstance(CLSID_MSDASQL, NULL, CLSCTX_INPROC_SERVER, IID_IDBInitialize, (void**)&pInitialize); if (SUCCEEDED(hr)) { // Set DB initialization properties hr = pInitialize->Initialize(); if (SUCCEEDED(hr)) { // Create session hr = pInitialize->QueryInterface(IID_IDBCreateSession, (void**)&pCreateSession); if (SUCCEEDED(hr)) { DBPROP rgProps[1]; rgProps[0].dwPropertyID = DBPROP_AUTH_USERID; rgProps[0].dwOptions = DBPROPOPTIONS_REQUIRED; rgProps[0].colid = DB_NULLID; rgProps[0].vValue.vt = VT_BSTR; rgProps[0].vValue.bstrVal = SysAllocString("myUsername"); // Connect to database IDBSession *pSession = NULL; hr = pCreateSession->CreateSession(NULL, IID_IDBSession, (IUnknown**)&pSession); if (SUCCEEDED(hr)) { hr = pSession->SetConnectionProperties(1, rgProps); // Create command pSession->QueryInterface(IID_IDBCreateCommand, (void**)&pCreateCommand); // Issue update command for BLOB field char updateSQL[256] = "UPDATE myTable SET myBlobField = ? WHERE myID = 1"; ICommandText *pCommandText = NULL; hr = pCreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown**)&pCommandText); if (SUCCEEDED(hr)) { hr = pCommandText->SetCommandText(DBGUID_DBSQL, updateSQL); if (SUCCEEDED(hr)) { // Set up parameter object for BLOB field IColumnsInfo *pColumnsInfo = NULL; pCommandText->QueryInterface(IID_IColumnsInfo, (void**)&pColumnsInfo); DBCOLUMNINFO *pColumnInfo = (DBCOLUMNINFO*)malloc(sizeof(DBCOLUMNINFO)); pColumnsInfo->GetColumnInfo(1, pColumnInfo, NULL, NULL, NULL, NULL); IAccessor *pIAccessor = NULL; pSession->QueryInterface(IID_IAccessor, (void**)&pIAccessor); HACCESSOR hAccessor; pIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, 1, &pColumnInfo->columnid, sizeof(DBPARAMETER), &hAccessor, NULL); DBPARAMETER param; param.dwParameterID = pColumnInfo->columnid; param.dwFlags = 0; param.iid = IID_NULL; param.pTypeInfo = NULL; param.pObject = NULL; param.pwszName = NULL; param.pwszDataSourceType = NULL; // Allocate a buffer for our BLOB data, and fill it with our desired data BYTE *pBlobData = (BYTE*)malloc(100000); // (In practice, you might want to read this data from a file or some other source) for (int i = 0; i < 100000; i++) { pBlobData[i] = (BYTE)(i % 256); } // Set value for BLOB field DBBINDING binding; binding.iOrdinal = 1; binding.dwPart = DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH; binding.eParamIO = DBPARAMIO_INPUT; binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED; binding.dwFlags = 0; binding.cbMaxLen = 100000; binding.cbMaxLen = 100000; binding.wType = DBTYPE_IUNKNOWN; binding.obStatus = 0; binding.obLength = 0; binding.obValue = 0; binding.pTypeInfo = NULL; binding.pObject = NULL; binding.pBindExt = NULL; binding.pDataSourceType = NULL; binding.pBindExt = NULL; binding.pObject = NULL; binding.dwReserved = 0; DBPARAMBINDING paramBinding; paramBinding.dwParamOrdinal = 1; paramBinding.pwszDataSourceType = NULL; paramBinding.dwFlags = 0; paramBinding.pwszName = NULL; paramBinding.iOrdinal = 1; DB_UPARAMS cParams = 1; DBPARAMINFO *rgParamInfo = NULL; DBCOUNTITEM cParamSets = 1; HROWSET hRowset; IRowsetChange *pRowset = NULL; IDBCreateCommand *pCommand = NULL; DBROWCOUNT cRowsAffected = 0; pCommandText->Execute(NULL, IID_IRowsetChange, NULL, &cRowsAffected, (IUnknown**)&pRowset); pCommandText->GetParameterInfo(&cParams, &rgParamInfo); rgParamInfo[0].cbMaxLen = 100000; rgParamInfo[0].dwFlags = 0; paramBinding.cbMaxLen = rgParamInfo[0].cbMaxLen; binding.cbMaxLen = rgParamInfo[0].cbMaxLen; IRowsetChange *pRowsetChange = NULL; pCommandText->QueryInterface(IID_IRowsetChange, (void**)&pRowsetChange); HROW hRow; pRowset->InsertRow(NULL, hAccessor, pBlobData, &hRow); IRowsetChange *pIRowsetChange = NULL; pCommandText->QueryInterface(IID_IRowsetChange, (void**)&pIRowsetChange); pIRowsetChange->SetData(hRow, hAccessor, pBlobData); // Commit transaction ITransactionLocal *pTransaction = NULL; pSession->QueryInterface(IID_ITransactionLocal, (void**)&pTransaction); DBPARAMS params; params.cParamSets = cParamSets; params.hAccessor = hAccessor; params.pData = pBlobData; params.rgParamIO = NULL; params.cbParamIO = 0; params.pVarData = NULL; params.rghCursors = NULL; params.rgRowStatus = NULL; params.hChapter = 0; params.cParamBindings = 1; params.rgParamBindings = &paramBinding; hr = pTransaction->StartTransaction(ISOLATIONLEVEL_READCOMMITTED,ISOLEVEL_CHAOS,0,NULL); hr = pRowsetChange->SetData(hRow, hAccessor, pBlobData); pRowsetChange->Update(hRow, NULL, NULL, &cRowsAffected); hr = pTransaction->Commit(FALSE, XACTTC_ASYNC_PHASEONE, 0); if (!SUCCEEDED(hr)) { printf("Could not commit transaction!"); } // Clean up ADO objects pCommand->Release(); pRowsetChange->Release(); pRowset->Release(); pIRowsetChange->Release(); pIAccessor->Release(); pCommandText->Release(); pColumnsInfo->Release(); CoFreeUnusedLibraries(); CoUninitialize(); free(pColumnInfo); free(pBlobData); } } } } } } } } ``` 请注意,这段代码在编写时并未测试。一些细节可能需要调整和完善,以确保它按您预期的方式运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值