一次SQL调优 聊一聊 SQLSERVER 数据页

news2024/9/27 19:24:10

一:背景

1.讲故事

最近给一位朋友做 SQL 慢语句 优化,花了些时间调优,遗憾的是 SQLSERVER 非源码公开,玩起来不是那么顺利,不过从这次经历中我觉得明年的一个重大任务就是好好研究一下它,争取在 SQLSERVER 性能优化上做一些成绩,哈哈! 个人觉得要想深入研究 SQLSERVER,得从它的存储引擎说起,说到存储引擎又得从核心的 数据页 说起,毕竟 mdf 就是由 数据页 拼出来的,当然理解的不对大家可以指出来。

二:理解数据页

1. 什么是数据页

一般来说,对大块资源或者数据进行高效管理都会按照一定粒度来划分的,比如说 Windows 对内存的管理就是按照 内存页 (4k) 来进行划分,言外之意就是 SQLSERVER 对 mdf 的管理也是按照 数据页 (8k) 来划分的,画个图大概就是这样的。

那如何来验证这个结论呢?刚才也说了数据都在数据页上,我们弄点数据然后在指定的数据页上提取出来就好了,这里用的是 SQLServer 2019


CREATE DATABASE MyTestDB
GO
USE MyTestDB;
GO
IF OBJECT_ID('person') IS NOT NULL
    DROP TABLE person;
CREATE TABLE person
(
    id INT IDENTITY,
    name CHAR(5)
);
GO
INSERT INTO dbo.person( name ) VALUES ('john');
INSERT INTO dbo.person( name ) VALUES ('mary');

2. 寻找数据所在的数据页

刚才图中也说了 mdf 是由无数个 数据页 拼出来的,那如何找到 person 表所在的数据页呢?其实微软提供了一个 dbcc ind 命令可以帮我们洞察出来,同时记得开始 3604 标记,让输出显示在控制台上,而不是默认的错误日志中,这个命令具体怎么用,大家可以查看官方文档。


DBCC TRACEON(3604)
DBCC IND(MyTestDB,person, -1)

从输出看有两条记录,第一个是 PagePID=41 是 IAM 数据页,而PagePID=280 就是我们 person 表记录所在的数据页编号,也就是说 person 表的记录在 mdf 文件偏移为 0n280 * 0n8192 的位置,用 WinDbg 算一下就是 0x00230000


