第九期:高可用与灾难恢复 —— 日志传送、复制、Always On

### 第九期:高可用与灾难恢复 —— 日志传送、复制、Always On #### 1. 高可用 vs 灾难恢复:两个不同的目标 | 维度 | 高可用(HA) | 灾难恢复(DR) | |——|————-|—————| | **目标** | 应对局部故障(服务器、磁盘、网络) | 应对大规模灾难(机房、城市、自然灾害) | | **指标** | RTO:分钟级,RPO:秒级或零 | RTO:小时级,RPO:分钟到小时级 | | **距离** | 同机房或同城 | 异地或跨区域 | | **典型技术** | 故障转移集群,Always On | 日志传送,异地备份 | | **自动切换** | 通常自动 | 通常手动 | 关键术语: – **RTO(恢复时间目标)**:从故障到恢复服务的时间 – **RPO(恢复点目标)**:允许丢失的数据时间窗口 #### 2. SQL Server 高可用技术全景 | 技术 | 范围 | 数据丢失 | 自动切换 | 复杂度 | 适用场景 | |——|——|———-|———-|——–|———-| | 故障转移集群 | 实例级 | 可能(取决于磁盘) | 是 | 中 | 共享存储,传统HA | | 日志传送 | 数据库级 | 分钟级 | 否 | 低 | DR,报表服务器 | | 事务复制 | 表级 | 秒级 | 否 | 高 | 数据分发,多主 | | 合并复制 | 表级 | 分钟级 | 否 | 高 | 移动/离线场景 | | 镜像(已弃用) | 数据库级 | 秒~零 | 是 | 中 | 老环境 | | Always On AG | 数据库级 | 零(同步)| 是 | 中高 | 现代HA+DR首选 | | Always On FCI | 实例级 | 可能 | 是 | 中 | 共享存储或无存储AG | #### 3. 故障转移集群(FCI) **架构图**:   客户端   ↓   虚拟网络名   ↓   活动节点 ← 心跳 ← 备用节点   ↓ ↓   共享存储(SAN)────┘ **工作原理**: – 多台服务器共享同一存储(SAN、SMB文件共享) – SQL […]

第八期:存储引擎深度(四)—— 事务与并发控制的内部实现

### 第八期:存储引擎深度(四)—— 事务与并发控制的内部实现 #### 1. 事务的ACID与SQL Server的实现层次 | ACID属性 | SQL Server实现机制 | 关键组件 | |———-|——————-|———-| | **原子性(Atomicity)** | 事务日志 + 撤消(Undo) | 事务日志,LSN | | **一致性(Consistency)** | 约束、触发器、隔离级别 | 查询处理器,锁管理器 | | **隔离性(Isolation)** | 锁 + 行版本控制 | 锁管理器,tempdb | | **持久性(Durability)** | WAL + 检查点 + 恢复 | 事务日志,检查点 | **核心原则回顾**:WAL(Write-Ahead Logging)—— 日志写入必须在数据页写入之前完成。 #### 2. 深入事务日志:LSN与日志链 **LSN(Log Sequence Number)**:每个日志记录的唯一标识,由三部分构成: – **VLF序号**(2字节):虚拟日志文件编号 – **块偏移量**(4字节):日志块在VLF中的位置 – **日志槽号**(2字节):块内的日志记录序号 **日志记录的关键字段**: +--------------+----------+----------+-----------+------+ | 事务ID | LSN前向指针 | 操作类型 | 页ID(前/后镜像)| ... | +--------------+----------+----------+-----------+------+ – **前向指针**:形成日志链,支持正向扫描(恢复时重做)和反向扫描(回滚时撤消) – **操作类型**:LOP_BEGIN_XACT, LOP_COMMIT_XACT, LOP_INSERT_ROWS, LOP_MODIFY_ROW, LOP_DELETE_ROWS 等 **日志截断与日志备份的关系**: 简单恢复模式:检查点 → 截断非活动VLF 完整恢复模式:日志备份 → 标记VLF为可重用(但必须日志备份后) 大容量日志恢复模式:最小化日志操作(SELECT INTO, 索引重建)记录更少 **现象解释**: – 为什么完整恢复模式下日志一直增长?因为没有做日志备份(log_reuse_wait_desc = LOG_BACKUP) – 为什么大事务回滚非常慢?回滚需要重新扫描日志链,找到所有撤消记录并重放 #### 3. 行版本控制(Row Versioning)内部机制 **行版本的工作原理**: 1. 修改行时,不直接覆盖原行,而是在tempdb中存储行的先前版本 2. 每个行版本带有一个**事务序列号(XSN)** 3. 读操作根据隔离级别,选择合适版本的行 **两种行版本隔离级别**: | 隔离级别 | 读操作行为 | 写操作行为 | 配置 | |———-|———–|———–|——| | **READ COMMITTED SNAPSHOT(RCSI)** | 读取语句开始时已提交的最新行版本 | 仍然使用锁(X锁,U锁) | 数据库级设置 | | **SNAPSHOT ISOLATION** | 读取事务开始时已提交的行版本 | 检测写冲突(更新冲突会失败) | 数据库级+事务级设置 | **行版本的结构(存储在tempdb中)**: +---------------------------+----------+----------+------+ | 版本头(XSN、长度、指针) | 前镜像数据 | 后镜像数据 | 链指针 | +---------------------------+----------+----------+------+ – 版本链:通过指向tempdb中前一个版本的指针形成 – 版本清理:当没有任何活动事务需要该版本时,定期清理 **启用行版本控制**: -- 检查是否启用 SELECT name, is_read_committed_snapshot_on, snapshot_isolation_state FROM sys.databases WHERE name = 'YourDB'; -- 启用RCSI(需要数据库独占,重启现有连接) ALTER DATABASE YourDB SET READ_COMMITTED_SNAPSHOT ON; -- 启用快照隔离 ALTER DATABASE YourDB SET ALLOW_SNAPSHOT_ISOLATION ON; -- 事务级别使用快照隔离 SET TRANSACTION ISOLATION LEVEL […]

第七期:查询优化器与执行计划 —— 如何读懂与干预

