SQL Server故障排查与解决方案全解析

引言

SQL Server是企业最常用的关系型数据库之一,广泛应用于各类业务系统。作为数据库管理员或开发人员,需要快速识别并解决SQL Server可能出现的各类故障问题。本文将系统介绍SQL Server常见故障类型、排查工具及解决方案,帮助技术人员更高效地处理数据库异常情况。
在这里插入图片描述

常见故障类型及排查方法

1. 性能问题

1.1 高CPU使用率

症状

  • 系统响应缓慢
  • SQL Server进程CPU使用率持续高于80%
  • 任务管理器显示sqlservr.exe占用大量CPU资源

排查方法

-- 查找当前消耗CPU资源最多的查询
SELECT top 20
    req.session_id,
    req.status,
    req.command,
    req.cpu_time,
    req.total_elapsed_time,
    stmt.text AS query_text,
    p.query_plan
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(req.sql_handle) stmt
OUTER APPLY sys.dm_exec_query_plan(req.plan_handle) p
ORDER BY req.cpu_time DESC;

解决方案

  • 优化高耗CPU的查询,添加合适的索引
  • 检查是否存在过度编译和重编译问题
  • 使用查询存储(Query Store)跟踪和优化查询性能
  • 适当调整"最大并行度"(MAXDOP)配置
1.2 内存不足

症状

  • 错误日志中出现内存相关错误(如701、802、8645等)
  • 性能突然下降
  • 查询执行缓慢或超时

排查方法

-- 检查SQL Server的内存使用情况
SELECT
    physical_memory_kb/1024 AS physical_memory_mb,
    virtual_memory_kb/1024 AS virtual_memory_mb,
    committed_kb/1024 AS committed_mb,
    committed_target_kb/1024 AS committed_target_mb
FROM sys.dm_os_sys_info;

-- 检查内存占用情况
SELECT TOP 10
    [type],
    SUM(pages_kb)/1024 AS size_mb
FROM sys.dm_os_memory_clerks
GROUP BY [type]
ORDER BY SUM(pages_kb) DESC;

解决方案

  • 调整SQL Server最大内存配置限制
  • 释放内存缓存:DBCC FREESYSTEMCACHE ('ALL')
  • 释放过程缓存:DBCC FREEPROCCACHE
  • 检查并终止长时间运行的查询
  • 增加服务器物理内存
1.3 阻塞和死锁

症状

  • 查询长时间处于阻塞状态
  • 系统性能突然下降
  • 错误日志中出现死锁相关信息

排查方法

-- 检查当前阻塞情况
;WITH BlockingTree (blocking_session_id, session_id, level)
AS (
    -- 锁的源头(阻塞源头)
    SELECT DISTINCT
        r.blocking_session_id,
        r.session_id,
        0 AS level
    FROM sys.dm_exec_requests r
    WHERE r.blocking_session_id <> 0
        AND r.blocking_session_id not IN (SELECT session_id FROM sys.dm_exec_requests)
    
    UNION ALL
    
    -- 被阻塞的会话
    SELECT
        r.blocking_session_id,
        r.session_id,
        level + 1
    FROM sys.dm_exec_requests r
    INNER JOIN BlockingTree bt ON bt.session_id = r.blocking_session_id
)
SELECT 
    REPLICATE('  ', level) + CAST(s.session_id AS VARCHAR) AS session_id,
    DB_NAME(r.database_id) AS database_name,
    s.login_name,
    SUBSTRING(
        qt.text, 
        r.statement_start_offset / 2 + 1,
        (CASE WHEN r.statement_end_offset = -1 
            THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 
            ELSE r.statement_end_offset 
        END - r.statement_start_offset) / 2 + 1
    ) AS current_statement,
    r.wait_type,
    r.wait_time / 1000.0 AS wait_time_seconds,
    r.wait_resource
FROM BlockingTree bt
LEFT JOIN sys.dm_exec_requests r ON r.session_id = bt.session_id
LEFT JOIN sys.dm_exec_sessions s ON s.session_id = bt.session_id
OUTER APPLY sys.dm_exec_sql_text(r.sql_handle) qt
ORDER BY level, bt.session_id;

