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等方法可以使用,在此不做说明;以上方法加以

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

    据搬移顺起来。

  • 相关阅读:
    显示等待WebDriverWait
    MySQL添加注释
    linux
    linux时区问题
    CentOS禁用笔记本touchpad
    Mysql事务隔离级别
    IDEA集成有道翻译插件/maven帮助插件/mybatis插件
    SVN服务器的搭建和使用
    IntelliJ IDEA工具的安装使用
    IntelliJ IDEA的使用操作链接
  • 原文地址:https://www.cnblogs.com/fygh/p/2278844.html
Copyright © 2011-2022 走看看