sqlserver 数据存储

/*

测试数据存储

*/

准备工作

-- =============================================

-- 创建测试数据库

-- =============================================

create database test

go

-- =============================================

-- 创建测试表

-- =============================================

use test

go

create table test(

id int identity(1,1) not null,

name varchar(10) not null

)

go

-- =============================================

-- 导入数据

-- =============================================

declare @i int

set @i=0

while @i<100

begin

insert into test(name) values(‘aaa‘)

set @[email protected]+1

end

go

declare @i int

set @i=0

while @i<100

begin

insert into test(name) values(‘bbb‘)

set @[email protected]+1

end

go

declare @i int

set @i=0

while @i<100

begin

insert into test(name) values(‘ccc‘)

set @[email protected]+1

end

go

declare @i int

set @i=0

while @i<100

begin

insert into test(name) values(‘ddd‘)

set @[email protected]+1

end

go-- =============================================

-- 查看相关信息

-- =============================================

SELECT COUNT(*) FROM test

select * from sys.partitions where [object_id]=object_id(‘test‘)

select * from sys.objects where name=‘test‘ and type_desc=‘USER_TABLE‘

select * from sys.indexes where [object_id]=2105058535

EXEC sp_spaceused ‘test‘

GO

结果如下:

测试步骤

1、我们需要找到数据存放于哪些数据页上面。

打开一个session 1 执行以下代码:

begin tran

update test set name=‘fff‘ where id =1

waitfor delay ‘00:00:05‘

select * from test where id=2

同时打开session 2 执行以下代码

begin tran

update test set name=‘fff‘ where id =2

select * from test where id=1

同时打开session 3 执行以下代码:

select * from sys.dm_os_waiting_tasks where session_id>50

结果如下:

然后执行以下代码:

dbcc traceon(3604)

go

dbcc page(‘test‘,1,21,3)

go

结果如下:

从截图的红框部分可以发现,page(1:21)上存储了385条记录(具体说明见之后的页头信息说明),但是我们之前保存了400条记录,那剩下的15条记录存放在哪一个数据页?

回滚session 1 、 session 2的事务。

在session 1 中执行以下代码:

begin tran

update test set name=‘fff‘ where id =386

waitfor delay ‘00:00:05‘

select * from test where id=387

同时在session 2中执行以下代码:

begin tran

update test set name=‘fff‘ where id =387

select * from test where id=386

同时打开session 3 执行以下代码:

select * from sys.dm_os_waiting_tasks where session_id>50

结果如下:

然后执行以下代码:

dbcc traceon(3604)

go

dbcc page(‘test‘,1,55,3)

go

结果如下:

因为没有索引,所以页面无法知道下一页是那个页面。

m_prevPage = (0:0) :前一页的页码           m_nextPage = (0:0) :后一页页码

2、创建聚集索引

-- =============================================

-- 创建聚集索引

-- =============================================

create clustered index ix_test on test(id)

drop index ix_test on test

获取创建索引之后的数据页,同样需要把之前的方法再操作一遍,此时,我获取到的第一个数据页为page(1:79)

-- =============================================

-- 查看数据页存储

-- =============================================

dbcc traceon(3604)

go

dbcc page(‘test‘,1,79,3)

go

结果:

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

PAGE: (1:79)

BUFFER:

BUF @0x04C83CE0

bpage = 0x1DD2A000                   bhash = 0x00000000                   bpageno = (1:79)

bdbid = 7                            breferences = 0                      bcputicks = 0

bsampleCount = 0                     bUse1 = 27502                        bstat = 0xc0010b

blog = 0x1212121b                    bnext = 0x00000000

bdbid=7 :数据库ID,可以使用DB_NAME(7)得到数据库名为测试库test。

Bpageno=(1:79)  :数据页编号,1指文件ID,79指数据页ID

PAGE HEADER: (页头信息):

Page @0x1DD2A000

m_pageId = (1:79)                    m_headerVersion = 1                  m_type = 1

m_typeFlagBits = 0x0                 m_level = 0                          m_flagBits = 0x0

m_objId (AllocUnitId.idObj) = 30     m_indexId (AllocUnitId.idInd) = 256

Metadata: AllocUnitId = 72057594039894016

Metadata: PartitionId = 72057594038910976                                 Metadata: IndexId = 0

Metadata: ObjectId = 2105058535      m_prevPage = (0:0)                   m_nextPage = (1:89)

pminlen = 8                          m_slotCnt = 368                      m_freeCnt = 0

m_freeData = 7456                    m_reservedCnt = 0                    m_lsn = (30:248:60)

m_xactReserved = 0                   m_xdesId = (0:0)                     m_ghostRecCnt = 0

m_tornBits = 0

m_pageId = (1:79) :数据页编号

m_prevPage = (0:0) :上一页编号

m_nextPage = (1:89) :下一页编号,可以执行dbcc page(‘test’,1,89,3)来查看m_prevPage值必为(1:79)

