zoukankan      html  css  js  c++  java
  • 历史数据迁移那些事

          关于历史数据迁移这事,相信很多数据库管理员都经常要面对,因为应对的场景和数据量的不同,数据迁移方法和技术也

    五花八门;在一个数据量不大的表上迁移数据一般都不会有什么问题,一旦表的数据量增大,而且应用对这个表访问非常频繁时,

    那如何有效的迁移数据就会是一件很有讲究的事。

    迁移时先考虑以下几点要求:

    1. 如何设计历史数据保存方法;
    2. 如何将数据搬移到历史表中,如何将原表的数据删除;
    3. 在线系统要求在数据搬移时,对系统影响很小;
    4. Job什么时间运行合适;数据量变大时,搬移时间是否足够;
    5. 数据搬移,采用with(nolock)可能造成重复数据,使得搬移过程出错,如何处理比较好;

    第一:设计历史数据保持方法:

         这个是个比较泛的概念,不同的公司使用的方式都不一样,常见有以下几种:

    1. 简单的历史表方式:

             其实就是在库中新建一个同结构的历史表,如:Product_History,然后将历史数据都放入到这个表中,一般适合数据量不

             大的情况;

             优劣:优点是比较简单,便于维护,查询的时候通过View将两个表Union起来;缺点是如果数据量比较大时,会有很大的

             性能问题(包括查询和插入);

    1. 按日期新建历史表:

            可能是按一个月或者一年新建一个历史表,如:Product_2010、Product_2011、Product_2012…等,然后将不同时期的

            历史数据存放到不同的历史表中;这个方式比较常见,查询的时候通过View将这些表Union All起来。

            优劣:优点是按日期分表后,会将数据量横向切小,单个表的数据量一般不会太大,处理起来性能比较好;缺点是维护比较

            麻烦,尤其是有字段的增删改的时候,所有的历史表都需要跟着修改。

    1. 数据仓库存放:

           这种方式很多有BI部门的公司采用,会将历史数据和当前数据都定期存放到数据仓库中,不仅可以给BI提供制作报表的数据,

           前台用户查询历史数据,APP程序就可以直接定位到仓库去查询,不影响产品环境。

           优劣:优点很明显,不仅解决了历史数据查询对产品环境的影响和BI报表数据来源的问题,而且减轻了生产库保存这些历史数

           据的负担;缺点是需要有BI部门,也就意味着需要相关的软、硬和人员的投入。

    第二: 数据插入和删除:

         数据插入这个还是比较好解决的,我们可以直接用insert插入,也可以用BCP,Bulk Insert、SSIS等方式;数据删除在数据量

    比较大的时候比较棘手,主要是要权衡系统影响和速度的问题,如果每次删除的数据量比较大,势必会对系统使用者造成影响,如果

    每次删除的数据量比较小,可能需要比较长的时间才能完成(分区表有直接删掉某个分区的操作,这样可以不用删除数据,直接删掉

    分区就行,在此不做说明,可以参考相关文档);

    第三: 系统影响的考虑:

         这个和第二点相关,主要是处在系统删除的时候,需要考虑到锁的情况,避免数据库出现大批量的阻塞。

    第四: Job运行时间和预计的搬移时间控制:

         JOB运行时间毫无疑问是要放到系统业务的低峰时期(一般都是晚上),不过前期我们应该知道我们的系统在什么时间段内业务

    比较少,这段时间里面就适合做数据的搬移操作;控制搬移时间,这个要求我们对数据搬移进行控制,如果达到某个时间点(预计业

    务开发繁忙了),数据还没有搬移完成,那么应该中断搬移过程,以免影响到业务的正常进行;如果出现这种数据不能按时搬移完成

    的情况,意味着需要调整搬移的过程,或者使用更高效的方法,防止历史数据越积越多。

    第五:主键冲突问题:

         基于对系统的影响考虑,我们一般在查询数据时都采用了With(nolock) 的方式,而数据库在默认的Read Commited隔离级

    别下,加上With(nolock) 效果和Read UnCommited一样,这样在搬移数据时,可能会造成将还没有提交的数据搬移到历史表的情

    况,在下次运行时,便产生了主键冲突的问题;有以下几种方法能避免此种情况:

    1. 搬移时不加With(nolock),考虑到对系统的影响,一般不采用;
    2. 历史表去掉主键,但是我们的历史数据是需要查询的,这样会造成很大的性能问题;
    3. 插入到历史表时先做判断,只插入历史表中不存在的数据,这个方法可行,但是如果历史数据表很大,判断的时间将会很长,不是最优的方法;
    4. 利用索引创建时的参数设置ignore_dup_key,将其设置为ON,这样就可以将重复的记录直接过滤掉,这种方法比较理想(后面例子有说明)。

    我们以下面的两个表为例,对一些常用的数据搬移方法(将相关的方法建到JOB或者SP里面调用就行了),进行说明:

    --Master table
    create table Product
    (
    Id int identity(1,1) primary key,
    Name varchar(100),
    Product_Desc varchar(1000),
    Price decimal(12,2),
    CreateTime datetime,
    UpdateTime datetime,
    Statu tinyint
    )
    --index
    create index ix_createtime on Product(CreateTime) with(fillfactor=90,online=on)

    --History
    create table Product_History
    (
    Id int primary key, --没有自增属性
    Name varchar(100),
    Product_Desc varchar(1000),
    Price decimal(12,2),
    CreateTime datetime,
    UpdateTime datetime,
    Status tinyint
    )
    --index
    create index ix_createtime on Product_History (CreateTime) with(fillfactor=90,online=on)

    No1. 简单的数据搬移:

    简单的历史数据迁移方法,我们直接新建一个同结构的表,命名为表名加History,然后按照条件,每天搬移一部分数据到历史表中即可;

    例如:我们将下面Product表的数据搬移到Product_History历史表中:

    --搬移条件
    --
    10天前,Status=0的Product
    insert into Product_History
    select * from Product with(nolock)
    where CreateTime<convert(varchar,DATEADD(day,-10,GETDATE()),112) and Status=0

    --删除条件
    delete from Product
    where CreateTime<convert(varchar,DATEADD(day,-10,GETDATE()),112) and Status=0

    No2. 方法一的改进:

    对于数据量不大的表,我们为节省时间,可以直接把搬移和删除两个步骤放到一起来进行,方法如下:

    --合并搬移和删除条件
    delete a
    output deleted.* into Product_History
    from Product a
    where CreateTime<convert(varchar,DATEADD(day,-10,GETDATE()),112) and Status=0

    No3. 分步搬移法(减轻对系统影响):

    方法一和二在数据量比较小时是没有问题的,搬移过程能很快完成,但是一旦数据量变大,数据搬移可能需要一段时间才能完成的情况

    下,如果再采用这种方式,势必对访问这个表的APP造成大量阻塞,这个是在线系统不能接受的,于是我们不得不把系统影响考虑进来,

    把大的数据量分批次来搬移,方法如下:

    --------------------------分步搬移---------------------------------
    while(1=1)
    begin
    declare @productid table
    (
    id int primary key
    )

    insert into @productid
    select top 1000 id from Product with(nolock)
    where CreateTime<convert(varchar,DATEADD(day,-10,GETDATE()),112) and Status=0

    if exists(select 1 from @productid )
    begin
    --搬移条件
    insert into Product_History
    select * from Product with(nolock)
    where id in(select id from @productid)

    --删除条件
    delete from Product
    where id in(select id from @productid)

    waitfor delay '00:00:02'
    end
    else
    break;

    end
    --------------------------------------------------------------------

    ---------------------------分步搬移,合并搬移和删除条件------------------
    --
    分步搬移,合并搬移和删除条件
    while(1=1)
    begin

    delete top (1000) a
    output deleted.* into Product_History
    from Product a
    where CreateTime<convert(varchar,DATEADD(day,-10,GETDATE()),112) and Status=0

    if @@ROWCOUNT=0
    break
    else
    waitfor delay '00:00:02' --delay 2s

    end
    ---------------------------------------------------------------------

    No4. 控制搬移时间:

      我们在第三步的基础上做修改,为了防止搬移作业在业务高峰时间还在运行,阻塞业务操作,我们需要在搬移进行到某个时间点时,

    中断搬移作业的运行,方法如下:

    ---------------------------7点后终止运行------------------
    while(1=1)
    begin

    delete top (1000) a
    output deleted.* into Product_History
    from Product a
    where CreateTime<convert(varchar,DATEADD(day,-10,GETDATE()),112) and Status=0

    if @@ROWCOUNT=0 or datepart(HOUR, GETDATE())>=7 --7点后终止运行
    break
    else
    waitfor delay '00:00:02' --delay 2s

    end
    ---------------------------------------------------------------------

    No5. 了解搬移的进度:

       有时候我们看到搬移JOB一直在运行,但是没有办法知道它要运行到什么时候才能运行完,只能干等,于是我们有对某些搬移作业进

    度情况作了解的需求,我们再来更改脚本:

    --先创建一个搬移记录表
    create table temp_history_2011_12_01
    ( prd_id int primary key,
    is_del bit --是否搬移完成
    )

    --------------------------了解搬移进度---------------------------------
    truncate table temp_history_2011_12_01

    --所有需要搬移的数据插入到表中
    insert into temp_history_2011_12_01
    select id,0 from Product with(nolock)
    where CreateTime<convert(varchar,DATEADD(day,-10,GETDATE()),112) and Status=0

    while(1=1)
    begin
    declare @productid table
    (
    id int primary key
    )
    --1000
    insert into @productid
    select top 1000 prd_id from temp_history_2011_12_01 where is_del=0

    if exists(select 1 from @productid )
    begin

    --搬移条件
    insert into Product_History
    select * from Product with(nolock)
    where id in(select id from @productid)

    --删除条件
    delete from Product
    where id in(select id from @productid)

    --update is_del
    update temp_history_2011_12_01 set is_del=1
    where prd_id in(select id from @productid)

    waitfor delay '00:00:02'
    end
    else
    break;

    end
    truncate table temp_history_2011_12_01
    ---------------------------------------------------------------------

    --------------假定已运行时间是5小时,估计剩余时间-------------
    declare @fin_count int,@need_count int
    select @fin_count=COUNT(0) from temp_history_2011_12_01 with(nolock) where is_del=1
    select @need_count=COUNT(0) from temp_history_2011_12_01 with(nolock) where is_del=0

    select @need_count*1.0/@fin_count*5
    ---------------------------------------------------------------------

    No6. 避免重复键冲突:

     我们先来看下ignore_dup_key作用的描述:使涉及多行的insert语句在有重复键值的错误时不会引起整个语句的回滚,那个导致不唯一的行会被舍弃, 而其他的行都将被插入或者更新,同样可以在创建主键约束时,使用该选项。

    于是,我们可以在历史表创建时,在主键上使用这个选项,例如:

    CREATE TABLE [dbo].[Product_History](
    [Id] [int] NOT NULL,
    [Name] [varchar](100) NULL,
    [Product_Desc] [varchar](1000) NULL,
    [Price] [decimal](12, 2) NULL,
    [CreateTime] [datetime] NULL,
    [UpdateTime] [datetime] NULL,
    [Status] [tinyint] NULL,
    PRIMARY KEY CLUSTERED
    (
    [Id] ASC
    )WITH (IGNORE_DUP_KEY = ON) ON [PRIMARY]
    ) ON [PRIMARY]

    这样就可以避免因主键冲突导致数据搬移JOB的失败了。

     

        以上是就SQL层面的历史数据搬移的一些方法,还有BCP、Bulk Insert、SSIS等方法可以使用,在此不做说明;以上方法加以

    调整,便可以做成比较通用的数据搬移方法,包括控制搬移记录数、控制搬移时间、了解搬移进度等问题都可以得到灵活控制,让数

    据搬移顺起来。

  • 相关阅读:
    HAproxy 1.5 dev14 发布
    IBM/DW 使用 Java 测试网络连通性的几种方法
    Skype 4.1 Linux 发布,支持微软帐号登录
    Dorado 7.1.20 发布,Ajax的Web开发平台
    Aspose.Slides for Java 3.0 发布
    开发版本 Wine 1.5.18 发布
    BitNami Rubystack 开始支持 Ruby 2.0
    XWiki 4.3 正式版发布
    Silverlight实例教程 Out of Browser的Debug和Notifications窗口
    Silverlight实例教程 Out of Browser与Office的互操作
  • 原文地址:https://www.cnblogs.com/fygh/p/2278844.html
Copyright © 2011-2022 走看看