### 第七期:查询优化器与执行计划 —— 如何读懂与干预 #### 1. 查询优化器的工作流程 SQL Server 执行一条查询的完整路径: T-SQL 语句   ↓ 【解析】语法检查 → 生成解析树   ↓ 【绑定】代数化(Algebrizer)→ 绑定到对象(表、视图、列),生成逻辑树(查询树)   ↓ 【优化】Query Optimizer → 基于代价生成多个候选计划 → 选择代价最低的   ↓ 【生成】执行计划 → 存入计划缓存   ↓ 【执行】Query Executor + Storage Engine → 返回结果 **优化器的核心任务**:在有限时间内找到“足够好”的执行计划(不是绝对最优,否则优化本身成本太高)。 **三个阶段**: | 阶段 | 策略 | 时间 | 搜索范围 | |——|——|——|———-| | 阶段0 | 事务处理(Trivial Plan) | 极快 | 只生成最简单的计划(如行数=1时直接扫描) | | 阶段1 | 探索阶段(Exploration) | 快 | 搜索部分有希望的变换规则 | | 阶段2 | 完全优化(Full Optimization) | 慢 | 搜索全部变换规则(并行计划也在此生成) | **现象解释**: – 为什么简单查询几乎瞬间返回执行计划?因为命中了阶段0,直接生成Trivial Plan。 – 为什么复杂查询第一次编译时间长?因为优化器需要搜索多个候选计划(阶段2)。 #### 2. 执行计划基础:读懂图形化计划 **三种获取执行计划的方式**: | 方式 | 命令 | 特点 | |——|——|——| | 估计执行计划 | SET SHOWPLAN_XML ON | 不执行查询,基于统计信息估算 | | 实际执行计划 | SET STATISTICS XML ON | 执行查询,包含实际行数、运行时信息 | | 实时执行计划 | sp_WhoIsActive 或 DMV | 查看正在执行的计划的实时进度(2016+) | **核心运算符(从数据流向角度理解)**: | 运算符 | 作用 | 图标特征 | |——–|——|———-| | **Clustered Index Seek** | 在聚集索引中精确查找 | 黄色箭头+放大镜 | | **Clustered Index Scan** | 扫描整个聚集索引(表扫描) | 黄色箭头+表格 | | **Index Seek** | 在非聚集索引中精确查找 | 绿色箭头+放大镜 | | **Index Scan** | 扫描整个非聚集索引 | 绿色箭头+表格 | | **Key Lookup(Bookmark Lookup)** | 从非聚集索引回到聚集索引/堆取行 | 小书签图标 | | **Nested Loops** | 循环嵌套连接(适用于小表驱动大表) | 两个圆套在一起 | | **Hash Match** | 哈希连接(适用于大表无索引) | 两个圆相交 | | **Merge […]

第六期:并发控制(下)—— 死锁检测、分析与消除

### 第六期:并发控制(下)—— 死锁检测、分析与消除 #### 1. 死锁的定义与必要条件 **死锁**:两个或多个事务各自持有对方需要的资源,且都不释放,导致永久阻塞。 **四个必要条件**(全部满足才会死锁): | 条件 | 说明 | 示例 | |——|——|——| | **互斥** | 资源一次只能被一个事务持有 | 排他锁(X)同时只能一个事务持有 | | **持有并等待** | 持有资源的同时请求其他资源 | 事务A持有表T1锁,请求表T2锁 | | **不可抢占** | 已持有的锁不能被强制释放 | SQL Server不会主动剥夺锁 | | **循环等待** | 事务间形成等待环 | A等B → B等C → C等A | **现象解释**: – 为什么高并发系统更容易死锁?因为事务间交错执行的概率增加,更容易形成循环等待。 – 为什么死锁通常发生在多个表之间或同一表的不同行?因为需要形成”互相等待”的环。 #### 2. SQL Server的死锁检测与处理机制 **死锁检测**: – **锁监视器线程(Lock Monitor)**:每5秒启动一次,检测系统内是否有死锁。 – **等待图(Wait Graph)**:维护资源与等待关系的有向图,检测到循环即判定死锁。 – **高频检测**:当死锁发生频率较高时,检测频率自动提升(可低至100ms)。 **死锁处理**: 1. 检测到死锁循环 2. 选择"牺牲品"(Victim) 3. 终止牺牲品事务,回滚其所有操作 4. 释放该事务持有的所有锁 5. 让其他事务继续运行 6. 向牺牲品客户端返回错误号 1205 **牺牲品选择依据**: – **死锁优先级**:SET DEADLOCK_PRIORITY(LOW / NORMAL / HIGH / -10~10数值) – 优先级最低的会话被选中 – 相同优先级时,选择回滚代价最小的事务 – 默认所有会话优先级为 NORMAL(0) -- 设置当前会话为低优先级(更容易被杀) SET DEADLOCK_PRIORITY LOW; -- 设置关键事务为高优先级(不容易被杀) SET DEADLOCK_PRIORITY HIGH; **现象解释**: – 为什么重试能解决死锁错误?因为死锁是偶发的,重新执行通常能成功。 – 为什么长事务更容易成为牺牲品?回滚代价大,但在相同优先级时,SQL Server会选择回滚代价小的(不是长的,而是修改少的)。 #### 3. 典型死锁场景与案例 **场景一:两个事务交叉更新两张表** 事务A: 事务B: UPDATE T1 SET ... UPDATE T2 SET ... WAITFOR '00:00:01' WAITFOR '00:00:01' UPDATE T2 SET ... UPDATE T1 SET ... 锁过程: 1. A持有T1的X锁,请求T2的X锁 → 等待B释放T2 2. B持有T2的X锁,请求T1的X锁 → 等待A释放T1 3. 死锁形成 → SQL Server杀死其中一个 **场景二:同一表上的行级锁死锁** 事务A: 事务B: BEGIN TRAN BEGIN TRAN UPDATE Orders SET Status=1 UPDATE Orders SET Status=1 WHERE OrderID=101 WHERE OrderID=102 -- 此时A持有101的行X锁,B持有102的行X锁 SELECT * FROM Orders SELECT * FROM Orders WHERE OrderID=102 WHERE OrderID=101 -- 需要共享锁,被B的X锁阻塞 -- 需要共享锁,被A的X锁阻塞 死锁形成! **场景三:范围锁(SERIALIZABLE级别)导致的死锁** -- 事务A:查询并插入缺失行 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; […]

