Sql Server 2012特性- Online添加非空栏位

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 我们都知道,Sql Server在一个数据量巨大的表中添加一个非空栏位是比较费心的,缺乏经验的DBA或是开发人员甚至可能鲁莽地直接添加导致阻塞相应业务,甚至可能因为资源欠缺造成实例的全局问题.当然这都是Sql 2008R2及以前版本的情况.



我们都知道,Sql Server在一个数据量巨大的表中添加一个非空栏位是比较费心的,缺乏经验的DBA或是开发人员甚至可能鲁莽地直接添加导致阻塞相应业务,甚至可能因为资源欠缺造成实例的全局问题.当然这都是Sql 2008R2及以前版本的情况.在SQL2012中采用了新的实现方式.这里我将对比相应的实现方式给大家做个介绍.并简单说明Sql Server早期版本添加非空列的方法.

添加非空栏位的实现方式

早期版本(Sql Server2008R2及以前)添加非空栏位(要求有默认值)是对表中的所有数据行依次修改调整

我们通过一个简单的实例来看下

Sql 2008R2 SP2 Code


Create database tadnull
go
use tadnull
go
create table t2(id int not null identity (1,1),dystr varchar(20),fixstr char(30));
go
set nocount on 
declare @batchSize int
set @batchSize=1000
declare @i int
set @i=0
while(@i<20000)
begin
  if (@i%@batchSize=0)
    begin
      if (@@TRANCOUNT>0)COMMIT TRAN
      BEGIN TRAN
    end
  insert into t2(dystr,fixstr)values('aaa'+str(RAND()*100000000),'bbb'+str(RAND()*100000000))
  set @i=@i+1
end

 if (@@TRANCOUNT>0)COMMIT TRAN
dbcc ind(tadnull,t2,1) -----find a datapage pageid 21
    
    dbcc traceon(3604)
    dbcc page('tadnull',1,21,3)-----view the datapage 21

通过DBCC PAGE我们打印其中的一个数据页进行分析.

可以看到图1-1当前数据页的最后修改的日志记录为m_lsn = (28:69:279)

数据页中第一行数据(slot 0)页偏移量0x60,行长度为58


                      图1-1

添加非空栏位code

alter table t2 add tt int not null default '10'
dbcc page('tadnull',1,21,3)-----view the datapage 21
select b.* from sys.system_internals_partitions a
    join sys.system_internals_partition_columns b on a.partition_id = b.partition_id
    where a.object_id = object_id('t2');-----查看行数修改情况    

可以看到图1-2添加非空栏位后,字节数据页最后修改的LSN号由m_lsn = (28:69:279)变为了

    m_lsn = (43:432:186),数据页中首行长度也由58变为了62正好是一个int数据类型长度.

当然我们也可以看到添加过程中会有大量的key锁出现.图1-3

(可以通过DMV查看,或者Xevents,trace profiler等探究)

从特定的DMV中,我们看到新加列的修改行数为200000,为表的数据行数,这也反应的数据修改为逐行修改.图1-4


图1-2


图1-3


图1-4


由此我们可以看到,在sql2008R2及以前版本中添加非空栏位会对表中数据逐行操作.操作执行成本很高.

接下来我们再看看sql2012采用的新的实现方式.

Sql 2012 SP1 code


Create database tadnull
go
use tadnull

create table t2(id int not null identity (1,1),dystr varchar(20),fixstr char(30));
go
set nocount on 
declare @batchSize int
set @batchSize=1000
declare @i int
set @i=0
while(@i<200000)
begin
  if (@i%@batchSize=0)
    begin
      if (@@TRANCOUNT>0)COMMIT TRAN
      BEGIN TRAN
    end

  insert into t2(dystr,fixstr)values('aaa'+str(RAND()*100000000),'bbb'+str(RAND()*100000000))
  set @i=@i+1

end
 if (@@TRANCOUNT>0)COMMIT TRAN

     
dbcc ind('tadnull','t2',1)--------find a data page (pageid 120)

dbcc traceon(3604)
dbcc page('tadnull',1,120,3)

通过DBCC PAGE我们打印其中的一个数据页进行分析.

可以看到图2-1当前数据页的最后修改的日志记录为  m_lsn = (33:155:157)

数据页中第一行数据(slot 0)页偏移量0x60,行长度为58


           图2-1

通过图2-2可以看出,页的最后修改的LSN记录m_lsn = (33:155:157)行长度 Length 58与添加栏位前一致!这与sql2008R2截然不同.

通过图2-3可以看出新增栏位的修改行数为0,这反应了添加新行的修改非逐行修改.

由以上两个图我们可以推断出添加新的非空栏位并非在新数据页上逐一操作,而是元数据级的操作.