解决方案

  • 使用KILL命令终止长时间阻塞的会话
  • 优化事务设计,缩短事务持续时间
  • 考虑使用乐观并发控制
  • 修改隔离级别
  • 使用死锁监控扩展事件

2. 数据库备份与恢复问题

2.1 备份失败

症状

  • 备份作业失败
  • 错误日志中出现空间不足或权限问题

排查方法

  • 检查备份目标磁盘空间
  • 验证SQL Server服务账户对备份目录的权限
  • 查看SQL Server错误日志

解决方案

  • 清理备份目标磁盘空间
  • 授予SQL Server服务账户适当权限
  • 适当调整备份策略(如差异备份、日志备份等)
  • 实现压缩备份减少空间占用
2.2 数据库恢复失败

症状

  • 恢复过程失败
  • 数据库标记为"恢复挂起"或"可疑"状态

排查方法

  • 查看SQL Server错误日志
  • 使用DBCC CHECKDB验证备份完整性
  • 检查恢复操作参数

解决方案

  • 使用可靠的备份集进行恢复
  • 尝试使用WITH CONTINUE_AFTER_ERROR选项恢复
  • 如可接受,使用WITH REPLACE选项
  • 对于损坏的数据库,使用DBCC修复选项

3. 数据库损坏

3.1 页面级损坏

症状

  • 查询特定表时出现824、823错误
  • 错误日志中出现页面校验和错误

排查方法

-- 检查数据库一致性
DBCC CHECKDB('数据库名') WITH NO_INFOMSGS, ALL_ERRORMSGS;

-- 检查特定表
DBCC CHECKTABLE('表名') WITH NO_INFOMSGS, ALL_ERRORMSGS;

解决方案

  • 从最近有效备份恢复
  • 如无可用备份,尝试DBCC修复选项:
    DBCC CHECKDB('数据库名', REPAIR_ALLOW_DATA_LOSS);
    
    注意:此操作可能导致数据丢失
  • 使用页面级还原(如果有页级备份)
3.2 系统表损坏

症状

  • 无法访问系统目录视图
  • 数据库启动失败
  • 元数据查询失败

排查方法

  • 检查SQL Server错误日志
  • 尝试访问系统表视图

解决方案

  • 从备份恢复数据库
  • 如无备份,可能需要创建新数据库并导入用户数据

深入排查工具

1. 系统动态管理视图(DMV)

DMV是SQL Server中最重要的排查工具之一,可以查看服务器内部状态。

-- 查看等待统计
SELECT TOP 10
    wait_type,
    waiting_tasks_count,
    wait_time_ms,
    wait_time_ms / waiting_tasks_count AS avg_wait_time_ms
FROM sys.dm_os_wait_stats
WHERE waiting_tasks_count > 0
ORDER BY wait_time_ms DESC;

-- 查看缓存命中率
SELECT
    (a.cntr_value * 1.0 / b.cntr_value) * 100.0 AS buffer_cache_hit_ratio
FROM sys.dm_os_performance_counters a
JOIN sys.dm_os_performance_counters b
    ON b.object_name = a.object_name
WHERE a.counter_name = 'Buffer cache hit ratio'
    AND b.counter_name = 'Buffer cache hit ratio base';

2. 扩展事件(Extended Events)

扩展事件比SQL Trace更强大、更轻量,是排查高级问题的首选工具。

-- 创建用于捕获死锁的扩展事件会话
CREATE EVENT SESSION [Deadlock_Capture] ON SERVER 
ADD EVENT sqlserver.xml_deadlock_report
ADD TARGET package0.event_file(SET filename=N'C:\temp\Deadlocks.xel')
WITH (MAX_MEMORY=4096 KB,
      EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,
      MAX_DISPATCH_LATENCY=30 SECONDS,
      MAX_EVENT_SIZE=0 KB,
      MEMORY_PARTITION_MODE=NONE,
      TRACK_CAUSALITY=OFF,
      STARTUP_STATE=OFF)
