使用 SQL-DMO 收缩 SQL Server 事务日志






4.99/5 (43投票s)
使用 SQL-DMO 缩减 SQL Server 事务日志。
引言
我们某个部门的员工安装了一个使用 MSDE 作为数据库服务器的应用程序。该应用程序经常无故挂起,直到我们最终发现导致这种奇怪行为的原因是事务日志增长过快。在查阅 MSDN 后,我发现缩减 MSDE/SQL Server 2000 的事务日志只需要 2 个简单的命令(请参阅 KB272318)。
BACKUP LOG %DB_NAME% WITH TRUNCATE_ONLY
DBCC SHRINKFILE (%LOG_NAME%, %LOG_SIZE%)
但是,由于该部门的员工对 MSDE 是什么以及如何使用 osql 工具运行这些命令一无所知,他们请我编写一个简单的向导应用程序来简化他们的工作。我编写的第一个版本使用了 MFC,而第二个版本我使用 WTL 实现,以避免拖入大小为 1 MB 的 mfc dll,并静态链接到 C 运行时。由于需要 C++ 异常支持,我无法最小化 ATL 中的 CRT 使用。
那么这个向导有什么功能呢?
它可以
- 缩减 SQL Server 数据库的事务日志。
- 缩减数据库后备份数据库。
- 创建 SQL Server 作业,该作业会在事务日志达到限制大小时自动缩减它们。
- 查看最终结果,即缩减后的事务日志大小。
- 将 SQL Server 企业管理器用户界面元素显示为其自身的用户界面元素。
关注点
您可以使用此向导作为执行 SQL-DMO 和 SQL-NS 不同任务的示例。向导的每个页面都将在单独的章节中,并且是以下内容的示例:
登录页面
- (SQL-DMO) 列出网络可用的 SQL Server。
- (SQL-DMO) 启动已停止的 SQL Server。
- (SQL-DMO) 使用 Windows 或 SQL Server 身份验证连接到 SQL Server。
数据库选择页
- (SQL-DMO) 列出 SQL Server 数据库,如果需要,排除系统数据库。
- (SQL-NS) 显示数据库属性(如果安装了 SQL-NS)。
- (SQL-DMO) 列出 SQL Server 数据库的事务日志。
- (SQL-DMO) 检索事务日志文件的属性。
备份页
- 格式化备份介质集属性(也支持拖放)。
- (SQL-DMO) 在备份前验证备份介质集。
缩减页
- (SQL-DMO) 启动作业服务器 (SQL Server Agent)。
- (SQL-DMO) 创建 SQL Server 作业及其步骤。
- (SQL-DMO) 创建触发所创建作业的 SQL Server 警报。
- (SQL-DMO) 启动 SQL Server 作业(已注释)。
- (SQL-DMO) 缩减事务日志。
- (SQL-NS) 使用 SQL Server 企业管理器 UI 备份选定的数据库。
- (SQL-DMO) 备份选定的数据库。
- (SQL-DMO) 使用事件接收器处理备份对象事件(PercentComplete、Complete 和 NextMedia 事件)。(有关更多详细信息,请参阅
IBackupSink
源代码)。 - (SQL-DMO) 备份完成后验证备份介质集文件。
杂项
- 执行 SQL Server 服务管理器。
- 执行 SQL Server 企业管理器。
- 精简 CRT 和全局堆内存。
- 显示 COM 错误。
- 显示 Win32 错误。
实现
我将不再深入探讨细节,因为源代码本身就很容易理解。只说几句:
此向导使用 Microsoft SQL-DMO (SQL Distributed Management Objects) 和 SQL-NS (SQL Namespace API) 来执行其任务。SQL-DMO 允许用支持 Automation 或 COM 的语言编写的应用程序执行 SQL Server 企业管理器执行的所有功能。
SQL-NS 允许用支持 Automation 或 COM 的语言编写的应用程序将 SQL Server 企业用户界面的部分包含在其自己的用户界面中。
请参阅下图。SQL-NS 建立在 SQL-DMO 之上。
几年前,我编写了一个 Visual Basic 应用程序,该应用程序使用 SQL-DMO 将 Microsoft Access 数据库转换为 SQL Server 数据库(由于循环关系,我们在 SQL 脚本和 SQL Server 导入向导方面遇到了问题)。
我记得,要使用 SQL-DMO 对象,我只需要从 VB 项目中添加对其类型库的引用。但是,从 VC 5.0 开始,我们就有编译器内置的 COM 支持——只需使用 #import
预处理器指令,编译器就会读取类型库并为 COM 接口生成 C++ 包装类和智能指针。因此,我们获得了 VB 的便捷性和 C++ 的强大功能。我之所以提到这一点,是因为绝大多数抱怨是几乎所有的示例都是用 VB 实现的——但将它们转换为 C++ 只是一件小事。此外,使用编译器 COM 支持允许我们在 MFC 和 ATL 中使用相同的代码,甚至不使用任何框架(旧的 MFC 项目也可供下载)。只需查看 示例代码。
处理 SQL-DMO 事件会稍微复杂一些。SQL-DMO 的 Backup
、BulkCopy
、Replication
、Restore
、SQLServer
和 Transfer
对象是可连接的 COM 对象,支持回调到客户端应用程序。我们必须实现函数来处理来自服务器的回调,称为接收器。使用 IConnectionPoint
接口,客户端通知服务器其处理回调的能力,并提供其接收器实现作为参数。客户端实现的接收器是 COM 对象。与任何 COM 应用程序开发任务一样,使用 C++ 实现任何 SQL-DMO 可连接对象的接收器都相当容易。客户端应用程序定义一个类,继承自定义的 SQL-DMO 接收器接口定义,然后实现成员来处理感兴趣的回调。IBackupSink
和 缩减页 (CWizPgShrink
) 的源代码说明了如何实现一个可以处理备份对象事件的 COM 对象(您还可以使用 _COM_SMARTPTR_TYPEDEF
宏来生成 IConnectionPointContainer
和 IConnectionPoint
智能指针)。
SQL-NS 的使用也通过 #import
指令来导入类型库,并为 SQL-NS 的 COM 接口生成 C++ 包装类和智能指针。请参阅 示例代码。
示例代码 (SQL-DMO)
这是一个缩减 "Northwind" 数据库事务日志的示例代码。
// // g_spSqlServer2 is SQL Server object global pointer static LPCTSTR spszDbs = _T( "[Northwind]" ); const LONG lLogSize = 2; // try { SQLDMO::_Backup2Ptr spBackup2 = NULL; _CREATEINSTANCE( spBackup2, SQLDMO::Backup2 ); spBackup2->Database = (LPCTSTR)spszDbs; spBackup2->Initialize = (VARIANT_BOOL)( TRUE ); spBackup2->Action = (SQLDMO::SQLDMO_BACKUP_TYPE)SQLDMO::SQLDMOBackup_Log; spBackup2->TruncateLog = (SQLDMO::SQLDMO_BACKUP_LOG_TYPE)SQLDMO::SQLDMOBackup_Log_TruncateOnly; spBackup2->SQLBackup( g_spSqlServer2 ); SQLDMO::_Database2Ptr spDatabase2 = g_spSqlServer2->Databases->Item( (LPCTSTR)spszDbs ); SQLDMO::LogFilesPtr spLogFiles = spDatabase2->TransactionLog->LogFiles; SQLDMO::_LogFile2Ptr spLogFile2 = NULL; LONG lCount = spLogFiles->Count; for( LONG l = 1; l <= lCount; l++ ) { spLogFile2 = spLogFiles->Item( l ); if( spLogFile2->Size > lLogSize ) spLogFile2->Shrink( lLogSize, SQLDMO::SQLDMOShrink_Default ); } } catch( IN _com_error e ) { // Display com error } //
示例代码 (SQL-NS)
这是一个像 SQL Server 企业管理器那样显示 "Northwind" 数据库属性的示例代码。
// // g_spSqlNamespace is ISQLNamespace object global pointer static LPCTSTR spszDbs = _T( "[Northwind]" ); // try { LONG lServer = g_spSqlNamespace->GetRootItem(); LONG lDatabases = spSqlNamespace->GetFirstChildItem( lServer, SQLNS::SQLNSOBJECTTYPE_DATABASES, (LPCTSTR)NULL ); LONG lDatabase = spSqlNamespace->GetFirstChildItem( lDatabases, SQLNS::SQLNSOBJECTTYPE_DATABASE, (LPCTSTR)spszDbs ); SQLNS::SQLNamespaceObjectPtr spSQLNSNamespaceObj = g_spSqlNamespace->GetSQLNamespaceObject( lDatabase ); spSQLNSNamespaceObj->ExecuteCommandByID( SQLNS::SQLNS_CmdID_PROPERTIES, (LONG)(HWND)NULL, SQLNS::SQLNamespace_PreferModeless ); } catch( IN _com_error e ) { // Display com error } //
全局函数
GetSqlServerPath
- 正如您在上面的图片中看到的,如果 SQL Server 服务管理器和 SQL Server 企业管理器在目标机器上可用,程序就可以执行它们。SQL Server 工具文件夹的路径可以从注册表 HKLM\SOFTWARE\Microsoft\Microsoft SQL Server \80\Tools\ClientSetup 键的 SQLPath 值中检索。因此,将 "Binn\sqlmangr.exe" 附加到检索到的值就构成了 SQL Server 服务管理器的完整路径,将 "Binn\SQL Server Enterprise Manager.msc" 附加到检索到的值就构成了 SQL Server 企业管理器的完整路径,我们可以将它们传递给::ShellExecute
或::ShellExecuteEx
函数。HKLM\SOFTWARE\Microsoft\MSSQLServer\Setup 注册表键的 SQLPath 值包含 SQL Server 数据根文件夹。
GetSqlServerPath
函数为您完成了所有这些工作。DisplayComError
- 您认为它做什么?SkipODBCPrefix
- SQL-DMO 的 ODBC 错误消息带有前缀,例如 [Microsoft][ODBC SQL Server Driver][SQL Server][SQL DMO]msg... 或类似的内容。尽管将 SQL Server 对象ODBCPrefix
属性设置为 false 可以移除它们,但有时这些前缀仍然被附加。您可以使用此函数来移除剩余的部分。FormatMessage
- 从已加载模块的消息表资源格式化 Win32 API 消息字符串。TrimWorkingSet
- 该函数压缩 CRT 和全局堆,并将进程的工作集临时修剪为零,这实际上减少了应用程序对物理 RAM 内存的使用。DoEvents
- 来自 Visual Basic 的恐龙。它会将程序执行权交给操作系统处理所有待处理的消息。在密集循环中有用。
已知bug
- 我的
CMenuButton
(见图)不对反射消息做出响应。我必须手动向它发送BN_CLICK
。 - 如果
CMenuButton
下方没有空间来显示其菜单,它会弹出菜单,但按钮会被菜单遮挡。 - 如果您在远程服务器上缩减事务日志,则不支持备份(它会给出路径错误)。我必须尝试使用临时备份设备来查看问题是否仍然存在。
- 在备份前验证备份介质集有时会给出错误(尝试添加两个相同的介质集文件和一个新文件)。我非常不喜欢这部分代码。而且更不喜欢之前的实现(它已被注释掉)。如果有人有任何改进此处的想法,我将非常感激。
未知 bug
这是我的第一个 WTL 应用程序。请注意许多未知的和其它类型的 bug :) 欢迎所有改进、优化和修复。
项目依赖项
- Microsoft Platform SDK 2003
- Microsoft WTL (Windows Template Library) 7.1
- Microsoft SQL-DMO (SQL Distributed Management Objects) 8.0
- Microsoft SQL-NS (SQL Namespace API) 8.0
- Microsoft SQL-DMO 和 SQL-NS 8.0 开发工具(随 Microsoft SQL Server 2000 安装)
程序依赖项
- Microsoft SQL-DMO 8.0
- Microsoft Windows 2000 或更高版本(不支持 Windows 9x)
项目依赖项说明
我已从项目中删除了 rcstamp.exe(用于增加项目构建号的工具),以减小其大小。如果您想自动增加项目构建号,请下载 RCStamp 工具,将其复制到解决方案的 bin 子目录中,并在项目的生成后步骤中添加以下行:
对于 VC 6.0: "$(WkspDir)\bin\RCStamp.exe" "$(WkspDir)\$(TargetName) \$(TargetName).rc" *.*.+.*
对于 VC 7.0 及更高版本: "$(SolutionDir)\bin\RCStamp.exe" "$(ProjectDir)$(TargetName).rc" *.*.+.*
此项目最初是用 VS.NET 2003 开发的,然后手动转换为 VC 6.0,并使用 将 VS.NET 2003 项目文件转换为 VS.NET 2002 的实用工具转换为 VS.NET 2002。原始 VS.NET 2003 解决方案文件命名为 tlsw.sln.2003 和 tlswa.vcproj.2003 - 您可以将其重命名回原来的名称。
程序设置
安装程序 inf 文件也包含在项目中。您可以使用 IExpress 工具构建一个小型的安装程序 exe。IExpress 随 Microsoft Internet Explorer Administration Kit 一起提供——它允许构建小型快速的安装包。我的安装程序 inf 文件将应用程序可执行文件和帮助文件复制到 Windows 系统文件夹(因为这是一个管理工具),在 [开始菜单]>[程序]> [公司名称]>[事务日志缩减向导] 目录中创建指向它们的快捷方式,并注册卸载所需的信息(您可以在控制面板的 [添加/删除程序] 中将其卸载)。注意:不包含 SQL-DMO 和 SQL-NS 的安装脚本。创建安装程序 inf 文件稍微偏离主题,我将在另一篇文章中介绍该主题。
致谢
- 由 peterchen 提供的用于增加项目构建号的 RC Stamp 工具。
- 感谢 Robert Edward Caldecott 提供了使用 Marlett 字体显示字形的绝妙想法。我在 MSDN(创建向导)中发现了这个技巧,是在下载了他的文章代码(参见向导介绍页的项目符号)之后。
- 同一个人 Robert Edward Caldecott 编写的 WTL 自适应属性表。
- 由 Paul Bludov 编写的 WTL bug。
- 由 Michael Dunn 编写的文章 MFC 程序员的 WTL。
- 由 Ed Gadziemski 提供的 十个 WTL 技巧和窍门。
- 由 dacris 提供的 将 VS.NET 2003 项目文件转换为 VS.NET 2002 的实用工具。
历史
- 2004 年 4 月 19 日 - 发布了文章。