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

事件起因:开发发现有表插入数据失败,查看后发现INT类型自增值已经到了最大值,无法继续插入,需要修改INT类型为BIGINT类型。

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

作为一群自认为还算有点经验的老DBA,大家相互商量下,决定删除复制,然后禁止访问,删除索引,再使用ALTER TABLE修改,最后再加回索引,再修改访问,再不初始化订阅,多完美的步骤。

于是在一群人嘻哈围观中,执行了上述步骤,运行修改脚本:

ALTER TABLE TableName
ALTER COLUMN ID BIGINT

然后大家开始预估执行时间,有预估十几分钟的,有预估半小时的,有预估两小时的,然后开始坐等。。。

半小时过去,没有完成,有人开始坐不住,一个小时过去,没有完成,大家开始着急,两个小时过去,还是没有完成,老板坐不住啦,杀过来质问。。。

这下玩大啦。。。

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

由于在多台机器上执行,一些取消执行,一些硬抗等着执行成功,无论是取消执行还是继续执行,都不知道啥时能回滚成功或者执行成功,大家一边在惶恐中祈祷早点执行完成,一边还得给老大们解释各种原因。

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

表行数:11亿+

表数据:60GB+

表情况:1个BIGINT列+3个INT列+1个VARCHAR(50)列(VARCHAR(50)列平均每行长度11Byte)

修改操作:将字段类型INT改为BIGINT

对于继续执行等待ALTER TABLE成功的:耗时9.5小时完成

对于执行四小时然后取消执行的:耗时超过10小时未完成

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

扯了这么多水货,咱们来点干货,为什么修改字段类型消耗这么长时间呢?

首先排查是否由硬件资源瓶颈导致,硬件配置为:

CPU: 48CORE

内存:128GB

IO: FIO卡或高级共享存储

CPU使用未超过10%,磁盘每秒日志写入8MB左右,内存未发现等待分配情况

那问题出在那呢?

问题还是出在日志上,可以轻松发现,无论是在FIO卡还是共享存储上,磁盘每秒日志写入8MB左右,这个数值太低,经过肖磊咨询MS工程师了解到,修改数据类型,需要修改每一行的数据,每行数据生成一条日志记录,从而导致大量的日志,而由于未知原因,每秒写入磁盘的日志量较低,因此导致整个修改操作维持很长时间。

让我们来做个测试:

--=======================
--使用测试数据库DB2
use db2
GO
--================
--创建测试表
CREATE TABLE Test001
(
C1 INT IDENTITY(1,1),
C2 BIGINT
)
GO
--================
--往测试表中导入2000+行数据
INSERT INTO Test001(C2)
SELECT OBJECT_ID FROM sys.all_objects
GO
--================
--备份日志以截断日志
BACKUP LOG [db2] TO DISK='NUL'
GO
--================
--查看当前日志情况
--由于日志截断,当前日志记录数很少
SELECT * FROM sys.fn_dblog(NULL,NULL)
ORDER BY [Current LSN] DESC GO
--=====================
--修改字段类型
ALTER TABLE Test001
ALTER COLUMN C1 BIGINT
GO
--================
--再次查看当前日志情况
--发现增加4000+的日志记录数
SELECT * FROM sys.fn_dblog(NULL,NULL)
ORDER BY [Current LSN] DESC


由于我们操作的数据有11亿+条,整个ALTER TABLE 操作是一个完整的事务操作,因此是一个极其庞大的事务,再加上其他诸如复制日志读取等操作影响,导致整个修改耗时9.5个小时,产生书300GB+的日志,对业务造成比较严重的影响。

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

推荐解决办法:

创建新表,将数据导入到新表中,整个导入过程不影响业务访问(在修改前业务已无法插入数据,没有写操作,但是还有读请求),数据导入完成后,建立对于索引,然后使用SP_RENAME修改表名。这样可以将业务影响降低到最低,并且导入数据可以并发进行,提高导入效率,减小整个操作周期。

使用此推荐方法,用SSMS自带的导入导入工具,约4个小时完成导入。

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

个人猜想:

整个修改数据类型操作,是单线程操作,并且可能受单个log buffer每秒能产生的日志记录数限制,因此每秒写入的日志量较小,修改的行数比较少,导致整个修改操作持续大量时间。

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

夜深了,妹子提神