GO

-- 启动会话
ALTER EVENT SESSION [Deadlock_Capture] ON SERVER STATE = START;

3. SQL Server日志

SQL Server错误日志是排查问题的重要资源。

-- 查看SQL Server错误日志
EXEC sp_readerrorlog;

-- 查看特定错误
EXEC sp_readerrorlog 0, 1, 'error', 'failure';

4. 性能监控工具

CPU高
内存不足
I/O慢
阻塞
未解决
已解决
性能问题发生
收集性能数据
分析瓶颈
查询优化
内存配置
存储优化
锁优化
解决方案实施
效果评估
监控和预防

常见故障解决实战

1. TempDB争用问题

症状

  • 高并发环境下性能下降
  • 等待类型为PAGELATCH_EX或PAGELATCH_SH,且涉及到TempDB

排查方法

-- 检查TempDB等待情况
SELECT
    session_id,
    wait_type,
    wait_duration_ms,
    blocking_session_id,
    resource_description
FROM sys.dm_os_waiting_tasks
WHERE wait_type LIKE 'PAGELATCH_%'
AND resource_description LIKE '2:%'; -- TempDB的数据库ID是2

解决方案

  • 增加TempDB数据文件数量(通常设置为CPU核心数,但不超过8个)
  • 将TempDB文件放在不同的磁盘上
  • 预先分配适当的TempDB大小,避免自动增长
  • 启用Trace Flag 1118(SQL Server 2016之前)

2. 网络连接问题

症状

  • 客户端报连接错误
  • 连接间歇性中断
  • 错误信息包含"socket错误"或"named pipes"等

排查方法

  • 检查SQL Server网络配置
  • 验证客户端能否ping通服务器
  • 检查防火墙设置
  • 查看SQL Server错误日志

解决方案

  • 确保SQL Server网络协议正确启用
  • 验证防火墙允许SQL Server端口(默认1433)
  • 检查客户端连接字符串
  • 通过SQL Server配置管理器验证服务状态

3. 数据库恢复模式与日志增长问题

症状

  • 事务日志文件迅速增长
  • 磁盘空间不足警告
  • 备份操作耗时增加

排查方法

-- 检查数据库日志使用情况
DBCC SQLPERF(LOGSPACE);

-- 检查日志恢复模式
SELECT name, recovery_model_desc FROM sys.databases;

解决方案

  • 根据需要配置适当的恢复模式:
    • 完整恢复模式:需要定期执行日志备份
    • 简单恢复模式:适用于不需要时间点恢复的数据库
  • 执行日志备份释放日志空间
  • 如紧急情况,可使用BACKUP LOG WITH TRUNCATE_ONLY(仅SQL Server 2000)或在简单恢复模式下执行CHECKPOINT

C#代码示例:监控SQL Server健康状态

下面提供一个C#代码示例,用于监控SQL Server的关键健康指标:

using System;
using System.Data;
using System.Data.SqlClient;

/// <summary>
/// SQL Server健康状态监控工具
/// 用于实时检测SQL Server的关键健康指标
/// </summary>
public class SqlServerHealthMonitor
{
    private string _connectionString;
    
    public SqlServerHealthMonitor(string connectionString)
    {
        _connectionString = connectionString;
    }
    