第五期:并发控制(上)—— 隔离级别、锁与锁升级

### 第五期:并发控制(上)—— 隔离级别、锁与锁升级 #### 1. 并发问题与隔离级别 **三种常见并发问题**: | 问题 | 定义 | 示例 | |——|——|——| | **脏读** | 读到未提交事务的修改 | 事务A修改了行R未提交,事务B读到修改后的值,A回滚 | | **不可重复读** | 同一事务内两次读取同一条记录,值不同 | 事务B第一次读行R,事务A修改并提交,事务B第二次读到新值 | | **幻读** | 同一事务内两次查询,结果集行数不同 | 事务B第一次查满足条件的行集,事务A插入新行,事务B第二次查到多一行 | **SQL Server 支持的隔离级别**(从弱到强): | 隔离级别 | 脏读 | 不可重复读 | 幻读 | 实现机制 | |———-|——|————|——|———–| | READ UNCOMMITTED | ✅可能 | ✅可能 | ✅可能 | 不加共享锁,不遵守锁协议 | | READ COMMITTED(默认)| ❌ | ✅可能 | ✅可能 | 读时加共享锁,读完立即释放 | | REPEATABLE READ | ❌ | ❌ | ✅可能 | 读时加共享锁,保持到事务结束 | | SERIALIZABLE | ❌ | ❌ | ❌ | 加范围锁(Range Lock),防止插入 | | SNAPSHOT | ❌ | ❌ | ❌ | 使用行版本,不阻塞写 | | READ COMMITTED SNAPSHOT(RCSI)| ❌ | ❌ | ✅可能 | 读行版本,写仍用锁 | **SQL Server 默认隔离级别**:READ COMMITTED(使用共享锁防止脏读) **现象解释**: – 为什么同一个查询在会话A中连续两次,结果可能不同?因为默认 READ COMMITTED 允许不可重复读。 – 为什么 ALTER DATABASE 开启 READ_COMMITTED_SNAPSHOT 后,读操作不再被写阻塞?因为读改用 tempdb 的行版本,不再需要共享锁。 #### 2. 锁的类型与粒度 **锁粒度**(从细到粗): | 粒度 | 资源 | 场景 | |——|——|——| | RID(行ID)| 堆中的单行 | 精确点查 | | KEY | 索引行 | 通过聚集索引定位 | | PAGE | 8KB 页 | 页内有多行被访问 | | EXTENT | 64KB 区 | 分配/释放空间时 | | TABLE | 整张表 | 全表扫描或锁升级 | | DATABASE | 整个数据库 | DDL 操作或备份 | **主要锁模式**: | 锁模式 | […]

第四期:堆、聚集索引与非聚集索引 —— 物理存储与访问路径

第四期:堆、聚集索引与非聚集索引 —— 物理存储与访问路径 1. 基础回顾:页(Page)与区(Extent) 页(8KB):SQL Server 中最小的 I/O 单元。每页包含 96 字节的页头(对象ID、分区ID、下一页指针等)+ 实际数据行。 区(64KB):8 个物理连续页的组。混合区(存放多个对象的页)和统一区(只属于一个对象)。 IAM(Index Allocation Map)页:记录表或索引使用了哪些区,用于扫描整个对象。 2. 堆(Heap)—— 没有聚集索引的表 存储结构:数据页无序排列,新行通常放在最后一个页(如果有空间),或者从可用空间中分配。 行的物理位置:由 RID(8 字节:文件号:页号:槽号)唯一标识。 查找方式:必须 全表扫描(按 IAM 逐页读取)或通过非聚集索引的 RID 定位。 修改特性:插入时可随意找空位;更新行若长度增长且本页放不下会移动到新页(原位置留下转发指针)。 现象解释: 为什么堆上查询任意条件都很慢?因为没有有序结构,只能全表扫描。 为什么堆会产生碎片?因为行移动产生转发指针,增加额外 I/O。 3. 聚集索引(Clustered Index)—— 在树上组织数据 存储结构:B+ 树(非叶子节点存放索引键和子页指针;叶子节点就是 数据页本身)。 唯一特性:一个表只能有一个聚集索引。数据行按聚集键顺序物理存储(但实际不严格连续,通过双向链表连接叶子页)。 查找路径(等于或范围查询): 根页 → 中间页 → 叶子页(数据页)→ 解压行数据 深度一般为 2~4 层(IO 次数极少)。 插入/更新:维护键顺序可能引发 页分裂(开销大)。 现象解释: 为什么主键默认是聚集索引(但并非必然)?因为主键常作为查询入口,聚集索引能最快定位。 为什么插入 GUID 主键会产生大量碎片?因为 GUID 随机,页分裂频繁。 4. 非聚集索引(Nonclustered Index)—— 目录索引 存储结构:也是 B+ 树,但叶子节点 不包含完整数据行,而是存放一个指向数据行的“指针”: 表有聚集索引 → 指向 聚集索引键(不是 RID)。 表是堆 → 指向 RID。 查找方式: 索引查找(Index Seek):从根页下探,定位到少量索引行。 索引扫描(Index Scan):整体遍历索引的叶子页(比表扫描小,但范围大时仍昂贵)。 关键概念:书签查找(Bookmark Lookup) 当查询需要返回非索引列(不在索引键或包含列中)时: 非聚集索引找到聚集键/RID → 再到聚集索引/堆中取完整行 → 产生额外 IO。 代价:如果查找的行数很少,书签查找没问题;如果行数太多(例如超过总行数的 1%~5%),优化器可能选择全表扫描。 5. 覆盖索引(Covering Index)—— 避免书签查找 定义:一个非聚集索引包含了查询所需的 所有列(通过 INCLUDE 或索引键本身)。 语法示例: CREATE INDEX idx_orders_date ON Orders(OrderDate) INCLUDE (CustomerID, ShipAmount); 访问路径:只扫描非聚集索引的叶子页,获取全部数据,无需访问数据页。 性能提升:减少一倍以上 I/O(数据页可能更宽且分散)。 现象解释:为什么同样的查询,加一个 INCLUDE 后速度暴增?因为消除了书签查找中昂贵的随机 I/O。 6. 索引交叉与索引联合(Index Intersection / Union) 索引交叉:多个非聚集索引分别查找,然后交集结果(较少用)。 索引联合:先用一个索引找到行,再用另一个索引做书签查找(不常见)。 7. 常见“索引误用”现象与根因 现象 根本原因 架构解释 明明有索引,还是全表扫描 查询条件不符合索引查找要求(如函数包裹列、OR 条件、非前导列范围查询) B+ 树只能从最左前缀开始下探 使用非聚集索引反而比全表扫描慢 书签查找行数占比过大(阈值约 1%~5%) 每条记录需要随机 I/O 访问数据页,而全表扫描是顺序 I/O 插入性能急剧下降 表上有多个非聚集索引,且聚集索引键宽或频繁分裂 每插入一行,所有非聚集索引也要维护(页分裂、重平衡) 查询统计数据不准导致选错索引 统计信息过时或采样不足 优化器基于代价估算,依赖统计信息 索引查找深度大(5+ 次逻辑读) 索引树层数多(通常因为索引键非常宽且表巨大) B 树高度 ≈ log( 行数 / 每页索引行数 ) 8. 监控与评估索引使用情况 DMV 作用 sys.dm_db_index_physical_stats 索引碎片(avg_fragmentation_in_percent)、深度、页数 sys.dm_db_index_usage_stats 查找次数(user_seeks)、扫描次数(user_scans)、更新次数(user_updates) sys.dm_db_missing_index_details 建议创建哪些索引(但需验证) 无用索引判断:user_updates 远大于 user_seeks/scans → 维护成本高,可考虑删除。 9. 设计指导原则 聚集索引的选择: 窄、唯一、递增(如 IDENTITY,或 OrderDate 倒序)。 避免 GUID、随机宽字符串。 最常做范围查询或排序的列。 非聚集索引策略: 高选择性(唯一值接近行数)的列做索引键。 在 =, > 等常用筛选中使用前导列。 覆盖频繁查询(用 INCLUDE 包含所有回表列)。 维护与重建: 碎片 > 30% […]