感兴趣读者可以通过Xevent,Trace Profiler的锁捕捉来进一步验证.


图2-2


图2-3

在添加完后的打印数据页120的内容中我们可以看到图2-4中栏位号4名称dd已经被添加,但其物理长度为0,及行中栏位值未添加到此数据页上


图2-4

问题来了,那么这个栏位就永远不会去填充数据页而只是标识吗?

我们简单修改一下120页中的一条数据然后再打印此页内容

修改CODE


update t2 set dystr='shanksgao1111' where id=1
checkpoint
dbcc page('tadnull',1,120,3)

由于我得屏幕不能完全打印单页中我们所需的内容,此页没截图,直接输出.截取其中需要信息

可以从红色的字体中看到,此页上的LSN变化为m_lsn = (157:336:2),第一行长度(id=1)变为了62(58+4)虽然我只是修改了变长栏位dystr但变长长度和以前一样.但新添加的非空栏位的值也添加到此页上来. Slot 0 Column 4 Offset 0x26 Length 4 Length (physical) 4.

******************************************打印数据页内容******************************************

 

m_freeData = 7640                   m_reservedCnt = 0                   m_lsn = (157:336:2)

 

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

 

m_tornBits = -1896544491            DB Frag ID = 1                     

 

Allocation Status

 

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

 

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

 

ML (1:7) = NOT MIN_LOGGED          

 

Slot 0 Offset 0x1d9a Length 62

 

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

 

Record Size = 62                   

 

Memory Dump @0x000000002F5EBD9A

 

0000000000000000:   30002a00 01000000 62626220 20203838 39373839  0.*.....bbb   889789

 

0000000000000014:   30202020 20202020 20202020 20202020 20200a00  0                 ..

 

0000000000000028:   00000400 0001003e 00736861 6e6b7367 616f3131  .......>.shanksgao11

 

000000000000003C:   3131                                          11    

 

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

 

id = 1                             

 

Slot 0 Column 2 Offset 0x31 Length 13 Length (physical) 13

 

dystr = shanksgao1111               

 

Slot 0 Column 3 Offset 0x8 Length 30 Length (physical) 30

 

fixstr = bbb   8897890                                                  

 

Slot 0 Column 4 Offset 0x26 Length 4 Length (physical) 4

dd = 10                        

******************************************打印数据页内容******************************************

通过上面的实例我们可以看到,在sql2012中,当我们添加一个非空栏位时,实际对相应数据页上的操作并非逐一修改添加.因此执行速度非常快,对线上业务影响很低.但由于其并非实际填充数据页使的Sql Server在应用,维护数据页时进行额外的工作.对此Sql Server采用了较为平滑的处理方式.当更新某(些)行时,再进行页中数据填充,降低了影响粒度.更友好地满足了线上业务的需求.

注:此方式添加新的非空栏位时不包括Blob数据类型.

   添加行版本戳(rowversion/ timestamp)不支持.

早期版本调整方式

我们可以看到sql2012的改进,但早期版本如果维护该如何操作?

早期版本中添加非空栏位针对数据量较小的表还好,但是针对大表由于其添加非空栏位的实现特性,致使阻塞发生的几率明显加大.因此针对大表我们必须谨慎进行.

a 如果有能调整的时间窗口则可以先预估下执行时间(相似环境中测试)然后时间窗口中操作.

b 如果没有可供调整的时间窗口而需求又看似急茬,这样我们可以采用如下步骤循序渐进的调整降低其影响的粒度.

添加非空栏位

1 添加一个带默认值得可为空栏位

alter table t2 add col5 int default 100

2 添加一个约束使其col5不可为空,但不检查现有数据

ALTER TABLE t2 WITH NOCHECK

ADD CONSTRAINT CK_NOTNULL CHECK (col5 IS NOT NULL)

3 轮询批量修改现有数据,更改现有col5为默认值

WHILE 1=1

BEGIN

  BEGIN TRAN

   UPDATE TOP(2000) t2 SET col5 = 100 WHERE col5 IS NULL

    IF @@rowcount < 2000

    BEGIN

      COMMIT TRAN

      BREAK;

    END

  COMMIT TRAN

END-------------限制数据量修改,尽量避免锁升级

 

添加行版本戳

由上面的内容我们已经知道添加行版本戳(rowversion)时无法使用新特性的,那面对大表DBA该如何操作呢?如果有相应时间窗口那是最好,如果没有,DBA需要和研发沟通利用sql server相应知识变向调整.

这里我借鉴与sql 2012 online添加非空栏位时类似的实现方式但需要研发同事配合

大表中添加行版本戳

1 创建一个与原表SCHEMA完全一致的表