Metadata: PartitionId = 72057594038910976 :所属分区ID

Metadata: AllocUnitId = 72057594039894016 :包含该页的分区单元ID

Metadata: ObjectId = 2105058535 :该页所属的对象ID

m_level = 0 :该页面在索引中的级别(数据页为0)

m_slotCnt = 368 :该页存储的数据行数,可以执行dbcc page(‘test’,1,89,3)来查看m_slotCnt必为32,368+32=400,与该表插入的行数完全一致。

Allocation Status

GAM (1:2) = ALLOCATED                SGAM (1:3) = ALLOCATED

PFS (1:1) = 0x64 MIXED_EXT ALLOCATED 100_PCT_FULL                         DIFF (1:6) = CHANGED

ML (1:7) = NOT MIN_LOGGED

Slot 0 Offset 0x60 Length 20

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 20

Memory Dump @0x0B2FC060

00000000:   30000800 01000000 03000002 00110014 †0...............

00000010:   00616161 ††††††††††††††††††††††††††††.aaa

Slot 0 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

DROPPED = NULL

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4 :第1行数据

id = 1  :字段id的值,实际长度为 Length 4 Length (physical) 4

Slot 0 Column 2 Offset 0x11 Length 3 Length (physical) 3

name = aaa   :字段name的值,实际长度为Length 3 Length (physical) 3

注意:

执行语句:update test set name=‘fffff‘ where id=1

再重新执行语句dbcc page(‘test’,1,79,3) 可以发现,slot 0 这行没有值。Page(1:79)有值的首行是从slot 1开始。这个数据仅仅只是更新而不是删除,原有slot 0的值去哪里里了?

执行dbcc page(‘test’,1,89,3) ,你会发现slot 32行变成了page(1:79)行的值。

从这里可以看出:update操作的原理,删除原有得数据行,并将数据重新插入到这个对象所属页面的最后一行。

Slot 1 Offset 0x74 Length 20

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 20

Memory Dump @0x0B2FC074

00000000:   30000800 02000000 03000002 00110014 †0...............

00000010:   00616161 ††††††††††††††††††††††††††††.aaa

Slot 1 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

DROPPED = NULL

Slot 1 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 2

Slot 1 Column 2 Offset 0x11 Length 3 Length (physical) 3

name = aaa

Slot 2 Offset 0x88 Length 20

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 20

Memory Dump @0x0B2FC088

00000000:   30000800 03000000 03000002 00110014 †0...............

00000010:   00616161 ††††††††††††††††††††††††††††.aaa

Slot 2 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

DROPPED = NULL

Slot 2 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 3

Slot 2 Column 2 Offset 0x11 Length 3 Length (physical) 3

name = aaa

.

.

.

.

.

.

Slot 366 Offset 0x1cf8 Length 20

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 20

Memory Dump @0x0B2FDCF8

00000000:   30000800 6f010000 03000002 00110014 †0...o...........

00000010:   00646464 ††††††††††††††††††††††††††††.ddd

Slot 366 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

DROPPED = NULL

Slot 366 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 367

Slot 366 Column 2 Offset 0x11 Length 3 Length (physical) 3

name = ddd

Slot 367 Offset 0x1d0c Length 20

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 20

Memory Dump @0x0B2FDD0C

00000000:   30000800 70010000 03000002 00110014 †0...p...........

00000010:   00646464 ††††††††††††††††††††††††††††.ddd

Slot 367 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

DROPPED = NULL

Slot 367 Column 1 Offset 0x4 Length 4 Length (physical) 4

id = 368

Slot 367 Column 2 Offset 0x11 Length 3 Length (physical) 3

name = ddd

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

结论

1、在没有聚集索引(非聚集索引我也测试了,但dbcc page的结果与没有索引时保持一致)的情况下,数据页的m_pageId 、m_prevPage属性值都为(0:0),也就是说,无法指向下一页。

但是,使用dbcc ind(‘test‘,2105058535,-1)可以看到页面的前后顺序(这里特别感谢肖磊),注意,由于我重新删除数据库并按照文章上面的步骤执行后,pageid有变化,已经对不上文章中的pageid。

2、当出现了聚集索引,数据会迁移到别的存储页面中。这就说明:重建聚集索引时,数据可能会从一个页面移动到另外的页面,从而产生大量的IO。

3、数据更新时,数据库先删除原有数据,同时再往改对象所存储的最后一页插入更新后的数据。这是武断的结论,感谢高文佳和肖磊,

高文佳的意见是:

对于update操作 在数据库上到底做UPDATE 还是做DELETE+INSERT,取决当前的空间是否能存放更新后的数据

肖磊的意见是:

m_freeCnt这个是指页面剩余空间,如果它是0,你再将其中一条记录update成更长的value,当然会引起页拆分。对于varchar类型,还有overflow的问题

