我们都知道数据在存储引擎中是以页的形式组织的,但数据页在不同的组织形式中其中对应的数据行存储是不尽相同的,这里通过实例为大家介绍下堆表的中特有的一种情形Forwared Records及处理方式.

概念

堆表中,当对其中的记录进行更新时,如果当前数据页无法满足更新行的容量,此时这行记录将会转移到新的数据页中,而原数据页中将会留下指针(文件号,页号,槽号)链接到新的数据页中.

Code 创建测试数据

create database testpage
go
use testpage
go
create table testtb
(
id int identity(1,1),
str1 char(100) default replicate('a',100),
str2 varchar(2000) default replicate('b',500),
str3 varchar(2000) default replicate('c',1000)
)
goinsert into testtb default values
go 20

Code 查看相关数据页 如图1-1

DBCC TRACEON(3604)GODBCC IND(testpage,'testtb', 1)---find the data pageGODBCC PAGE('testpage', 1, 79, 3)-----view data page find slot 2(ID=3)GO

图1-1

现在我们来更新ID=3的数据使当前数据页(79)无法容纳此行数据,然后观察数据页,

Code

update dbo.testtb set str2=replicate('t', 1000) where ID=3--update ID=3

GO

DBCC PAGE('testpage', 1, 79, 3)

GO

继续找到slot 2槽位(ID=3)观察 如图1-2所示,此时slot2数据的Record Type = FORWARDING_STUB,也就是此时槽位2只留下RID记录,数据转到其他数据页中了(Forwarding to  =  file 1 page 94 slot 0  )

图1-2

这里稍微深入的讲下RID的存储内容,实例中根据dbcc page已经给我们展示RID的内容,实际上存储是16进制的如图1-2中的黑色部分(045e0000 00010000 00

).具体对应RID内容如图1-3

图1-3

我们在找到实际存储ID=3的数据页看下数据内容(1:94:0) 如图1-4

图中我省去了数据内容

code

DBCC PAGE('testpage', 1, 94, 3)

GO

图1-4

接下来我们继续更新ID=3让新的数据页也无法容纳它,然后观察相应的数据页如图1-5(三个dbcc page 合成图)

(此时ID=3的原始页94,槽号2指向了新的数据页位置184)如图所示1-5所示

code

insert into testtb default values
go 20----先插入一些数据update dbo. Testtb set str2=replicate('t', 2000),str3=replicate('t', 2000) where ID=3 GO---继续更新ID=3DBCC PAGE('testpage', 1, 79, 3)--------源ID=3,现在执行(1:184:2)
GO
DBCC PAGE('testpage', 1, 94, 3)--------第一次修改时ID=3存储位置(1:94:0),现在slot 0没有了
GO
DBCC PAGE('testpage', 1, 184, 3)------目前id=3的数据存储位置
GO

图1-5

可以看出id=3的原始页(1:79:2)的数据再次变更后的由(1:94:0)挪到了(1:184:2)中,

而页号94槽号0就不存在了.

堆表中的非聚集索引.

当堆表中有非聚集索引存在时,非聚集索引RID指向的原始页位置

我们通过实例看下

注:关于heap rid我就不做详细介绍了,实例中通过查询转换可以算出10进制对应的RID

Code

CREATE UNIQUE NONCLUSTERED INDEX inx_1 ON testtb (id )DBCC IND(testpage,'testtb', -1)----find the index page (page type 2)115
GO
DBCC PAGE('testpage', 1, 115, 3)---find the heap rid where id=3 heap rid =0x4F00000001000200DECLARE @HeapRid BINARY(8)
SET @HeapRid = 0x4F00000001000200SELECTCONVERT (VARCHAR(5),CONVERT(INT, SUBSTRING(@HeapRid, 6, 1)+ SUBSTRING(@HeapRid, 5, 1)))+ ':'+ CONVERT(VARCHAR(10),CONVERT(INT, SUBSTRING(@HeapRid, 4, 1)+ SUBSTRING(@HeapRid, 3, 1)+ SUBSTRING(@HeapRid, 2, 1)+ SUBSTRING(@HeapRid, 1, 1)))+ ':'+ CONVERT(VARCHAR(5),CONVERT(INT, SUBSTRING(@HeapRid, 8, 1)+ SUBSTRING(@HeapRid, 7, 1)))AS 'Fileid:Pageid:slot'