0:090> ? 0n280 * 0n8192
Evaluate expression: 2293760 = 00000000`00230000

那是不是呢?可以用 WinHex 验证一下,为了不出现进程占用,先把 MyTestDB 下线了,最后记得再上线即可。


ALTER DATABASE MyTestDB SET OFFLINE

ALTER DATABASE MyTestDB SET ONLINE

从 WinHex 上看,果然是在偏移为 0x00230000 这个数据页上。

3. 如何从内存中看到数据页

刚才我们看到的数据页只是物理硬盘上的,但数据页和数据页之间的逻辑关系肯定是在内存中用一定的数据结构来承载的,接下来看下这个 数据页 映射到 SQLSERVER 进程内存的哪里呢?微软提供了 DBCC PAGE 命令可以查看指定 数据页 的详细信息。


DBCC TRACEON(3604)
DBCC PAGE(MyTestDB,1,280,2)

输出结果如下:


DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

PAGE: (1:280)


BUFFER:


BUF @0x000002B41104F480

bpage = 0x000002B3F0632000          bPmmpage = 0x0000000000000000       bsort_r_nextbP = 0x000002B41104F3D0
bsort_r_prevbP = 0x0000000000000000 bhash = 0x0000000000000000          bpageno = (1:280)
bpart = 1                           ckptGen = 0x0000000000000000        bDirtyRefCount = 0
bstat = 0x9                         breferences = 0                     berrcode = 0
bUse1 = 12454                       bstat2 = 0x0                        blog = 0x15ab215a
bsampleCount = 0                    bIoCount = 0                        resPoolId = 0
bcputicks = 0                       bReadMicroSec = 182                 bDirtyContext = 0x0000000000000000
bDbPageBroker = 0x0000000000000000  bdbid = 10                          bpru = 0x000002B3FA708040

PAGE HEADER:


Page @0x000002B3F0632000

m_pageId = (1:280)                  m_headerVersion = 1                 m_type = 1
m_typeFlagBits = 0x0                m_level = 0                         m_flagBits = 0x8200
m_objId (AllocUnitId.idObj) = 179   m_indexId (AllocUnitId.idInd) = 256 
Metadata: AllocUnitId = 72057594049658880                                
Metadata: PartitionId = 72057594043170816                                Metadata: IndexId = 0
Metadata: ObjectId = 581577110      m_prevPage = (0:0)                  m_nextPage = (0:0)
pminlen = 13                        m_slotCnt = 2                       m_freeCnt = 8060
m_freeData = 128                    m_reservedCnt = 0                   m_lsn = (37:584:3)
m_xactReserved = 0                  m_xdesId = (0:0)                    m_ghostRecCnt = 0
m_tornBits = -116446693             DB Frag ID = 1                      

Allocation Status

GAM (1:2) = ALLOCATED               SGAM (1:3) = NOT ALLOCATED          PFS (1:1) = 0x41 ALLOCATED  50_PCT_FULL
DIFF (1:6) = CHANGED                ML (1:7) = NOT MIN_LOGGED           

DATA:


Memory Dump @0x000000F840DF8000

000000F840DF8000:   01010000 00820001 00000000 00000d00 00000000  ....................
000000F840DF8014:   00000200 b3000000 7c1f8000 18010000 01000000  ........|...........
000000F840DF8028:   25000000 48020000 03000000 00000000 00000000  %...H...............
000000F840DF803C:   1b2a0ff9 00000000 00000000 00000000 00000000  .*..................
000000F840DF8050:   00000000 00000000 00000000 00000000 10000d00  ....................
000000F840DF8064:   01000000 6a6f686e 20020000 10000d00 02000000  ....john ...........
000000F840DF8078:   6d617279 20020000 00002121 21212121 21212121  mary .....!!!!!!!!!!
000000F840DF808C:   21212121 21212121 21212121 21212121 21212121  !!!!!!!!!!!!!!!!!!!!
000000F840DF80A0:   21212121 21212121 21212121 21212121 21212121  !!!!!!!!!!!!!!!!!!!!
...
000000F840DF9FF4:   21212121 21212121 70006000                    !!!!!!!!p.`.

OFFSET TABLE:

Row - Offset                        
1 (0x1) - 112 (0x70)                
0 (0x0) - 96 (0x60)                 


DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

Completion time: 2022-12-30T17:48:20.5466040+08:00

从上面的 Memory Dump 区节中可以看到,数据在进程内存的 000000F840DF8064 ~ 000000F840DF8078 范围内,这里要吐槽的是内存地址按照 大端布局 的,看起来很不习惯,可以用 windbg 用 小端布局 来显示。


0:116> dp 000000F840DF8064
000000f8`40df8064  6e686f6a`00000001 000d0010`00000220
000000f8`40df8074  7972616d`00000002 21210000`00000220
000000f8`40df8084  21212121`21212121 21212121`21212121
000000f8`40df8094  21212121`21212121 21212121`21212121
000000f8`40df80a4  21212121`21212121 21212121`21212121
000000f8`40df80b4  21212121`21212121 21212121`21212121
000000f8`40df80c4  21212121`21212121 21212121`21212121
000000f8`40df80d4  21212121`21212121 21212121`21212121

4. sql 请求源码研究

喜欢玩 windbg 的朋友肯定想对 sqlserver 进行汇编级洞察,比如研究下 sql 请求在 sqlserver 里面的执行流是什么样的? 其实很简单,我们可以这样处理,使用 ba 对 john 的内存地址下一个硬件断点,即 ba r4 000000f840df8064+0x4 ,然后在 SSMS 上执行一条 SELECT * FROM person 语句,因为要提取 john 自然就会命中。


0:104> ba r4 000000f840df8064+0x4 
0:104> g
Breakpoint 0 hit
sqlmin!BTreeMgr::GetHPageIdWithKey+0x4a:
00007ff8`d4ea121a 48894c2478      mov     qword ptr [rsp+78h],rcx ss:000000f8`45278028=0000024800000025
0:102> k
 # Child-SP          RetAddr               Call Site