    /// <summary>
    /// 检查SQL Server实例的CPU使用情况
    /// </summary>
    public void CheckCpuUsage()
    {
        string query = @"
            SELECT TOP(1)
                record_id,
                SQLProcessUtilization,
                SystemIdle,
                100 - SystemIdle - SQLProcessUtilization AS OtherProcessUtilization
            FROM (
                SELECT
                    record_id,
                    DatetimeRecord,
                    SQLProcessUtilization,
                    SystemIdle
                FROM (
                    SELECT
                        record.value('(./Record/@id)[1]', 'int') AS record_id,
                        record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle,
                        record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS SQLProcessUtilization,
                        DATEADD(ms, -1 * record.value('(./Record/@ms)[1]', 'int'), GETDATE()) AS DatetimeRecord
                    FROM (
                        SELECT TOP(30) CONVERT(xml, record) AS record
                        FROM sys.dm_os_ring_buffers
                        WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
                        AND record LIKE '%<SystemHealth>%'
                        ORDER BY timestamp DESC
                    ) AS RingBufferInfo
                ) AS RecordInfo
            ) AS CPU_Usage
            ORDER BY record_id DESC";

        using (SqlConnection connection = new SqlConnection(_connectionString))
        {
            try
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand(query, connection))
                {
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        if (reader.Read())
                        {
                            int sqlCpu = reader.GetInt32(reader.GetOrdinal("SQLProcessUtilization"));
                            int systemIdle = reader.GetInt32(reader.GetOrdinal("SystemIdle"));
                            int otherCpu = reader.GetInt32(reader.GetOrdinal("OtherProcessUtilization"));
                            
                            Console.WriteLine($"SQL Server CPU: {sqlCpu}%");
                            Console.WriteLine($"System Idle: {systemIdle}%");
                            Console.WriteLine($"Other Processes: {otherCpu}%");
                            
                            // 根据CPU使用率判断健康状态
                            if (sqlCpu > 85)
                            {
                                Console.WriteLine("警告: SQL Server CPU 使用率过高!");
                                // 这里可以添加告警逻辑
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"检查CPU使用率时出错: {ex.Message}");
            }
        }
    }
    
    /// <summary>
    /// 检查内存使用情况
    /// </summary>
    public void CheckMemoryUsage()
    {
        string query = @"
            SELECT
                physical_memory_in_use_kb/1024 AS physical_memory_in_use_mb,
                large_page_allocations_kb/1024 AS large_page_allocations_mb,
                locked_page_allocations_kb/1024 AS locked_page_allocations_mb,
                page_fault_count,
                memory_utilization_percentage,
                available_commit_limit_kb/1024 AS available_commit_limit_mb,
                process_physical_memory_low,
                process_virtual_memory_low
            FROM sys.dm_os_process_memory";

        using (SqlConnection connection = new SqlConnection(_connectionString))
        {
            try
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand(query, connection))
                {
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        if (reader.Read())
                        {
                            int memoryInUse = reader.GetInt32(reader.GetOrdinal("physical_memory_in_use_mb"));
                            int memoryUtilization = reader.GetInt32(reader.GetOrdinal("memory_utilization_percentage"));
                            bool physicalMemoryLow = reader.GetBoolean(reader.GetOrdinal("process_physical_memory_low"));
                            bool virtualMemoryLow = reader.GetBoolean(reader.GetOrdinal("process_virtual_memory_low"));
                            
                            Console.WriteLine($"SQL Server 内存使用: {memoryInUse} MB");
                            Console.WriteLine($"内存利用率: {memoryUtilization}%");
                            
                            if (physicalMemoryLow)
                            {
                                Console.WriteLine("警告: 物理内存不足!");
                                // 这里可以添加告警逻辑
                            }
                            
                            if (virtualMemoryLow)
                            {
                                Console.WriteLine("警告: 虚拟内存不足!");
                                // 这里可以添加告警逻辑
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"检查内存使用情况时出错: {ex.Message}");
            }
        }
    }
    
    /// <summary>
    /// 检查数据库文件空间使用情况
    /// </summary>
    public void CheckDatabaseSpace()
    {
        string query = @"
            SELECT
                DB_NAME(database_id) AS DatabaseName,
                Name AS LogicalName,
                Physical_Name AS PhysicalName,
                CAST(CAST(size AS BIGINT) * 8 / 1024.0 AS DECIMAL(18,2)) AS SizeMB,
                CAST(CAST(FILEPROPERTY(name, 'SpaceUsed') AS BIGINT) * 8 / 1024.0 AS DECIMAL(18,2)) AS UsedSpaceMB,
                CAST(CAST(size AS BIGINT) * 8 / 1024.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS BIGINT) * 8 / 1024.0 AS DECIMAL(18,2)) AS FreeSpaceMB,
                CAST((CAST(FILEPROPERTY(name, 'SpaceUsed') AS BIGINT) * 8 / 1024.0) / (CAST(size AS BIGINT) * 8 / 1024.0) * 100 AS DECIMAL(18,2)) AS PercentUsed
            FROM sys.database_files
            WHERE type_desc <> 'FULLTEXT'";