第三期:事务日志、WAL 与崩溃恢复 —— 如何保证断电也不丢数据?

第三期:事务日志、WAL 与崩溃恢复 —— 如何保证断电也不丢数据? 1. 事务日志的物理结构:不只是“一个文件” 每个数据库至少有一个日志文件(.ldf),内部被划分为 VLF(Virtual Log File) 逻辑单元: VLF 数量由日志文件大小和自动增长参数决定(过多 VLF 会影响性能)。 LSN(Log Sequence Number):每个日志记录的单调递增编号,用于定位和恢复。 日志记录内容:事务开始/结束、每次数据修改(Before/After 或操作描述)、页分配、DDL 操作等。 现象解释:为什么 ALTER DATABASE … MODIFY FILE 后日志仍很大?因为 VLF 已被分配但未截断或收缩。收缩日志会释放尾部未使用的 VLF。 2. 最关键原则:WAL(Write-Ahead Logging) 在将数据页的修改写入磁盘(数据文件 .mdf/.ndf)之前,必须先将对应的日志记录写入磁盘(.ldf)。 为什么? 持久性:事务提交时,即使数据页还在内存中(脏页),日志已经硬化,重启后可通过 Redo 恢复已提交事务。 原子性:未提交事务的修改,可通过 Undo 回滚(基于日志中的“撤消信息”)。 具体流程(UPDATE 示例): 修改缓冲池中的数据页 → 页变脏。 同时将修改操作写入日志缓存。 事务提交时:强制执行 日志刷盘(log flush),把日志缓存中的记录写入 .ldf。 只有日志刷盘成功,客户端才收到“提交成功”。 脏页可以稍后由 Checkpoint/LazyWriter 异步写回。 现象解释:为什么高并发插入/更新时,磁盘 I/O 压力常在日志盘?因为每次提交都要刷日志。使用“延迟提交”或批量提交可缓解。 3. 日志缓存与刷盘机制 日志缓存:每个事务先写到内存中的日志缓存(不可分页),大小固定(通常几十到几百 KB)。 刷盘触发条件: 事务提交(最常见) 日志缓存满(约 60KB 或 1/3 缓存) 显式 CHECKPOINT 其他需要强一致性的操作(如创建数据库快照) 刷盘方式:调用 WriteFile + FlushFileBuffers 确保持久化(成本高)。 优化选择: DELAYED_DURABILITY(延迟持久性):允许事务提交时不立即刷日志,提升性能但可能丢失最近的数据(适合非关键日志)。 4. 检查点(Checkpoint)与崩溃恢复流程 检查点:将当前所有脏页从缓冲池写回数据文件,并在日志中标记一个 LSN(最老脏页对应的 LSN)。 作用: 缩短崩溃恢复时 需要重做的日志量。 避免日志无限增长(但日志截断依赖备份或简单恢复模式中的检查点)。 崩溃恢复三步走(SQL Server 重启或 RESTORE WITH RECOVERY): 1. 分析阶段:扫描日志,找出检查点后所有活跃事务(已提交但未 Redo?实际顺序略有调整)。 2. 重做(Redo):从检查点的最小 LSN 开始,重复所有已提交事务的修改(确保数据页对应日志)。 3. 撤消(Undo):回滚所有未提交事务的修改(基于日志中的撤消信息)。 最终数据库一致。 现象解释: 为什么 SQL Server 重启后长时间“正在恢复”?因为需要重做/撤消大量日志(日志文件很大或检查点不频繁)。 为什么大事务回滚也很慢?回滚本质也是重放日志中的“撤消操作”(需要从头到尾扫描日志)。 5. 日志截断、收缩与增长 日志截断:逻辑上标记日志中不活动的 VLF 为可重用(并不会释放磁盘空间)。 触发条件: 简单恢复模式:每次检查点后截断。 完整恢复模式:只有日志备份后才会截断。 日志收缩:DBCC SHRINKFILE 可将未使用的尾部 VLF 释放给操作系统,但会引入大量 I/O 和索引碎片,不建议常规使用。 日志暴涨的常见原因: 长时间未做日志备份(完整恢复模式)。 大事务(如索引重建、批量更新)一次性生成海量日志。 复制/AlwaysOn 同步等待(未确认)。 延迟提交或未提交的打开事务。 6. 监控与诊断关键 DMV 常用视图/函数 信息 sys.dm_tran_database_transactions 当前事务的日志空间使用量(database_transaction_log_bytes_used) sys.dm_tran_log_stats(2016+) 日志统计:每秒写入量、等待 WRITELOG 等 DBCC LOGINFO 查看 VLF 状态(活动/可重用) sys.dm_os_performance_counters Log Bytes Flushed/sec, Log Flushes/sec sys.dm_db_log_space_usage 总日志大小与已用百分比 关键指标: Log Flush Waits/sec 过高 → 日志磁盘瓶颈。 Log Growths/sec 持续 >0 → 自动增长频繁(性能杀手),需预分配大小。 VLF 数量(DBCC LOGINFO 输出行数)>200 说明日志文件被过度自动增长过。 7. 最佳实践与调优 分离日志文件到高 IOPS 磁盘(不要放系统盘或数据盘)。 预估日志大小,手动设置初始大小和增长增量(如 1~4 GB),避免频繁自动增长。 完整恢复模式下定时做日志备份(如每 15~30 分钟),防止日志无限膨胀。 避免不必要的长事务(例如事务中等待用户输入或跨网络调用)。 使用间接检查点(TARGET_RECOVERY_TIME)控制脏页刷新频率,平衡恢复时间与性能。 监控 log_reuse_wait_desc 了解日志截断被什么阻止: SELECT name, log_reuse_wait_desc FROM sys.databases; 常见等待原因:LOG_BACKUP(未备份)、ACTIVE_TRANSACTION(未提交事务)、REPLICATION 等。 第三期小结 事务日志与 […]