00 000000f8`45277fb0 00007ff8`d4ea0b59     sqlmin!BTreeMgr::GetHPageIdWithKey+0x4a
01 000000f8`45278450 00007ff8`d4ea08b7     sqlmin!IndexPageManager::GetPageWithKey+0x119
02 000000f8`45278d20 00007ff8`d4eb22d1     sqlmin!GetRowForKeyValue+0x203
03 000000f8`45279880 00007ff8`d4eb2a39     sqlmin!IndexDataSetSession::GetRowByKeyValue+0x141
04 000000f8`45279a70 00007ff8`d4eb279b     sqlmin!IndexDataSetSession::FetchRowByKeyValueInternal+0x230
05 000000f8`45279ed0 00007ff8`d4eb2883     sqlmin!RowsetNewSS::FetchRowByKeyValueInternal+0x437
06 000000f8`4527a000 00007ff8`d4eaadab     sqlmin!RowsetNewSS::FetchRowByKeyValue+0x96
07 000000f8`4527a050 00007ff8`d4f93d60     sqlmin!CMEDScan::StartSearch+0x4f8
08 000000f8`4527a170 00007ff8`d4f93f3a     sqlmin!CMEDCatYukonObject::GetTemporalCurrentTableId+0x10e
09 000000f8`4527a380 00007ff8`d801f0d1     sqlmin!CMEDProxyRelation::GetTemporalCurrentTableId+0x7a
0a 000000f8`4527a3c0 00007ff8`d801dfb8     sqllang!CAlgTableMetadata::FPartialBind+0xb58
0b 000000f8`4527a580 00007ff8`d80394b3     sqllang!CAlgTableMetadata::Bind+0x317
0c 000000f8`4527a620 00007ff8`d800415d     sqllang!CRelOp_Get::BindTree+0x78f
0d 000000f8`4527a890 00007ff8`d80418a1     sqllang!COptExpr::BindTree+0x85
0e 000000f8`4527a8c0 00007ff8`d800415d     sqllang!CRelOp_FromList::BindTree+0x31
0f 000000f8`4527a920 00007ff8`d802c2a3     sqllang!COptExpr::BindTree+0x85
10 000000f8`4527a950 00007ff8`d800415d     sqllang!CRelOp_QuerySpec::BindTree+0x2e8
11 000000f8`4527aa60 00007ff8`d80042dd     sqllang!COptExpr::BindTree+0x85
12 000000f8`4527aa90 00007ff8`d800415d     sqllang!CRelOp_SelectQuery::BindTree+0x489
13 000000f8`4527ab80 00007ff8`d8003f23     sqllang!COptExpr::BindTree+0x85
14 000000f8`4527abb0 00007ff8`d8004e47     sqllang!CRelOp_Query::FAlgebrizeQuery+0x4bd
15 000000f8`4527ae30 00007ff8`d7ff5576     sqllang!CProchdr::FNormQuery+0x8f
16 000000f8`4527ae70 00007ff8`d7ff4a79     sqllang!CProchdr::FNormalizeStep+0x5bd
17 000000f8`4527b4b0 00007ff8`d7ff5124     sqllang!CSQLSource::FCompile+0xea5
18 000000f8`4527e1b0 00007ff8`d7e659c3     sqllang!CSQLSource::FCompWrapper+0xcb
19 000000f8`4527e280 00007ff8`d7e6387a     sqllang!CSQLSource::Transform+0x721
1a 000000f8`4527e3e0 00007ff8`d7e6e67b     sqllang!CSQLSource::Execute+0x4fa
1b 000000f8`4527e8c0 00007ff8`d7e6d815     sqllang!process_request+0xca6
1c 000000f8`4527efc0 00007ff8`d7e6d5ef     sqllang!process_commands_internal+0x4b7
1d 000000f8`4527f0f0 00007ff8`d4096523     sqllang!process_messages+0x1d6
1e 000000f8`4527f2d0 00007ff8`d4096e6d     sqldk!SOS_Task::Param::Execute+0x232
1f 000000f8`4527f8d0 00007ff8`d4096c75     sqldk!SOS_Scheduler::RunTask+0xa5
20 000000f8`4527f940 00007ff8`d40bb160     sqldk!SOS_Scheduler::ProcessTasks+0x39d
21 000000f8`4527fa60 00007ff8`d40baa5b     sqldk!SchedulerManager::WorkerEntryPoint+0x2a1
22 000000f8`4527fb30 00007ff8`d40bafa4     sqldk!SystemThreadDispatcher::ProcessWorker+0x3ed
23 000000f8`4527fe30 00007ff8`f6d86fd4     sqldk!SchedulerManager::ThreadEntryPoint+0x3b5
24 000000f8`4527ff20 00007ff8`f865cec1     KERNEL32!BaseThreadInitThunk+0x14
25 000000f8`4527ff50 00000000`00000000     ntdll!RtlUserThreadStart+0x21