        using (SqlConnection connection = new SqlConnection(_connectionString))
        {
            try
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand(query, connection))
                {
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        Console.WriteLine("数据库文件空间使用情况:");
                        Console.WriteLine("-------------------------------------------");
                        
                        while (reader.Read())
                        {
                            string dbName = reader.GetString(reader.GetOrdinal("DatabaseName"));
                            string logicalName = reader.GetString(reader.GetOrdinal("LogicalName"));
                            decimal sizeMB = reader.GetDecimal(reader.GetOrdinal("SizeMB"));
                            decimal usedMB = reader.GetDecimal(reader.GetOrdinal("UsedSpaceMB"));
                            decimal freeMB = reader.GetDecimal(reader.GetOrdinal("FreeSpaceMB"));
                            decimal percentUsed = reader.GetDecimal(reader.GetOrdinal("PercentUsed"));
                            
                            Console.WriteLine($"数据库: {dbName}, 文件: {logicalName}");
                            Console.WriteLine($"  大小: {sizeMB} MB, 已用: {usedMB} MB, 空闲: {freeMB} MB, 使用率: {percentUsed}%");
                            
                            // 空间使用率警告
                            if (percentUsed > 90)
                            {
                                Console.WriteLine($"  警告: {dbName}.{logicalName} 空间使用率超过90%!");
                                // 这里可以添加告警逻辑
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"检查数据库空间使用情况时出错: {ex.Message}");
            }
        }
    }
    
    /// <summary>
    /// 检查数据库活跃连接数
    /// </summary>
    public void CheckActiveSessions()
    {
        string query = @"
            SELECT 
                DB_NAME(dbid) AS DatabaseName,
                COUNT(dbid) AS NumberOfConnections,
                loginame AS LoginName
            FROM sys.sysprocesses
            WHERE dbid > 0
            GROUP BY dbid, loginame
            ORDER BY COUNT(dbid) DESC";

        using (SqlConnection connection = new SqlConnection(_connectionString))
        {
            try
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand(query, connection))
                {
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        Console.WriteLine("数据库活跃连接:");
                        Console.WriteLine("----------------------------");
                        
                        while (reader.Read())
                        {
                            string dbName = reader.GetString(reader.GetOrdinal("DatabaseName"));
                            int connections = reader.GetInt32(reader.GetOrdinal("NumberOfConnections"));
                            string loginName = reader.GetString(reader.GetOrdinal("LoginName"));
                            
                            Console.WriteLine($"数据库: {dbName}, 登录: {loginName}, 连接数: {connections}");
                            
                            // 连接数警告
                            if (connections > 100) // 设置合适的阈值
                            {
                                Console.WriteLine($"  警告: {dbName} 有大量连接 ({connections})!");
                                // 这里可以添加告警逻辑
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"检查活跃会话时出错: {ex.Message}");
            }
        }
    }
}

预防措施与最佳实践

1. 数据库维护计划

  • 定期备份数据库和事务日志
  • 定期执行DBCC CHECKDB检查数据库一致性
  • 设置索引和统计信息的自动维护计划
