web-dev-qa-db-ja.com

データを失うことなく、テーブルから別のテーブルにデータを移動する

現在数百万のレコードを保持しているログテーブルがあります。そのテーブルでパーティション分割を有効にしたいので、ここで私が行ったことは次のとおりです。

  1. パーティション関数とパーティション構成を作成しました。
  2. そのパーティション構成で同じ構造を持つ空のテーブルを作成しました。
  3. この時点から現在のログテーブルからコピーしたデータ(T1と呼びましょう)は、新しいパーティションテーブルに逆方向に移動します。

次の手順では、最後の残りのレコードをT1からTnowにコピーし、2つのテーブルの名前を変更して、アプリケーションが新しいパーティション分割テーブルへの書き込みを開始するようにします。

もちろん、ログテーブルは頻繁にアクセスされるので、私の質問は次のとおりです。

このプロセス中にデータが失われないようにするにはどうすればよいですか?ユーザーが何も気付かないようにすることはできますか?または、この短い時間の間、アプリケーションを停止する必要がありますか?または、アプリケーションを実行し続けるために、そのテーブルを単にブロックできますか?もしそうなら、どうですか?

4
Mattia Nocerino

必要なパーティショニング戦略で新しいテーブルを作成することを検討し、すべてを結合する両方のテーブルの上にビューを追加します。人々にビューを使用させ、基になるテーブルとビューに対してトリガーの代わりにトリガーを記述してもらいます。

挿入は新しいテーブルに送信され、更新はデータを新しいテーブルに移動し、削除は両方のテーブルに適用される必要があります。

次に、バックグラウンドでバッチ移動を行い、一度に多くのレコードを新しいテーブルに移動します。これが行われている間も並行性の問題が発生する可能性があり、いくつかの恐ろしい実行計画がありますが、移動が行われている間もオンラインのままにしておくことができます。

理想的には、エンドユーザーへの影響を最小限に抑えるために金曜日の午後にプロセスを開始し、月曜日の朝までに完了するようにしてください。準備が整ったら、ビューを変更して新しいテーブルのみを指すようにすると、恐ろしい実行プランはなくなります。理想的には。

データがバッチで移行されているときにトリガーが起動しないようにするには、トリガーで削除または挿入されたテーブルの行数を確認し、バッチの行数に近い場合はアクティビティをスキップします。

これをエンドユーザーにわかりやすくするほど、より多くの作業(およびテスト)が必要になります。これは、パーティショニングを使用している場合に特に当てはまります。多くの場合、すべてのクエリが高速になると信じられていますが、一部のクエリは非常に遅くなります。可能な場合は、パーティション分割テーブルを備えた開発サーバーで、できるだけ多くのワークロードをテストしてください。

9
Brent Ozar

あなたが話したパーティショニングを行うために、より小さなロギングテーブルとより大きなアーカイブテーブルを検討したいかもしれません:

私が勤務する病院では、約15年前に同様の問題が発生しました。監査ログがそれらのレポートによってロックされている間、人々はレポートのために監査ログにアクセスしようとしました。したがって、レポート作成のためにログを別のデータベースにチャンクすることにより、レポートをログから分離しました。サービスのギャップを回避するために、タイマーを設定して、データの一部を新しいテーブルに一度に分割します。あなたのケースでは、新しいテーブルにデータを取得したら、好きなように分割できます。

この戦略を使用すると、元のロギングテーブルは常に小さくなり、アーカイブテーブルは大きくなり、パーティション化したいものになります。私たちにとって、現在のログを履歴情報から分離することは非常に大きなメリットでした。私はあなたがあなた自身のためにその利益を比較検討する必要があると思います。しかし、少なくとも、データをアーカイブデータベース/テーブルにエクスポートするときに、進行中のロギングに影響を与えることなく、元のテーブルに書き込むことができます。

あなたの場合、履歴レポートを行うユーザーは、移動する新しいテーブルを持っているために気付く場合があります。ただし、ロギングは妨げられずに元のテーブルで続行されます。

ここにコードがあります、私の謝罪、それは少し無愛想です-ここで、newauditはログデータベースで、auditはアーカイブデータベースです。 (もちろん、本番稼働前に開発環境でこれをテストする必要があります):

Create proc [dbo].[spExportAuditLog]
as

    set nocount on
    declare @errorvar int,@max int, @min int, @watermark int, @batch int, @beforecounter int, @aftercounter int, @AuditCounter int, @NewAuditCounter int, @errortext varchar(255)
    select @beforecounter=0
    select @aftercounter=0
    select @NewAuditCounter = count(*) from newaudit.dbo.auditlog
    print 'NewAudit AuditLog Count Before'
    print @NewAuditCounter
    print 'Audit AuditLog Count Before'
    select @AuditCounter=count(*) from [TARGETSQLSERVER].audit.dbo.auditlog
    print @AuditCounter

    select top 1 @max=ID from newaudit.dbo.auditlog where  datediff( minute, datestamp,getdate()) <= 1440 order by ID

    select @min=min(ID) from newaudit.dbo.auditlog
    Set XACT_ABORT on
    select @batch =100000
    select @watermark=@min
    while (@watermark <=@max)
    begin
            begin transaction
            insert into [TARGETSQLSERVER].audit.dbo.auditlog(ApplicationName, MessageType, Message, Comments, UserID, CustomFields, Datestamp, ServerName, MRN)
            select ApplicationName, MessageType, Message, Comments, UserID, CustomFields, Datestamp, ServerName, MRN
                         from newaudit.dbo.auditlog where ID between @watermark and @watermark+@batch and ID <= @max order by id
                         if @@ERROR <> 0 select @errorvar=@errorvar+1
            delete from newaudit.dbo.auditlog where ID between @watermark and @watermark+@batch and ID <= @max
            if @@ERROR <> 0 select @errorvar=@errorvar+1
            if @errorvar > 0 
            begin
                select @errortext= 'ROLLBACK!! Problem with error IDs between:' + CONVERT(varchar(50), @watermark) + ' and ' +CONVERT(varchar(50), @watermark+@batch)
                print @errorText
                rollback 
                select @errorvar=0
            end
            else commit
            select @watermark = @watermark+@batch +1
            waitfor delay '000:00:10'
    end

    select @NewAuditCounter = count(*) from newaudit.dbo.auditlog
    print 'NewAudit AuditLog  Count After'
    print @NewAuditCounter
    print 'Audit AuditLog Count After'
    select @AuditCounter=count(*) from [TARGETSQLSERVER].audit.dbo.auditlog
    print @AuditCounter
1
Sting