第二期:缓冲池与内存架构 —— 为什么缓存命中率决定性能?

第二期:缓冲池与内存架构 —— 为什么缓存命中率决定性能? 1. 内存整体视图:SQL Server 如何使用操作系统内存? SQL Server 实例启动后,会向操作系统申请一块内存(Min Server Memory ~ Max Server Memory),主要由以下部分组成: 内存区域 作用 是否可释放 缓冲池(Buffer Pool) 缓存数据页(.mdf/.ndf) ✅ 可释放给其他缓存或 OS 计划缓存 存储执行计划、查询编译结果 ✅ 可释放(受内存压力时) 日志缓存 暂存待写入 .ldf 的日志记录 很小的固定区域 连接/线程/锁等结构 管理并发、会话、事务 相对固定 其他组件 CLR、链接服务器、扩展事件等 缓冲池通常占整个 SQL Server 内存的 70%~80%,是绝对主力。 现象解释:为什么任务管理器显示 SQL Server 占用内存很高且不释放?因为缓冲池是主动缓存,它会尽量多占内存以减少磁盘 I/O。一旦被 OS 或其他进程挤压,SQL Server 会适当释放(但默认“不主动让出”)。 2. 缓冲池内部结构 —— 不只是“一堆内存页” 缓冲池按 8KB 数据页 为单位管理,每个页代表磁盘上的一个数据页或索引页。关键组件: 页头:记录页的元信息(对象ID、分区ID、LSN、页类型等)。 实际数据:行、索引键、或大对象指针。 页的三种状态: Clean(干净页):内存中的内容与磁盘完全一致,可直接丢弃。 Dirty(脏页):已被修改但未写回磁盘,不能随意丢弃。 Free(空闲页):还未被使用。 可用页列表(Free List):空页的链表,用于快速分配新页。 懒写入器(LazyWriter):当内存压力出现时,扫描缓冲池,将干净页直接丢弃,将脏页触发写入磁盘后再丢弃,腾出空间。 检查点(Checkpoint):定期或主动触发,将所有脏页写回磁盘,为恢复缩短重做时间。 3. 页面读取流程(命中 vs 未命中) 查询请求一个数据页(PageID) 1. 缓冲池查找 -> 命中 -> 直接使用(逻辑读) | 未命中 ↓ 2. 从磁盘异步读取(物理读)到缓冲池,可能触发淘汰: - 优先从 Free List 取空页 - 如果 Free List 太少,LazyWriter 清理干净页/脏页 - 若 LazyWriter 也赶不上,查询等待(ASYNC_IO_COMPLETION) 现象解释:为什么第一次查询慢,后来快?因为第一次是物理读(从 .mdf 读到内存),后续是逻辑读(直接从缓冲池命中)。 4. 页面写入 —— 脏页如何落盘? Checkpoint(自动每 1 分钟或手动): 扫描每个数据库的脏页,批量写出到数据文件。 写入完成后在数据库的 启动页 记录最后一个 LSN(日志序列号),供恢复使用。 LazyWriter(内存压力时): 行为类似 Checkpoint,但目标更激进(腾出空间而非仅做恢复点)。 EagerWrite(大操作如创建索引):主动提前写出,避免内存爆炸。 间接检查点(Indirect Checkpoint)(SQL Server 2012+):允许按数据库设置目标恢复时间,写得更平滑。 关键保证:WAL 原则 —— 日志总是先于数据页写入磁盘。脏页可以延迟写回,但对应的日志必须已经硬化。 5. 监控内存与缓冲池的关键指标 指标 意义 健康阈值 Page Life Expectancy (PLE) 一个页在缓冲池中平均停留的秒数。过低表示频繁被挤出。 一般 >300 秒,OLTP 可更高 Buffer Cache Hit Ratio (逻辑读 – 物理读)/逻辑读,反映命中率。 >95% 较好,<90% 需关注 Free List Stalls/sec 查询等待空页的次数。持续大于0 → 严重内存压力 接近 0 Lazy Writes/sec LazyWriter 每秒刷出的页数。过高说明内存不足 持续高需增加内存或减少工作集 Checkpoint Pages/sec 检查点刷脏页的速率 正常波动大,突然飙升无大碍 如何查看: SELECT counter_name, cntr_value FROM sys.dm_os_performance_counters WHERE object_name LIKE '%Buffer Manager%'; SELECT [name], [target_time], [actual_time] FROM sys.dm_os_ring_buffers; — 间接检查点记录 #### 6. 常见内存压力现象与排查 | 现象 | 典型原因 | 架构层面解释 | |------|----------|----------------| […]