我通过测试,在更新第二个数据页上面的id为380的数据发现,完全符合肖磊的意见,当数据页剩余存储空间足够时,即m_freeCnt值不为0时,数据的位置不会发生改变。

时间: 2024-08-11 15:36:30

sqlserver 数据存储的相关文章

sqlserver数据存储

概述 最近要分享一个课件就重新把这块知识整理了一遍出来,篇幅有点长,想要理解的透彻还是要上机实践. 正文 聚集索引 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 --创建测试数据库 CREATE DATABASE Ixdata GO USE [Ixdata] GO ---创建测试表 CREATE TABLE Orders (ID INT PRIMARY KEY IDENTITY(1,1), NAME CHAR(80)NOT NU

sqlserver数据导入到oracle中

原来的网站数据存储在sqlserver中,梳理好数据表的字段关系,需要导入到oracle数据库中.网上提供了若干解决方法.其中一个是在服务器有sqlserver和oracle的情况下直接进行导出工作.这个操作我尝试了,没有成功.之前的时候access可以导入到oracle中,然后就考虑把sqlserver的数据导入到access中.实际的操作是: 1.sqlserver到excel: 直接打开一个excel 2.excel到access access外部数据---〉excel,选择对应的exce

Android数据存储之SQLite的操作

Android作为一个应用在移动设备上的操作系统,自然也就少不了数据的存储.然而SQLite作为一个轻型的关系型数据库,基于其轻量.跨平台.多语言接口及安全性等诸多因数考虑,因而Android较大的数据存储采用了SQLite.SQLite与大多数关系型数据库一样都遵循ACID,语法也非常相似.只要您懂得mysql.sqlserver等关系型数据库的操作,只要查看下SQLite的官方文档便可快速上手.SQLite语法您可通过http://sqlite.org/lang.html进行查看. 接下来看

深入理解Sqlserver文件存储之页和应用 (转)

我们每天都在使用数据库,我们部门使用最多的关系数据库有Sqlserver,Oracle,有没有想过这些数据库是怎么存放到操作系统的文件中的?有时候为了能够设计出最优的表结构,写出高性能的Sqlserver脚本,处理海量数据并发,我们必须解底层原理.由于个人兴趣最近研究了下Sqlserver的文件存储,由于水平有限,下面只讲解Sqlserver的最小存储单元-页. 什么是页,区? 什么会有一个页的概念,我们知道对于操作系统来说,文件可以认为是一个很大 的线性空间,如果按地址空间顺序分配容量(也就是

Sqlserver数据库存储路径的修改

Sqlserver数据库存储路径的修改 Sqlserver数据库存储路径问题:本系统sqlserver路径默认是存储在C盘目录下的,由于数据会慢慢变大和避免重装系统数据丢失等问题,最好手动将路径设置在D盘. 更改路径方法: 情况一:更改数据库默认存储路径 1.打开数据库,登录进去,选择服务器 "属性". 2.选择"数据库设置",如图直接修改数据和日志的存储路径 这样新建数据库默认的存储路径就变为手动设置的路径了. 情况二:对已有的数据库转移到D盘 1.打开数据库,登

.net之工作流工程展示及代码分享(三)数据存储引擎

数据存储引擎是本项目里比较有特色的模块. 第一,使用接口来对应不同的数据库.数据库可以是Oracle.Sqlserver.MogoDB.甚至是XML文件.采用接口进行对应: 1 public interface IWorkflowDB 2 { 3 List<Flow> GetFlows(); 4 bool SaveFlow(Flow flow); 5 bool DeleteFlow(Guid flowId); 6 7 FlowInstance GetFlowInstanceByInstance

04_数据存储

1. 理论概述 Android数据存储方式: SharedPreferences存储 手机内部文件存储 手机外部文件存储 sqlite数据库存储 远程服务器存储 2. 数据存储开发 2.1 SharedPreferences存储 说明 SP存储专门用来存储一些单一的小数据 存储数据的类型:boolean,float,int,long,String 数据保存的路径:/data/data/packageName/shared_prefs/yyy.xml 可以设置数据只能是当前应用读取,而别的应用不可

使用文件进行数据存储四种模式

视频笔记: 1.应用包名:唯一标识一个应用 2.使用文件进行数据存储: (1)Context.MODE_PRIVATE: 默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中,可以使用Context.MODE_APPEND (2)Context_APPEND:只能被应用本身访问:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件. Context_WORLD_READABLE和Context_WORLD_

Android——数据存储(四种方式之一)SharedPrefereces

Android--数据存储(四种方式) 1.SharedPrefereces   轻量级.XML  存储文件名,数据保存在data/data/basepackage/shared_prefs/myopt.xml中   实例-收藏-记住密码自动登录 //一种轻量级的数据存储方式//通过KEY 存入数据--putxxxx(key,value) 取出数据--getxxxx(key  default)   2.读写SD卡  SD的根目录  适用于数据流读写 3.SQLite  轻量级.dp文件多用于手机