Select

在不中斷表讀取的情況下重新計算和替換大表

  • October 13, 2021

數據庫中有一個包含統計資訊的大表。必須經常讀取此表(通過返回單行的查找)。

該表必須每隔一段時間重新計算一次。有一個程序可以進行計算。目前它在臨時表中執行此操作,然後在完成後截斷真實表並將臨時表中的數據插入其中。

問題是這種截斷和插入需要一段時間並在此期間中斷查找。問題是如何在不中斷查找或僅非常短暫地中斷查找的情況下做到這一點。

我很高興聽到所有實現所需行為的選項;該解決方案不一定必須涉及臨時表。抱歉,如果這是一個既定的常見問題 - 我不確定要搜尋哪些術語。

兩個選項,取決於引擎和/或您擁有的權限 - 填充第二個表,然後重命名該表(例如sp_rename 'thetable', 'deadtable'; sp_rename 'newtable', 'thetable'; drop table deadtable;),或填充第二個表,然後讓您的應用程序選擇使用該表。

使用分區切換可以在表之間快速移動大量數據。即使您沒有明確定義分區,這也可以。從文件中,每個表都有一個預設分區

如果堆或索引未分區,則它由一個分區組成

這需要三個表: Working,其中包含應用程序目前正在使用的值。舊的,目前值將切換到空工作,無需 TRUNCATE。和 New 將在切換之前計算新的​​值集。

當然,真正的解決方案不需要使用這些確切的名稱。我選擇它們只是為了突出它們在實施中所扮演的角色。

首先是一些帶有一些數據的虛擬表來顯示目前狀態。

drop table if exists Old;
drop table if exists Working;
drop table if exists New;

create table Old(c int);
create table Working(c int);
create table New(c int);

Insert Working(c) values (1), (2), (3);

select 'Old', * from Old;
select 'Working', * from Working;
select 'New', * from New;

舊的和新的都是空的。工作包含應用程序在正常操作期間消耗的值。接下來我們重新計算成 New

Insert New(c) values (11), (22), (33);

為了移動數據,我們使用了兩個 SWITCH 語句。第一個將所有行從 Working 移動到 Old,使 Working 為空。第二個將所有行從 New 移動到 Working,將 New 留空。

begin transaction
  alter table Working switch to Old;
  alter table New switch to Working;
commit

在執行 SWITCH 之前,SWITCH 的目標(數據要移動到的表)必須為空。第一個 SWITCH 清空 Working ready 以在第二個 SWITCH 中接收新數據。擁有事務可確保在語句之間保持鎖定,從而防止應用程序看到空表。

擁有 Old 並不是絕對必要的。但是,如果沒有它,Working 將不得不在交換週期中被截斷,正如您所指出的那樣,這很慢。使用 Old,可以在關鍵路徑之外處理舊數據,而不會影響應用程序。不過,它確實必須處理。到下一個執行週期 Old 必須為空,否則上述操作將失敗。

當切換發生時,讀取操作無法針對工作進行。應用程序被阻止。延遲會出現峰值。但是,SWITCH 通過更改目錄中的元數據來工作,而不是通過實際在磁碟上移動行。切換應該非常快,並且延遲峰值是短暫的。

引用自:https://dba.stackexchange.com/questions/229945