第一期:SQL Server 基础架构与核心组件

第一期:SQL Server 基础架构与核心组件 1. 实例(Instance)—— SQL Server 的“进程容器” 定义:一个独立的 SQL Server 服务进程(sqlservr.exe),包含自己的一套系统数据库、用户数据库、配置和端口。 关键点: 一台机器可以装多个实例(默认实例 + 命名实例),彼此隔离,通过不同端口(默认 1433)区分。 每个实例有自己的内存、线程、调度器,但共享操作系统资源。 现象解释:为什么重启实例能清空某些缓存?因为实例重启会释放所有内存区域(如缓冲池、计划缓存)。 2. 系统数据库 —— SQL Server 自己的“控制面板” 数据库 作用 master 记录实例级别信息(登录账号、链接服务器、配置、其他数据库位置) msdb 存储作业、备份历史、维护计划、SQL Agent 相关信息 tempdb 全局工作空间:存放临时表、排序、哈希连接、行版本、重建索引时的中间数据。每次实例重启会重新创建。 model 任何新数据库的“模板” resource(只读隐藏) 存储系统存储过程、视图等定义,升级时覆盖 现象解释:tempdb 负载过高导致全实例变慢?因为所有用户的排序、哈希、临时表都在抢同一个数据库。 3. 数据库(Database)与文件组(Filegroup) 数据库物理组成: .mdf(主数据文件,1个) .ndf(次数据文件,0~N个) .ldf(事务日志文件,至少1个) 文件组:一组数据文件的逻辑容器。表和索引可以指定放在哪个文件组。 关键规则: 一个数据文件只属于一个文件组。 主文件组(PRIMARY)永远包含主数据文件。 文件组内部采用比例填满策略(按空闲空间比例循环分配)。 现象解释:为什么把大表放到另一个文件组能改善 I/O?因为可以让多个物理硬盘并发读写,绕开主文件组的 I/O 瓶颈。 4. 执行引擎(Relational Engine)—— 负责“编译与执行” 主要处理 T-SQL 的整个生命周期: Parser:语法检查,生成查询树。 Algebrizer:绑定到具体对象(视图、列、数据类型),生成逻辑树。 Optimizer:基于代价生成最优执行计划(关键:统计信息)。 Query Executor:调用存储引擎提供的数据访问方法,执行计划。 现象解释: 第一次执行慢,第二次快?——计划缓存在起作用(属于执行引擎的组件)。 参数嗅探问题?——优化器基于第一次传入的参数值生成计划,复用时可能不是最优。 5. 存储引擎(Storage Engine)—— 负责“数据怎么放、怎么取” 缓冲池(Buffer Pool):内存中最大区域,缓存数据页(8KB/页)。所有读/写最终都经过这里。 读:先查缓冲池 → 命中则直接返回;未命中则从磁盘读入缓冲池。 写:修改内存中的页(脏页),由 Checkpoint 或 LazyWriter 异步写回磁盘。 事务日志与 WAL(Write-Ahead Logging): 修改数据前,必须先写日志(日志刷盘后才允许修改内存页)。 保证 Crash Recovery 能回滚或重做。 数据访问方法:堆、B-Tree、聚集索引扫描、查找等。 锁与闩锁: 锁:事务隔离,协调用户并发(行、页、表锁)。 闩锁:保护内存结构(如缓冲池里的页),比锁更轻量、时间短。 现象解释: 为什么内存占满(不释放给 OS)?因为 SQL Server 把缓冲池当“缓存”,主动占用内存以减少磁盘 I/O。 为什么突然大量写磁盘?可能是 Checkpoint 或内存压力导致 LazyWriter 批量刷脏页。 为什么被阻塞?事务获得了锁不释放(例如更新未提交),另一个查询等待同样资源。 6. 完整工作流(一个 SELECT 语句从下发到返回) 客户端 → TDS 协议 → 实例端口(1433) 1. 协议层(SNI/Tabular Data Stream):解析请求,分发给执行引擎。 2. 执行引擎:解析→绑定→优化→生成执行计划(缓存)。 3. 存储引擎: - 访问方法:根据计划调用“查找/扫描”接口。 - 缓冲池管理器:检查所需数据页是否在内存。 - 若不在 → 发起异步 I/O 从磁盘(.mdf/.ndf)读入缓冲池。 4. 存储引擎返回数据行给执行引擎。 5. 执行引擎处理聚合、排序等,最后通过协议层返回客户端。 更新语句(UPDATE) 则多走一步: 1. 在缓冲池修改数据页 → 标记为脏页。 2. 日志记录写入 `.ldf`(强刷盘)。 3. 事务提交时:日志被硬化,但脏页可能仍在内存(后续由 Checkpoint 写回)。 7. 常见“现象”与架构关联对照表 现象 直接原因 涉及架构组件 重启实例后第一次查询很慢 缓冲池为空,大量物理读 缓冲池、存储引擎 相同查询有时快有时慢 参数嗅探导致不同执行计划 优化器、计划缓存 大量临时表操作造成磁盘 I/O 瓶颈 tempdb 争用或空间不足 tempdb、存储引擎 查询被长时间阻塞 某事务持有行/表锁未释放 锁管理器 + 隔离级别 内存占用过高且不释放 SQL Server 主动缓存数据页 缓冲池 突然长时间写盘任务 Checkpoint / LazyWriter 刷脏页 检查点、缓冲池 第一期小结 SQL Server 的基础架构可以概括为:实例内有多个数据库(含系统库),每个数据库由文件组划分物理文件。执行引擎负责 T-SQL 的编译与优化,存储引擎管理内存(缓冲池)、磁盘 I/O、事务日志和并发控制。理解这些组件间的协作,就能解释或预判大多数性能问题。 下一期预告:深入 缓冲池与内存架构 —— 为什么缓存命中率决定性能?如何监控和调优内存压力?