2 创建一个视图为原表union all 新表

3 需研发同事配合调整访问实现方式

 a 读访问从视图

b 插入操作插入到新表

c 修改操作原表移动到新表

4 采用批量轮询修改移动

5 移动完毕后调整表名称(原表改随意名,新表调整为原表名,告知研发调整,删除视图与原表)

 

如果研发无法配合调整,如此情况下就比较麻烦了.此时DBA需要一个时间窗口.

为了更迅捷地将相应栏位加上减小阻塞时间我们可以/尽量采取如下一系列的方式来缩短窗口时间.

1 选择实例中相关对象负载最低的时间段进行

2 为执行操作提供尽量多的资源来确保其尽快完成.

  如 调整加大buffer pool的大小

     操作前腾出足够的内存供其使用

Checkpoint

Dbcc dropcleanbuffers-----注意这些操作需保证实例应用在可接受的情况下进行

3 执行的会话中提前获取表级排他锁.(由于添加行版本戳本身也是逐行添加,会话中提前获取表的排他锁可以减小总共获取锁的次数进而加快执行速度) 


 begin tran ttt

    update t2 with(tablockx) set dystr='aaa' where 1=0

    /*调整代码*/

    alter table t2 add rv rowversion

    commit tran ttt
相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
3月前
|
SQL 存储 数据库
SQL学习一:ACID四个特性,CURD基本操作,常用关键字,常用聚合函数,五个约束,综合题
这篇文章是关于SQL基础知识的全面介绍,包括ACID特性、CURD操作、常用关键字、聚合函数、约束以及索引的创建和使用,并通过综合题目来巩固学习。
71 1
|
4月前
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
6月前
|
SQL 存储 监控
SQL Server的并行实施如何优化?
【7月更文挑战第23天】SQL Server的并行实施如何优化?
147 13
|
6月前
|
SQL
解锁 SQL Server 2022的时间序列数据功能
【7月更文挑战第14天】要解锁SQL Server 2022的时间序列数据功能,可使用`generate_series`函数生成整数序列,例如:`SELECT value FROM generate_series(1, 10)。此外,`date_bucket`函数能按指定间隔(如周)对日期时间值分组,这些工具结合窗口函数和其他时间日期函数,能高效处理和分析时间序列数据。更多信息请参考官方文档和技术资料。
|
6月前
|
SQL 存储 网络安全
关系数据库SQLserver 安装 SQL Server
【7月更文挑战第26天】
82 6
|
6月前
|
存储 SQL C++
对比 SQL Server中的VARCHAR(max) 与VARCHAR(n) 数据类型
【7月更文挑战7天】SQL Server 中的 VARCHAR(max) vs VARCHAR(n): - VARCHAR(n) 存储最多 n 个字符(1-8000),适合短文本。 - VARCHAR(max) 可存储约 21 亿个字符,适合大量文本。 - VARCHAR(n) 在处理小数据时性能更好,空间固定。 - VARCHAR(max) 对于大文本更合适,但可能影响性能。 - 选择取决于数据长度预期和业务需求。
505 1
|
6月前
|
SQL Oracle 关系型数据库
MySQL、SQL Server和Oracle数据库安装部署教程
数据库的安装部署教程因不同的数据库管理系统(DBMS)而异,以下将以MySQL、SQL Server和Oracle为例,分别概述其安装部署的基本步骤。请注意,由于软件版本和操作系统的不同,具体步骤可能会有所变化。
465 3
|
5月前
|
SQL 安全 Java
驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client
驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client
562 0
|
6月前
|
SQL 监控 数据库
SQL Server 查询超时问题排查
【7月更文挑战第8天】排查 SQL Server 查询超时涉及五个主要方面:检查复杂查询、评估服务器性能、审视配置参数、更新统计信息和分析执行计划。关注点包括查询的结构(如连接、子查询和索引),服务器资源(CPU、内存、网络延迟),连接和内存设置,以及统计信息的时效性。通过这些步骤可定位并解决性能瓶颈。
144 0
|
6月前
|
SQL 存储 Oracle
TDengine 3.3.2.0 发布:新增 UDT 及 Oracle、SQL Server 数据接入
**TDengine 3.3.2.0 发布摘要** - 开源与企业版均强化性能,提升WebSocket、stmt模式写入与查询效率,解决死锁,增强列显示。 - taos-explorer支持geometry和varbinary类型。 - 企业版引入UDT,允许自定义数据转换。 - 新增Oracle和SQL Server数据接入。 - 数据同步优化,支持压缩,提升元数据同步速度,错误信息细化,支持表名修改。 - 扩展跨平台支持,包括麒麟、Euler、Anolis OS等。
141 0