曲演杂坛--使用ALTER TABLE修改字段类型的吐血教训的更多相关文章

  1. 曲演杂坛--一条DELETE引发的思考

    原文:曲演杂坛--一条DELETE引发的思考 场景介绍: 我们有一张表,专门用来生成自增ID供业务使用,表结构如下: CREATE TABLE TB001 ( ID ,) PRIMARY KEY, D ...

  2. 曲演杂坛--使用CTE时踩的小坑:No Join Predicate

    在一次系统优化中,意外发现一个比较“坑”的SQL,拿出来供大家分享. 生成演示数据: --====================================== --检查测试表是否存在 IF(O ...

  3. 曲演杂坛--重建索引后,还使用混合分区么?(Are mixed pages removed by an index rebuild?)

    原文来自:http://www.sqlskills.com/blogs/paul/mixed-pages-removed-index-rebuild/ 在SQL SERVER 中,区是管理空间的基本单 ...

  4. 曲演杂坛--特殊字符/生僻字与varchar

    对于中文版的SQL SERVER,默认安装后使用的默认排序规则为Chinese_PRC_CI_AS,在此排序规则下,使用varchar类型来可以“正常存取”存放中文字符以及一些东南亚国家的字符,同时v ...

  5. 曲演杂坛--为什么SELECT语句会被其他SELECT阻塞?

    很多刚入门的DBA在捕获阻塞得时候,会问这么一个问题“为什么这个SELECT语句被那个SELECT语句阻塞了,难道不是共享锁么?” 让我们来做个小测试,首先准备一些测试数据: --========== ...

  6. 曲演杂坛--EXISTS语句

    通常在我写EXISTS语句时,我会写成IF EXISTS(SELECT TOP(1) 1 FROM XXX),也没细细考究过为什么要这么写,只是隐约认为这样写没有啥问题,那今天就深究下吧! 首先准备测 ...

  7. 曲演杂坛--Update的小测试

    今天偶然想起一个UPDATE相关的小问题,正常情况下,如果我们将UPDATE改写成与之对应的SELECT语句,其SELECT查询结果应与UPDATE的目标表存在一对一的关系,例如: 对于UPDATE语 ...

  8. 曲演杂坛--蛋疼的ROW_NUMBER函数

    使用ROW_NUMBER来分页几乎是家喻户晓的东东了,而且这东西简单易用,简直就是程序员居家必备之杀器,然而ROW_NUMBER也不是一招吃遍天下鲜的无敌BUG般存在,最近就遇到几个小问题,拿出来供大 ...

  9. 曲演杂坛--当ROW_NUMBER遇到TOP

    值班期间研发同事打来电话,说应用有超时,上服务器上检查发现有SQL大批量地执行,该SQL消耗IO资源较多,导致服务器存在IO瓶颈,细看SQL,发现自己都被整蒙了,不知道这SQL是要干啥,处理完问题赶紧 ...

随机推荐

  1. 关于Application.DoEvents()==转

    记得第一次使用Application.DoEvents()是为了在加载大量数据时能够有一个数据加载的提示,不至于系统出现假死的现象,当时也没有深入的去研究他的原理是怎样的,结果在很多地方都用上了App ...

  2. 两个onCreate方法?你真的了解onCreate()么?

    Activity的onCreate方法一直是我们编写一个activity最先重载的方法.细心的小伙伴在编写代码的时候回看到这样一幕: 咦,这里怎么会有两个onCreate提供给我们重载?选择困难症患者 ...

  3. 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:Weixin' did not find a matching property.

    警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclips ...

  4. linux分区之gpt(大于2T的分区)

    1.文件系统限制: ext3块尺寸 最大文件尺寸 最大文件系统尺寸1KiB  16GiB  2TiB2KiB  256GiB  8TiB4KiB  2TiB  16TiB8KiB  16TiB  32 ...

  5. GridView控件中的一些常见问题

    1. 无法获取模板列中的值,使用FindControl()方法无效: 给模板列中添加隐藏域,并给隐藏域绑定要获取的值,代码如下: <asp:HiddenField ID="hfIsFr ...

  6. swift - VFL

    1.VFL语法总结: //(1)“H”表示水平方向,“V”表示垂直方向 //(2)“|”表示父视图的边界 //(3)“[]”表示这是一个视图UIView的子类,可以组合多个条件,条件用"() ...

  7. swift中UITextView的使用

    https://blog.csdn.net/potato512/article/details/52692604

  8. 在控制台远程连接mysql数据库时,出现ERROR 2049 (HY000)错误

    问题的原因是,你本地的数据库版本过高,而远程的数据库版本低. 解决方法:在连接时加上  --skip-secure-auth 参数就可以了. mysql -h主机 -u用户名 -p密码 --skip- ...

  9. Photoshop和Halcon中的极坐标变换

    极坐标想必学过高中数学的人都听过,一般的坐标系中用(x, y)值来描述一个点的位置,而在极坐标系中,则使用到原点的距离ρ和夹角θ来描述该点的位置. 我很早就接触了Photoshop,知道Photosh ...

  10. MVC FormCollection 无法获取值的问题

     把action定义为[HttpPost],并且ajax.beginform中ajaxoption中定义为Post,在提交表单时就可以获取FormCollection的值了.httpGet或者后台不定 ...