DBA晨报·第30期|Google Cloud数据库Agent化、Oracle CPU修复481漏洞、PG 19异步I/O增强+国产淘汰赛开启

DBA晨报·第30期|Google Cloud数据库Agent化、Oracle CPU修复481漏洞、PG 19异步I/O增强+国产淘汰赛开启 为你摘取技术圈值得关注的3件事。今天是2026年4月25日,星期六。 01 云数据库|Google Cloud数据库全面Agent化:Spanner Omni发布,AI原生架构重塑数据库角色 在近日举行的Google Cloud Next 2026大会上,Google Cloud正式发布了Agentic Data Cloud战略,标志着其数据库产品线全面向AI Agent时代转型。 核心战略:从“数据存储”到“智能上下文引擎” Google Cloud工程副总裁Sailesh Krishnamurthy在大会演讲中指出:数据库50年来只有一个任务——存储数据并按需返回精确结果。但AI彻底打破了这一契约。 传统范式 AI Agent时代新范式 返回精确匹配结果 返回最佳相关结果 单一数据模型 图遍历+向量嵌入+全文搜索+关系操作融合 被动存储系统 主动智能上下文引擎 “模型令人惊叹,但它们没有所有上下文,”Krishnamurthy表示,“上下文就在数据中,而数据的核心存储在这些系统里。你需要提供上下文才能回答问题。” Spanner Omni:可下载的全球分布式数据库 大会重磅发布Spanner Omni——可下载版本的Spanner,支持: 本地部署:在企业自有数据中心运行 跨云部署:可在竞争对手的云平台上运行 统一管理:与Google Cloud管理平面集成,享受持续更新和AI能力注入 Gemini驱动的Agentic迁移工具 Google同步推出由Gemini大模型驱动的自动化迁移工具: 传统方案痛点:数据库迁移不止涉及Schema和数据,还包含应用中嵌入的SQL查询,过去需数月人工努力 新方案能力:Agent可处理应用层,包括嵌入式SQL查询的自动转换,大幅压缩迁移时间 核心价值:企业可将精力从“迁移工程”转向“业务创新” 战略意义:数据库进入“三位一体”竞争时代 Krishnamurthy进一步阐释:现代数据库需要实现图遍历、向量嵌入、语义搜索和全文搜索的融合,而不是为了不同查询类型而“不必要地移动数据”。这意味着数据库厂商的竞争焦点已从“单一性能指标”转向: 多模态融合能力 AI驱动的自动化 跨云部署灵活性 DBA视角:Google Cloud的Agentic战略释放了一个明确信号:数据库正在从“被动存储”进化为“主动智能引擎”。对于DBA而言,建议关注: 技能升级:向量检索、图查询等多模态数据处理能力成为必备技能 工具链变革:AI驱动的迁移和运维工具将改变DBA日常工作模式 架构思维:在多云时代,数据库的跨云部署能力可能成为架构选型的关键变量 02 安全预警|Oracle发布2026年4月CPU:481个补丁修复450个CVE,请尽快升级 Oracle于4月21日发布2026年4月Critical Patch Update(CPU),共计481个安全补丁,修复约450个CVE,涵盖28个产品家族。 补丁规模概览 指标 数据 总安全补丁数 481个 修复CVE数 约450个 远程未授权利用漏洞 300+个(约占62%) 第三方组件相关补丁 376个(约占78%) 核心产品受影响情况 产品家族 补丁数 远程未授权利用漏洞 CVSS最高分 Oracle Communications 139 93 9.8(严重) Financial Services Apps 75 59 9.8 Fusion Middleware 59 46 9.8 MySQL 34 3 9.8 E-Business Suite 18 8 9.8 GoldenGate 10 7 7.5 Database Server 8 4 7.5 Java SE 11 7 — Virtualization 9 1 — 严重漏洞情况 多个产品存在CVSS 9.8的严重漏洞,可被远程、无需认证的攻击者利用,可能导致: 远程代码执行(RCE) 权限提升 数据泄露 其中部分漏洞影响第三方组件(如Spring、Log4j等),已在Oracle产品分发中被发现可利用。 DBA视角 本次CPU补丁规模创近期新高。对于Oracle DBA: 升级紧迫性:存在多个CVSS 9.8的严重漏洞,可远程利用,建议尽快规划升级窗口 关注重点:MySQL收到34个补丁,其中1个严重(CVSS 9.8),波及Enterprise Backup组件 第三方风险:约78%补丁来自第三方组件漏洞,提醒DBA关注开源组件供应链安全 建议动作: 检查当前Oracle产品版本是否在受影响范围内 评估补丁对业务的影响,在测试环境验证 按资产重要性优先级,制定分批升级计划 关注Oracle后续发布的补丁说明和已知问题 03 版本回顾|PG 19异步I/O能力增强:自调节工作线程池 + 可观测性大幅提升 PostgreSQL 19在PG 18引入的异步I/O基础上,进行了重要增强。博主David Christensen在最新文章中详细分析了这些改进。 PG 18回顾:异步I/O首次亮相 PG 18引入了异步I/O(AIO)能力: Linux平台:使用io_uring实现,性能显著提升 其他平台:通过worker pool回退实现,但需手动调优 早期基准测试(pganalyze、Aiven等):读密集型负载、大范围顺序扫描场景真实性能提升 PG 19核心增强:自调节工作线程池 PG 19为io_method=worker模式新增4个自调节GUC参数: 参数 功能 io_min_workers 工作线程池下限 io_max_workers 工作线程池上限 io_worker_idle_timeout 空闲线程存活时间 io_worker_launch_interval 负载下新增线程的间隔 自调节逻辑: 当查询实际等待I/O时,工作线程池自动扩容 空闲时自动收缩 PG 18中手动调优的静态配置不再是最佳选择 升级建议:如果曾在PG 18中手动设置过worker数量,PG 19中应移除手配参数,用默认值进行基准测试。上限仍然重要——io_max_workers是硬上限,对于8核机器,设置32可能导致性能问题。 可观测性增强 PG 19显著提升了I/O层的可观测性: 不再是“盲猜”I/O瓶颈 可直接观测I/O层行为,定位性能问题 为DBA提供了更精确的调优依据 总结:PG 18的目标是让“读取不再阻塞执行器”,而PG 19的目标是让系统可以自调节并告诉你在做什么。 DBA视角 对于使用PostgreSQL的DBA: 升级价值:PG 19的自调节AIO大幅降低手动调优负担 基准测试:建议在测试环境验证自调节能力效果 可观测性:I/O层可观测性增强是DBA调优的重要工具 04 市场洞察|国产数据库淘汰赛开启:AI重写规则,生态决定生死 4月22日,2026中国数据库技术与产业大会上,行业形成高度共识:中国数据库产业正从规模化替代阶段,迈入高质量发展与市场化洗牌的全新阶段。 核心观点 行业集中度快速提升: 头部国产品牌销售额保持稳定增长 新增企业数量持续减少 […]
Recent Posts