-- 检查数据库一致性
USE [master]
GO
CREATE PROCEDURE [dbo].[usp_Check_Database_Consistency]
AS
BEGIN
    SET NOCOUNT ON;
    
    -- 声明变量
    DECLARE @DatabaseName NVARCHAR(255)
    DECLARE @SQL NVARCHAR(4000)
    DECLARE @ErrorLog NVARCHAR(255)
    
    -- 创建临时表保存结果
    IF OBJECT_ID('tempdb..#DBCCResults') IS NOT NULL
        DROP TABLE #DBCCResults
    
    CREATE TABLE #DBCCResults (
        DatabaseName NVARCHAR(255),
        CheckDate DATETIME,
        ErrorCount INT,
        ErrorMessage NVARCHAR(MAX)
    )
    
    -- 创建游标遍历所有用户数据库
    DECLARE db_cursor CURSOR FOR
    SELECT name FROM sys.databases
    WHERE database_id > 4 -- 排除系统数据库
    AND state_desc = 'ONLINE'
    
    OPEN db_cursor
    FETCH NEXT FROM db_cursor INTO @DatabaseName
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        SET @ErrorLog = 'DBCC_' + @DatabaseName + '_' + 
                       CONVERT(VARCHAR(10), GETDATE(), 112) + '.log'
        
        -- 执行DBCC CHECKDB
        SET @SQL = 'DBCC CHECKDB (''' + @DatabaseName + ''') WITH NO_INFOMSGS'
        
        BEGIN TRY
            INSERT INTO #DBCCResults (DatabaseName, CheckDate, ErrorCount, ErrorMessage)
            EXEC sp_executesql @SQL
        END TRY
        BEGIN CATCH
            INSERT INTO #DBCCResults (DatabaseName, CheckDate, ErrorCount, ErrorMessage)
            VALUES (@DatabaseName, GETDATE(), ERROR_NUMBER(), ERROR_MESSAGE())
        END CATCH
        
        FETCH NEXT FROM db_cursor INTO @DatabaseName
    END
    
    CLOSE db_cursor
    DEALLOCATE db_cursor
    
    -- 返回结果
    SELECT * FROM #DBCCResults
    DROP TABLE #DBCCResults
END
GO

2. 性能监控与告警

  • 设置SQL Server Agent作业监控关键性能指标
  • 使用数据收集集(Data Collector)定期收集性能数据
  • 配置告警阈值和通知机制

3. 高可用性与灾难恢复

  • 实现SQL Server高可用性方案
    • AlwaysOn可用性组
    • 数据库镜像
    • 日志传送
    • 故障转移群集
  • 制定并测试灾难恢复计划
  • 确保备份存储在异地位置

SQL Server故障排查流程图

性能问题
数据库错误
可用性问题
发现问题
识别故障类型
问题分类
性能排查
错误排查
可用性排查
收集性能指标
分析瓶颈
实施解决方案
分析错误日志
查看系统DMV
确定错误原因
实施解决方案
检查服务状态
检查网络连接
检查高可用性设置
实施解决方案
验证解决效果
问题是否解决
文档并总结
预防措施

常见故障的分类表

故障类型常见症状主要排查工具解决方案
CPU高响应缓慢,CPU使用率高DMV,性能监视器优化查询,添加索引
内存不足内存错误,性能下降DMV,性能监视器调整内存配置,释放缓存
I/O瓶颈磁盘队列长,I/O延迟高DMV,性能监视器优化I/O子系统,添加索引
阻塞/死锁查询等待,超时错误DMV,扩展事件优化事务,调整隔离级别
数据库损坏查询错误,一致性错误DBCC工具,错误日志恢复备份,修复数据库
TempDB争用TempDB PAGELATCH等待DMV,性能监视器增加TempDB文件数量
网络问题连接错误,超时SQL错误日志,网络工具检查网络配置,防火墙设置
日志增长磁盘空间不足DBCC SQLPERF备份日志,调整恢复模式

结论

SQL Server数据库故障排查是一个复杂而系统化的过程,需要数据库管理员具备深厚的技术背景和实践经验。本文介绍的故障分类、排查方法和解决方案可以作为处理SQL Server问题的基本框架。对于复杂问题,建议结合多种工具和方法,从不同角度分析问题,最终找到有效的解决方案。

与此同时,预防措施同样重要。通过实施合理的监控、维护计划和高可用性解决方案,可以大大减少数据库故障的发生率,确保业务系统的稳定运行。

参考资料

  1. Microsoft SQL Server 官方文档
  2. SQL Server 故障排查指南
  3. SQL Server 性能优化指南
  4. SQL Server 数据库恢复模式指南
  5. Brent Ozar - SQL Server 故障排查工具
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰茶_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值