从线程栈上看,有 SQLSERVER 核心的 Scheduler ,Task 以及 命令分析器,查询优化器,查询执行器 等各种核心元素,后续再慢慢研究吧。

三: 总结

深入的理解数据,索引在数据页上的布局,可以从根本上帮助我们理解如何减少请求在数据页上的流转,减少逻辑读,从而提升 sql 的查询性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/129560.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp实现音频播放抢占系统音频焦点

项目为使用uniapp框架开发的Android/iOS APP应用 实现功能需求 假设手机正在播放音乐,当前APP处于前台收到消息,需播放提示音提示用户。目标为降低后台正在播放音乐的音量,播放提示音,播放完毕后恢复后台音乐音量 需求分析 乍…

拉伯证券|新能源汽车前11月产销翻倍,渗透率升至三分之一

2022年11月,国内新能源轿车渗透率已升至33.8%,创前史新高。 2022年的最终一个交易日早盘,两市高开高走,沪指涨0.61%,深证成指涨0.35%,创业板指涨0.3%。板块上来看,Web3.0、虚拟人、网络游戏概念…

BI技巧丨RankxYoY

群友:PowerBI可以实现两年的排名差异么? 白茶:可以!安排! 本期的问题,来自于群内小伙伴的一个实际应用场景。在实际业务中,这个需求属于常见类型,展示当前排名时,用户关注…

【软件测试】 测试开发?看看一线大厂需求的测试开发技能......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 能在一线大厂工作是…

FPGA知识汇集-FPGA系统时序理论

时序约束条件 下面来具体讨论一下系统时序需要满足的一些基本条件。我们仍然以下图的结构为例,并可以据此画出相应的时序分析示意图。 在上面的时序图中,存在两个时序环,我们称实线的环为建立时间环,而虚线的环我们称之为保持时间…

Java死锁

一.死锁是什么? 死锁指两个或者两个以上的线程在执行过程中,去争夺同样一个共享资源,造成的相互等待的现象,如果没有外部干预,线程会一直阻塞,无法往下执行,这样一直处于相互等待资源的线程叫做…

极米Z6X Pro怎么样?极米Z6X Pro亮度如何?极米Z6X Pro值得入手吗?

投影仪不知怎么选?不妨看看极米Z6X Pro,半山黛青的全新配色下,是仅有5.3cm的轻薄机身,1.4kg的重量,一只手就可以轻松拿取。相比同价位的投影产品,极米Z6X Pro摆放方便又不占地,外观时尚精致&…

Go Map