HiddenMerit Daily · Issue 60

# 📊 HiddenMerit Daily · Issue 60 > **Focus on Database Frontiers, Practical Insights for DBAs** > July 22, 2026 | 5 Selected Global Breaking News ## 01|Oracle Releases Largest Quarterly Patch in History: 1,449 Patches Fix 1,235 CVEs, 261 Critical On July 21, Oracle released its July 2026 Critical Patch Update (CPU), setting a record for the largest single patch release in the company’s history. This CPU contains **1,449 security patches** fixing **1,235 independent CVEs** across 32 Oracle product families, of which **261 patches are rated Critical**. **Patch Distribution by Product Family**: | Product Family | Patches | Remotely Exploitable Without Authentication | |—————-|———|———————————————| | Oracle E‑Business Suite | 410 | 45 | | Oracle Fusion Middleware | 355 | 219 | | Oracle Communications | 168 | 122 | | Oracle MySQL | 54 | 9 | | Oracle Database Server | 15 | 6 | **Context for This Patch**: Oracle had already issued an urgent “Prepare Now” warning a week earlier, emphasising that AI is fundamentally lowering the barrier to discovering and exploiting vulnerabilities – frontier AI models can analyse software changes, reverse‑engineer security patches, and develop attack paths at unprecedented speed. Oracle has collaborated with state‑of‑the‑art […]

HiddenMerit Daily · Issue 59

# 📊 HiddenMerit Daily · Issue 59 > **Focus on Database Frontiers, Practical Insights for DBAs** > July 21, 2026 | 5 Selected Global Breaking News ## 01|Oracle Issues Urgent Warning: July 21 Release Update to Fix Large Number of High‑Risk Vulnerabilities, Immediate Deployment Recommended On July 13, Oracle issued an urgent security warning, strongly recommending that all customers running supported Oracle Database versions (including Oracle Database 19c and Oracle AI Database 26ai) immediately test and deploy the Release Update (RU) after its release on July 21. **Background**: New frontier AI models are significantly lowering the barrier to discovering and exploiting software vulnerabilities – these models can identify weaknesses, analyse software changes, reverse‑engineer security patches, and develop potential attack paths at unprecedented speed and scale. AI models are also becoming increasingly adept at combining multiple weaknesses across the application and data stack into complex attacks, even when individual weaknesses do not themselves pose a serious risk. As a result, protecting systems solely at the network or application layer is no longer sufficient; enterprises must protect the entire technology stack. **Fixes Included in This RU**: Oracle has collaborated with state‑of‑the‑art models from Anthropic and OpenAI to proactively identify and fix potential […]

HiddenMerit Daily · Issue 58

# 📊 HiddenMerit Daily · Issue 58 > **Focus on Database Frontiers, Practical Insights for DBAs** > July 20, 2026 | 5 Selected Global Breaking News ## 01|CAICT: Domestic Databases Enter Core System “Deep Water,” AI‑Native Leads New Industry Landscape On July 9, at the 2026 Trustworthy Database Development Conference, CAICT released the “Database Development Research Report (2026).” The report notes that domestic databases have basically completed peripheral system replacement and have officially entered the **critical business system breakthrough phase**. **Key Data**: – The global database market reached **$131.6 billion** in 2025 (approximately RMB 894.09 billion). – The Chinese database market reached **$9.49 billion** in 2025 (approximately RMB 64.48 billion), and is expected to reach **RMB 97.974 billion** by 2028, with a CAGR of 13.06%. – The number of domestic database vendors has shrunk from a peak of 167 to 94, with a clear head‑concentration effect and an intensifying “Matthew effect.” **AI‑Native Becomes the Main Theme**: The report points out that database technology is accelerating its evolution toward the **AI‑native direction**, and the global database industry is entering a new phase of landscape restructuring. The role of databases is upgrading from “underlying support systems” to “core engines enabling intelligent decision‑making […]

HiddenMerit Daily · Issue 56

# 📊 HiddenMerit Daily · Issue 56 > **Focus on Database Frontiers, Practical Insights for DBAs** > July 6, 2026 | 5 Selected Global Breaking News ## 01|Kingware Releases Manufacturing Scenario Database Evolution White Paper: SQL Server Replacement Enters “Deep Water” On July 5, CETC Kingware published a technical article titled “Database Evolution in Manufacturing Scenarios: How Kingware Replaces SQL Server,” pointing out that the data surge on industrial shop floors has exceeded the processing capacity of single‑node systems. Traditional database architectures that rely on vertical scaling are facing unprecedented performance challenges. Over the next 1‑3 years, manufacturing enterprises will no longer face only the “choice of replacement,” but must answer the strategic question: “How do we build an autonomous data foundation in the context of de‑IOE?” **Three Paradigm Shifts**: 1. **Hybrid Workloads Become the Norm**: The IT architecture of modern factories is shifting from separated OLTP and OLAP to HTAP mode. The same data system must simultaneously handle tens of thousands of device instruction writes per second and minute‑level production report analysis. 2. **Distributed Architecture Becomes a Hard Requirement**: When a single table exceeds 100 million rows with daily increments exceeding 1 million rows, the index maintenance cost of […]