可以看到select 的输出正好是(1:79:2)我们原始的id=3的位置

关于性能

由于forwarded record的存在,当访问到这种数据行时,会消耗额外的随机IO,从而影响性能.更有甚者,由于额外的数据页被放入内存中,造成BP的污染,致使性能下降.

(研发要求对一个频繁访问的大堆表更新扩充栏位,执行完了性能依旧下降有木有?)

我们通过简单实例来看下

访问forwarded record会造成额外IO如图2-1

Code

set statistics io onselect * from testtb where id=2select * from testtb where id=3

图2-1

当表数据量大时,大批量更新扩充栏位会造成对缓冲池的污染

code

create table testbp
(
id int identity(1,1),
str1 char(100) default replicate('a',100),
str2 varchar(2000) default replicate('b',500),
str3 varchar(2000) default replicate('c',1000)
)
go
insert into testbp default values
go 10000dbcc dropcleanbuffers
select * from testbpSELECT count(*)*8/1024 AS 'Cached Size (MB)'
,CASE database_id
WHEN 32767 THEN 'ResourceDb'
ELSE db_name(database_id)
END AS 'Database'
FROM sys.dm_os_buffer_descriptors with(nolock)
where db_name(database_id)='testpage'
GROUP BY db_name(database_id) ,database_id
-----buffer pool 15MBupdate dbo. testbp set str2=replicate('t', 1000)---make forwarded recordesdbcc dropcleanbuffersselect * from testbpSELECT count(*)*8/1024 AS 'Cached Size (MB)'
,CASE database_id
WHEN 32767 THEN 'ResourceDb'
ELSE db_name(database_id)
END AS 'Database'
FROM sys.dm_os_buffer_descriptors with(nolock)
where db_name(database_id)='testpage'
GROUP BY db_name(database_id) ,database_id
------31MB

顺序执行代码时可以看出,testpage表更改前后占Buffer Pool的大小分别为15M,31M,对BP影响明显.

监控/发现

实际生产环境中我们需要监控一些性能指标用来辅助DBA解决问题,保证运维效率,针对这里,我们监控性能计数器中SQL Server Access Methods对象中的forwarded records/sec,如果你设定的了性能Baseline,这个值如果有异常变化,则需要我们关注.

同时我们也可以根据系统的DMF找出特定对象的forwarded records信息.代码如下

select object_name(object_id) as objectName,index_type_desc,forwarded_record_countfrom sys.dm_db_index_physical_stats(db_id(),null,null,null, 'detailed')where object_name(object_id)='testbp'
------view the forwarded records info

注:可以通过简单的Batch检索整个库甚至实例中的堆表的相关信息,有兴趣的朋友自己写下.

处理

如果发现了因为forwarded Recordes引起的性能问题,我们可以选择表中创建聚集索引改变数据组织结构(forwarded Recordes只在堆表中存在).如果无法添加聚集索引,也可以选择重组堆表(alter table heap rebuild)操作时应注意时间窗口

结语

任何事物都存在因果,套用数据库系统中,我们应该清楚自己的所作所为,以及带来的效用/影响.合理到位的分析,评估会让我们的工作变得从容.

转载于:https://www.cnblogs.com/shanksgao/p/4045917.html