Go Map map 是一种key-value的键值对存储结构,其中key不能重复,无序。底层是hmap结构,hmap中维护buckets(bmap结构)。结构定义 type hmap struct {count int // 元素个数B uint8 // buckets已扩容的次数,buckets长…

JVM-Java内存区域

1、运行时数据区域 运行时数据区域:程序计数器、Java虚拟机栈、本地方法栈、堆、方法区。 非运行时数据区域:直接内存。 (1)程序计数器 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制。在多线…

谷歌2022年最受欢迎Chrome浏览器扩展程序:包含Tango,Compose AI等

根据截图排序从左到右依次介绍如下: RoPro:Roblox.com网站体验增加了许多有用且独特的功能; MyBib:自动创建 APA 样式、MLA 格式和哈佛引用样式引文; eJOY English:翻译自Netflix, Youtube, iFlix上的字…

Codeforces Round #841 (Div. 2) and Divide by Zero 2022(A-D)

Codeforces Round #841 (Div. 2) and Divide by Zero 2022(A-D) 题目链接限制AJoey Takes Moneystandard input/output1 s, 256 MBBKill Demodogsstandard input/output1 s, 256 MBCEven Subarraysstandard input/output2.5 s, 256 MBDValiant’s New M…

数字经济指标构建-各省、地级市匹配上市公司数据、城市数字化指数

一、中国城市数字经济指数2017-2022年(数据代码报告) 中国城市数字化指数全面覆盖城市数字化规划、建设、运营的各个方面,能够有效评估城市各个领域的数字化建设水平和运营效果。 全国城市数字经济发展热图 数据来源:中国城市数字…

语音转换之CycleGan-VC2:原理与实战

非平行语音转换CycleGAN 之前学习了传统统计学习里的经典的语音转换模型GMM。随着深度学习的发展,出现了更好的语音转换方法,今天学习较为经典的CycleGan。 平行语音转换一般流程 典型代表就是基于GMM的语音转换。平行数据就是说源语音和目标语音一一…

矢量图斑局部狭长判断和定位局部狭长部分(PostGIS、Java、C#实现)

矢量数据在数据采集过程中由于数据处理导致出现局部狭窄的面状部分,如下图 1. 定义和解决方法 狭长结构是指图斑几何形态上窄而长的部分,符号化后出现图形粘连压盖现象,导致难以在图面上清晰地表达出来。因此,依据地图表达比例尺因素需要对狭长结构进行融解处理。在…

helm部署frps和连接

文章目录一. helm部署frps1.1 下载1.2 部署1.2.1 不开启dashboard界面1.2.2 开启dashboard界面1.2.3 卸载1.3 查看1.4 IP Port 允许端口1.5 Web Ingress二. frpc客户端连接2.1 IP Port 连接2.2 Web 域名连接一. helm部署frps 1.1 下载 mkdir -p /root/i/helm && cd…

并查集专题1_图篇

1.并查集介绍 并查集支持查询和合并操作,只回答两个节点是不是在一个连通分量中,并不回答路径问题。 如果一个问题具有传递性,可以考虑用并查集。并查集最常见的一种设计思想是把在同一个连通分量中的节点组织成一个树形结构。 2.并查集的…

动作捕捉技术应用于地面移动机器人协同

《一千零一夜》故事集中收录的《阿拉丁神灯》深受读者们的喜爱,其中阿拉丁拥有一块神奇的魔毯,它具有运载功能,可以将物体轻松便捷的从一个地方转移到另一个地方。在现实生活中,可变形布作为一种轻量便携且具有良好适应性的载体&a…

redhat9中mysql常用命令(持续更新)

目录 1、查看当前用户 2、查看选择的数据库 3、创建数据库 4、创建数据表 5、插入数据 6、查看表所以字段的内容 7、查看数据库当前密码策略 8、查看密码插件 9、更改密码策略为LOW,改为LOW或0 10、 更改密码长度 11、设置大小写、数字和特殊字符均不要求 …

微服务系列专栏介绍

文章目录一 专栏介绍1.1 微服务行业背景不同行业IT系统更新频率IT系统存在的问题微服务架构在企业中应用情况1.2 什么是微服务1.3 微服务的特点1.4 微服务诞生背景1.5 微服务架构的优势二 专栏目标三 专栏涉及技术四 专栏架构1.微服务架构:2.Go语言3.go-micro架构4.…

入侵无线WiFi的主要方式及防护要点

从攻击形态上看,无线网络攻击主要可以分为三个大类:被动型攻击、主动型攻击以及针对网络组件的攻击: 被动攻击一般发生在攻击者处于无线网络范围内并可以监视无线通信内容时,最常见的被动攻击是数据包嗅探。由于被动攻击者只是监…