SQL Server 存储引擎-剖析Forwarded Records相关推荐

  1. SQL SERVER 存储大全以及常见实例

    xp_cmdshell --*执行DOS各种命令,结果以文本行返回. xp_fixeddrives --*查询各磁盘/分区可用空间 xp_loginconfig --*报告SQL Server 实例在 ...

  2. SQL Server存储机制

    SQL Server存储机制 1.区段 区段(extent)是用来为表和索引分配空间的基本存储单元.它由8个连续的64KB数据页组成. 基于区段(而不是实际使用空间)分配空间的概念的要点: 一旦区段已 ...

  3. 实现基于SQL Server存储账号的MDaemon群集

    本章概述      MDaemon Server(以下简称 MDaemon) 的冗余方案有多种选择,本章将以 微软 公司出品的 SQL Server 2005 简体中文企业版(以下简称 SQL2005 ...

  4. sql server 2008学习8 sql server存储和索引结构

    sql server的存储机制 区段: 是用来为表和索引 分配空间的基本存储单元. 由 8个连续的页面构成,大小为64kb. 区段的注意事项: 一旦区段已满,那么下一记录 将要占据的空间不是记录的大小 ...

  5. SQL Server 存储(3/8):理解GAM和SGAM页

    我们知道SQL Server在8K 的页里存储数据,区就是物理上连续的8个页.当我们创建一个数据库,数据文件会被逻辑分为页和区,当用户对象创建时,页会分配给它用来存储数据.GAM(Global All ...

  6. 使用SQL Server存储ASP.NET Session变量

    创建和配置ASP.NET Session状态数据库 在基于NLB(网络负载平衡)环境下的ASP.NET Web应用程序开发,我们需要将Session存储在数据库中供多个Web应用程序调用,以下为配置方 ...

  7. MySQL简介、M有SQL的存储引擎、表、字段和数据

    Java知识点总结:想看的可以从这里进入 目录 2.MySQL特性介绍 2.1.MySQL简介 2.2.存储引擎 2.3.表.字段.数据 2.MySQL特性介绍 2.1.MySQL简介 MySQL 是 ...

  8. MS SQL Server存储生僻字变为问号(?)的解决方法

    使用mssql的小伙伴经常会遇到生僻字的问题. 处理方法: 1.将存储生僻字的字段类型改为nvarchar,建议用语句alter修改以免数据库卡死. 2.使用企业管理器编辑前200行,手工将生僻字填到 ...

  9. sql server 性能_SQL Server预读机制; 概念和性能提升

    sql server 性能 The user's read requests in SQL Server are managed and controlled by the SQL Server Re ...

最新文章

  1. 数值比较器实现_伺服操作器FBQA7000销售-老友网
  2. SQL Server创建视图——视图的作用
  3. 简单分析算法的时间复杂度
  4. 我司那产品经理丨第二期
  5. Android 数据库升级解决方案
  6. 网络层协议简介之ICMP和ARP
  7. CG-CTF-Web-SQL注入1
  8. uva 11971 Polygon
  9. java native 关键字
  10. Linux入门学习(八)
  11. 我的docker随笔5:docker-compose的安装与使用
  12. 2020年,我靠Java加薪3倍工资,只因做对这件事
  13. 黄河计算机学校,黄河(数学与计算机学院)老师 - 重庆三峡学院 - 院校大全
  14. oracle 物料属性批次过期,物料批次特性值
  15. 通过Keepalived实现Redis Failover自动故障切换功能[实践分享]
  16. 王者荣耀钓鱼网源码php,王者荣耀钓鱼页面
  17. 尚观嵌入式c语言视频教程
  18. 软件实现兼容多厂商服务器运维管理,华为eSight ICT运维系统 略述网管系统
  19. 基于SpringBoot超市库存管理系统+文档
  20. mysql 表分区 排序_mysql 表分区

热门文章

  1. 在python中、对于函数定义代码的理解_python中如何理解装饰器代码?
  2. 关于ajax请求400问题解决
  3. java swing 模拟发牌_用java设计一个发牌程序
  4. ad20中怎么多选操作改层_在操作系统中CPU是怎么调度的
  5. 数据结构 | B树、B+树、B*树
  6. leader:你的代码太烂了我根本看不懂
  7. go语言快速刷《程序员面试金典》(3)
  8. leetcode647 回文子串
  9. 《Python Cookbook 3rd》笔记(2.11):删除字符串中不需要的字符
  10. 《Python Cookbook 3rd》笔记(2.3):用Shell通配符匹配字符串