資料維護
OSS Tables提供自動維護功能,自動執行壓縮、快照管理和未引用檔案清理三種最佳化任務。這些功能持續最佳化Table的查詢效能並降低儲存成本,無需配置外部計算資源。
自動維護功能包含三種最佳化任務,分別作用於不同的資源層級:
未引用檔案清理:Bucket層級配置。自動識別並刪除不再被任何錶快照引用的對象。
壓縮:表層級配置。將小檔案合并為大檔案,提升查詢效能。
快照管理:表層級配置。自動清理到期快照,減少中繼資料檔案數量和儲存開銷。
建立Table Bucket和Table後,系統自動應用以下預設維護配置。
維護功能 | 配置層級 | 參數 | 預設值 |
未引用檔案清理 | Bucket層級 | status | enabled |
unreferencedDays | 3 | ||
nonCurrentDays | 10 | ||
壓縮 | 表層級 | status | enabled |
targetFileSizeMB | 512 | ||
strategy | auto | ||
快照管理 | 表層級 | status | enabled |
maxSnapshotAgeHours | 120 | ||
minSnapshotsToKeep | 1 |
服務關聯角色
OSS Tables的自動維護功能(壓縮、未引用檔案清理、快照管理)由後台服務執行,該服務需要通過服務關聯角色AliyunServiceRoleForOssTableMaintenance擷取Table Bucket的存取權限。更多關於服務關聯角色的資訊,請參見服務關聯角色。
授權方式
首次訪問OSS控制台的Table Bucket 列表頁面時,如果當前帳號尚未建立該服務關聯角色,頁面頂部會顯示提示:缺少許可權:AliyunServiceRoleForOssTableMaintenance,單擊提示中的立即建立即可完成授權。授權完成後,OSS Tables後台維護服務將自動開始工作。
如果使用RAM使用者操作,需確保該使用者具備建立服務關聯角色的許可權。可由主帳號為RAM使用者添加以下權限原則:
{
"Version": "1",
"Statement": [
{
"Action": "ram:CreateServiceLinkedRole",
"Resource": "*",
"Effect": "Allow",
"Condition": {
"StringEquals": {
"ram:ServiceName": "tablemaintenance.oss.aliyuncs.com"
}
}
}
]
}許可權說明
該服務關聯角色的權限原則(AliyunServiceRolePolicyForOssTableMaintenance)包含以下許可權,用於讀取表配置、執行資料維護和更新中繼資料:
許可權(Action) | 用途 |
oss:GetTableBucketMaintenanceConfiguration | 讀取Bucket層級維護配置 |
oss:GetTableMaintenanceConfiguration | 讀取Table層級維護配置 |
oss:ListTableBuckets | 列舉Table Bucket |
oss:GetTableBucket | 擷取Table Bucket資訊 |
oss:ListTables | 列舉Table |
oss:GetTable | 擷取Table資訊 |
oss:GetTableData | 讀取表資料檔案(執行壓縮和清理) |
oss:PutTableData | 寫入表資料檔案(壓縮後產生新檔案) |
oss:GetTableMetadataLocation | 擷取中繼資料檔案位置 |
oss:UpdateTableMetadataLocation | 更新中繼資料檔案位置(維護完成後提交變更) |
未引用檔案清理
未引用檔案清理是Bucket層級的最佳化操作,作用於Table Bucket下的所有Table。未引用檔案清理功能會識別並刪除所有未被任何錶快照引用的對象。在資料寫入、更新或刪除過程中,可能會因為寫入作業失敗、快照到期等原因產生不再被任何中繼資料引用的資料檔案(如Parquet、Avro、ORC檔案)。這些孤立檔案持續佔用儲存空間。
開啟未引用檔案清理後,如果向Table Bucket中PUT了任何不屬於表的Parquet、Avro、ORC檔案,這些檔案也將被視為未引用檔案並被自動清理。
未引用檔案清理採用兩階段處理機制:首先將未被引用的檔案標記為non-current狀態(由unreferencedDays控制),然後在檔案處於non-current狀態指定天數後執行刪除(由nonCurrentDays控制)。
未引用檔案清理的配置參數如下表所示。
參數 | 說明 | 取值範圍 | 預設值 |
status | 是否啟用未引用檔案清理。 | enabled或disabled。 | enabled |
unreferencedDays | 檔案不被任何中繼資料引用後,標記為non-current狀態的等待天數。 | 1~2147483647。 | 3 |
nonCurrentDays | 檔案被標記為non-current狀態後,執行刪除操作的等待天數。 | 1~2147483647。 | 10 |
控制台
通過OSS管理主控台,直接查看和修改Table Bucket的自動維護配置。
操作步驟如下:
登入OSS管理主控台,在左側導覽列選擇Table Bucket 列表。
單擊目標Table Bucket名稱,進入Bucket詳情頁。
選擇資料維護 Tab頁。
在未引用檔案清理地區,查看當前配置狀態:
狀態:顯示該功能是否已啟用。
未引用天數:檔案不被任何中繼資料引用後,標記為non-current狀態的等待天數。預設值為3天。
非當前天數:檔案被標記為non-current狀態後,執行刪除操作的等待天數。預設值為10天。
單擊編輯按鈕,修改未引用檔案清理的配置參數,完成後單擊儲存。
ossutil
通過ossutil修改未引用檔案清理配置:
ossutil tables-api put-table-bucket-maintenance-configuration \
--table-bucket-arn {ARN} \
--type icebergUnreferencedFileRemoval \
--value '{"status":"enabled","settings":{"icebergUnreferencedFileRemoval":{"unreferencedDays":5,"nonCurrentDays":15}}}'SDK
以下代碼展示了如何通過SDK查詢和修改Table Bucket的未引用檔案清理配置。
Python
查詢未引用檔案清理配置:
import argparse
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.tables as oss_tables
parser = argparse.ArgumentParser(description="get table bucket maintenance configuration sample")
parser.add_argument('--region', help='The region in which the table bucket is located.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS Tables.')
parser.add_argument('--table-bucket-arn', help='The ARN of the table bucket.', required=True)
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss_tables.Client(cfg)
result = client.get_table_bucket_maintenance_configuration(
oss_tables.models.GetTableBucketMaintenanceConfigurationRequest(
table_bucket_arn=args.table_bucket_arn,
)
)
print(f'status code: {result.status_code},'
f' request id: {result.request_id},'
f' table bucket arn: {result.table_bucket_arn},'
f' configuration: {result.configuration}')
if __name__ == "__main__":
main()修改未引用檔案清理配置:
import argparse
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.tables as oss_tables
parser = argparse.ArgumentParser(description="put table bucket maintenance configuration sample")
parser.add_argument('--region', help='The region in which the table bucket is located.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS Tables.')
parser.add_argument('--table-bucket-arn', help='The ARN of the table bucket.', required=True)
parser.add_argument('--type', help='The maintenance type, e.g., icebergUnreferencedFileRemoval.', required=True)
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss_tables.Client(cfg)
value = {
'status': 'enabled',
'settings': {
'icebergUnreferencedFileRemoval': {
'unreferencedDays': 7,
'nonCurrentDays': 30
}
}
}
result = client.put_table_bucket_maintenance_configuration(
oss_tables.models.PutTableBucketMaintenanceConfigurationRequest(
table_bucket_arn=args.table_bucket_arn,
type=args.type,
value=value,
)
)
print(f'status code: {result.status_code},'
f' request id: {result.request_id}')
print(f'successfully updated maintenance configuration for: {args.table_bucket_arn}')
if __name__ == "__main__":
main()Go
查詢未引用檔案清理配置:
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/tables"
)
var (
region string
tableBucketArn string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&tableBucketArn, "table-bucket-arn", "", "The arn of the table bucket.")
}
func main() {
flag.Parse()
if len(tableBucketArn) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table bucket arn required")
}
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := tables.NewTablesClient(cfg)
result, err := client.GetTableBucketMaintenanceConfiguration(context.TODO(), &tables.GetTableBucketMaintenanceConfigurationRequest{
TableBucketARN: oss.Ptr(tableBucketArn),
})
if err != nil {
log.Fatalf("failed to get table bucket maintenance configuration %v", err)
}
log.Printf("get table bucket maintenance configuration result:%#v\n", result)
}修改未引用檔案清理配置:
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/tables"
)
var (
region string
tableBucketArn string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&tableBucketArn, "table-bucket-arn", "", "The arn of the table bucket.")
}
func main() {
flag.Parse()
if len(tableBucketArn) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table bucket arn required")
}
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := tables.NewTablesClient(cfg)
result, err := client.PutTableBucketMaintenanceConfiguration(context.TODO(), &tables.PutTableBucketMaintenanceConfigurationRequest{
TableBucketARN: oss.Ptr(tableBucketArn),
Type: oss.Ptr("icebergUnreferencedFileRemoval"),
Value: &tables.MaintenanceValue{
Settings: &tables.MaintenanceSettings{
IcebergUnreferencedFileRemoval: &tables.SettingsDetail{
UnreferencedDays: oss.Ptr(4),
NonCurrentDays: oss.Ptr(10),
},
},
Status: oss.Ptr("enabled"),
},
})
if err != nil {
log.Fatalf("failed to put table bucket maintenance configuration %v", err)
}
log.Printf("put table bucket maintenance configuration result:%#v\n", result)
}Java
查詢未引用檔案清理配置:
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.tables.OSSTablesClient;
import com.aliyun.sdk.service.oss2.tables.models.*;
public class GetTableBucketMaintenanceConfigurationSample {
public static void main(String[] args) throws Exception {
String region = "cn-hangzhou";
String tableBucketARN = "acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket";
try (OSSTablesClient client = OSSTablesClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region(region)
.build()) {
GetTableBucketMaintenanceConfigurationRequest request = GetTableBucketMaintenanceConfigurationRequest.newBuilder()
.tableBucketARN(tableBucketARN)
.build();
GetTableBucketMaintenanceConfigurationResult result = client.getTableBucketMaintenanceConfiguration(request);
System.out.printf("Status code:%d, request id:%s%n",
result.statusCode(), result.requestId());
System.out.printf("Successfully got maintenance configuration for table bucket: %s%n", tableBucketARN);
System.out.printf("Table Bucket ARN: %s%n", result.tableBucketARN());
result.configuration().forEach((key, value) -> {
System.out.printf("Configuration Type: %s, Status: %s%n", key, value.status());
});
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}修改未引用檔案清理配置:
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.tables.OSSTablesClient;
import com.aliyun.sdk.service.oss2.tables.models.*;
public class PutTableBucketMaintenanceConfigurationSample {
public static void main(String[] args) throws Exception {
String region = "cn-hangzhou";
String tableBucketARN = "acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket";
String type = "icebergUnreferencedFileRemoval";
String status = "enabled";
int unreferencedDays = 3;
int nonCurrentDays = 3;
IcebergUnreferencedFileRemovalSettings removalSettings = IcebergUnreferencedFileRemovalSettings.newBuilder()
.unreferencedDays(unreferencedDays)
.nonCurrentDays(nonCurrentDays)
.build();
TableBucketMaintenanceSettings settings = TableBucketMaintenanceSettings.newBuilder()
.icebergUnreferencedFileRemoval(removalSettings)
.build();
TableBucketMaintenanceConfigurationValue value = TableBucketMaintenanceConfigurationValue.newBuilder()
.status(status)
.settings(settings)
.build();
try (OSSTablesClient client = OSSTablesClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region(region)
.build()) {
PutTableBucketMaintenanceConfigurationRequest request = PutTableBucketMaintenanceConfigurationRequest.newBuilder()
.tableBucketARN(tableBucketARN)
.type(type)
.value(value)
.build();
PutTableBucketMaintenanceConfigurationResult result = client.putTableBucketMaintenanceConfiguration(request);
System.out.printf("Status code:%d, request id:%s%n",
result.statusCode(), result.requestId());
System.out.printf("Successfully configured maintenance for table bucket: %s%n", tableBucketARN);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}API
通過以下API操作:
壓縮
壓縮是表層級的最佳化操作。在Apache Iceberg表中,頻繁的流式寫入或小批量寫入會產生大量小檔案,導致查詢時需要開啟和掃描過多的檔案,降低查詢效率。壓縮將多個小檔案合并為目標大小的檔案,減少檔案數量,提升查詢效能,同時降低中繼資料開銷。
壓縮的配置參數如下表所示。
參數 | 說明 | 取值範圍 | 預設值 |
status | 是否啟用自動壓縮。 | enabled或disabled。 | enabled |
targetFileSizeMB | 合并後的目標檔案大小(單位:MB)。合并任務會將小於該大小的檔案合并為接近該大小的檔案。 | 1~2147483647。 | 512 |
strategy | 壓縮策略。不同策略決定檔案的合并方式和排序行為。 | auto、binpack、sort或z-order。當設定為sort或z-order時,表的metadata中必須已定義sort order。 | auto |
壓縮策略的含義:
auto:系統自動選擇最優的壓縮策略。對具有排序次序的表應用排序壓縮策略。對沒有排序次序的表應用二進位包壓縮策略。
binpack:僅按檔案大小進行合并,不改變資料排序。合并速度最快,適用於對排序無要求的情境。
sort:按照表中定義的sort order對資料進行排序後合并。適用於範圍查詢情境。需要表的metadata中已定義sort order。
z-order:使用Z-Order曲線對多個列同時排序後合并。適用於多維度查詢情境。需要表的metadata中已定義sort order。
查看和配置壓縮。
控制台
在Table的資料維護頁面查看和修改壓縮配置。
登入OSS管理主控台,在左側導覽列選擇Table Bucket 列表。
單擊目標Table Bucket名稱,在Table 列表中單擊目標Table名稱進入Table詳情頁。
選擇資料維護 Tab頁。
在壓縮地區,查看當前配置狀態:
狀態:顯示功能是否已啟用。
目標檔案大小:合并後的目標檔案大小,預設為512 MB。
壓縮策略:當前使用的壓縮策略,預設為 auto。
作業狀態:顯示最近一次壓縮作業的執行狀態和時間。
單擊編輯按鈕,修改壓縮的配置參數,完成後單擊儲存。
ossutil
通過ossutil修改壓縮配置:
ossutil tables-api put-table-maintenance-configuration \
--table-bucket-arn {ARN} \
--namespace mynamespace \
--name mytable \
--type icebergCompaction \
--value '{"status":"enabled","settings":{"icebergCompaction":{"targetFileSizeMB":256,"strategy":"binpack"}}}'SDK
以下代碼展示了如何通過SDK查詢和修改Table的壓縮配置。
Python
查詢壓縮配置:
import argparse
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.tables as oss_tables
parser = argparse.ArgumentParser(description="get table maintenance configuration sample")
parser.add_argument('--region', help='The region in which the table bucket is located.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS Tables.')
parser.add_argument('--table-bucket-arn', help='The ARN of the table bucket.', required=True)
parser.add_argument('--namespace', help='The namespace of the table.', required=True)
parser.add_argument('--name', help='The name of the table.', required=True)
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss_tables.Client(cfg)
result = client.get_table_maintenance_configuration(
oss_tables.models.GetTableMaintenanceConfigurationRequest(
table_bucket_arn=args.table_bucket_arn,
namespace=args.namespace,
name=args.name,
)
)
print(f'status code: {result.status_code},'
f' request id: {result.request_id},'
f' table arn: {result.table_arn},'
f' configuration: {result.configuration}')
if __name__ == "__main__":
main()修改壓縮配置:
import argparse
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.tables as oss_tables
parser = argparse.ArgumentParser(description="put table maintenance configuration sample")
parser.add_argument('--region', help='The region in which the table bucket is located.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS Tables.')
parser.add_argument('--table-bucket-arn', help='The ARN of the table bucket.', required=True)
parser.add_argument('--namespace', help='The namespace of the table.', required=True)
parser.add_argument('--name', help='The name of the table.', required=True)
parser.add_argument('--type', help='The maintenance type, e.g., icebergCompaction.', required=True)
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss_tables.Client(cfg)
value = {
'status': 'enabled',
'settings': {
'icebergCompaction': {
'targetFileSizeMB': 512,
'strategy': 'auto'
}
}
}
result = client.put_table_maintenance_configuration(
oss_tables.models.PutTableMaintenanceConfigurationRequest(
table_bucket_arn=args.table_bucket_arn,
namespace=args.namespace,
name=args.name,
type=args.type,
value=value,
)
)
print(f'status code: {result.status_code},'
f' request id: {result.request_id}')
print(f'successfully updated maintenance configuration for: {args.namespace}/{args.name}')
if __name__ == "__main__":
main()Go
查詢壓縮配置:
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/tables"
)
var (
region string
tableBucketArn string
namespace string
name string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&tableBucketArn, "table-bucket-arn", "", "The arn of the table bucket.")
flag.StringVar(&namespace, "namespace", "", "The name of the namespace.")
flag.StringVar(&name, "name", "", "The name of the table.")
}
func main() {
flag.Parse()
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
if len(tableBucketArn) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table bucket arn required")
}
if len(namespace) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, namespace name required")
}
if len(name) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table name required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := tables.NewTablesClient(cfg)
result, err := client.GetTableMaintenanceConfiguration(context.TODO(), &tables.GetTableMaintenanceConfigurationRequest{
TableBucketARN: oss.Ptr(tableBucketArn),
Namespace: oss.Ptr(namespace),
Name: oss.Ptr(name),
})
if err != nil {
log.Fatalf("failed to get table maintenance configuration %v", err)
}
log.Printf("get table maintenance configuration result:%#v\n", result)
}修改壓縮配置:
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/tables"
)
var (
region string
tableBucketArn string
namespace string
name string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&tableBucketArn, "table-bucket-arn", "", "The arn of the table bucket.")
flag.StringVar(&namespace, "namespace", "", "The name of the namespace.")
flag.StringVar(&name, "name", "", "The name of the table.")
}
func main() {
flag.Parse()
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
if len(tableBucketArn) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table bucket arn required")
}
if len(namespace) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, namespace name required")
}
if len(name) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table name required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := tables.NewTablesClient(cfg)
// icebergCompaction type
result, err := client.PutTableMaintenanceConfiguration(context.TODO(), &tables.PutTableMaintenanceConfigurationRequest{
TableBucketARN: oss.Ptr(tableBucketArn),
Namespace: oss.Ptr(namespace),
Name: oss.Ptr(name),
Type: oss.Ptr("icebergCompaction"),
Value: &tables.TableMaintenanceValue{
Status: oss.Ptr("enabled"),
Settings: &tables.TableMaintenanceSettings{
IcebergCompaction: &tables.IcebergCompactionSettingsDetail{
TargetFileSizeMB: oss.Ptr(400),
Strategy: oss.Ptr("auto"),
},
},
},
})
// icebergSnapshotManagement type
//result, err := client.PutTableMaintenanceConfiguration(context.TODO(), &tables.PutTableMaintenanceConfigurationRequest{
// TableBucketARN: oss.Ptr(tableBucketArn),
// Namespace: oss.Ptr(namespace),
// Name: oss.Ptr(name),
// Type: oss.Ptr("icebergSnapshotManagement"),
// Value: &tables.TableMaintenanceValue{
// Status: oss.Ptr("enabled"),
// Settings: &tables.TableMaintenanceSettings{
// IcebergSnapshotManagement: &tables.IcebergSnapshotManagementSettingsDetail{
// MaxSnapshotAgeHours: oss.Ptr(350),
// MinSnapshotsToKeep: oss.Ptr(1),
// },
// },
// },
//})
if err != nil {
log.Fatalf("failed to put table maintenance configuration %v", err)
}
log.Printf("put table maintenance configuration result:%#v\n", result)
}Java
查詢壓縮配置:
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.tables.OSSTablesClient;
import com.aliyun.sdk.service.oss2.tables.models.*;
public class GetTableMaintenanceConfigurationSample {
public static void main(String[] args) throws Exception {
String region = "cn-hangzhou";
String tableBucketARN = "acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket";
String namespace = "mynamespace";
String name = "mytable";
try (OSSTablesClient client = OSSTablesClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region(region)
.build()) {
GetTableMaintenanceConfigurationRequest request = GetTableMaintenanceConfigurationRequest.newBuilder()
.tableBucketARN(tableBucketARN)
.namespace(namespace)
.name(name)
.build();
GetTableMaintenanceConfigurationResult result = client.getTableMaintenanceConfiguration(request);
System.out.printf("Status code:%d, request id:%s%n",
result.statusCode(), result.requestId());
System.out.printf("Table ARN: %s%n", result.tableARN());
if (result.configuration() != null && !result.configuration().isEmpty()) {
System.out.println("Maintenance configurations:");
result.configuration().forEach((type, config) -> {
System.out.printf(" Type: %s, Status: %s%n", type, config.status());
if (config.settings() != null) {
if (config.settings().icebergCompaction() != null) {
System.out.printf(" Compaction - TargetFileSizeMB: %d, Strategy: %s%n",
config.settings().icebergCompaction().targetFileSizeMB(),
config.settings().icebergCompaction().strategy());
}
if (config.settings().icebergSnapshotManagement() != null) {
System.out.printf(" SnapshotManagement - MinSnapshotsToKeep: %d, MaxSnapshotAgeHours: %d%n",
config.settings().icebergSnapshotManagement().minSnapshotsToKeep(),
config.settings().icebergSnapshotManagement().maxSnapshotAgeHours());
}
}
});
} else {
System.out.println("No maintenance configuration found.");
}
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}修改壓縮配置:
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.tables.OSSTablesClient;
import com.aliyun.sdk.service.oss2.tables.models.*;
public class PutTableMaintenanceConfigurationSample {
public static void main(String[] args) throws Exception {
String region = "cn-hangzhou";
String tableBucketARN = "acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket";
String namespace = "mynamespace";
String name = "mytable";
String type = "icebergCompaction";
String status = "enabled";
int targetFileSizeMB = 256;
String strategy = "auto";
try (OSSTablesClient client = OSSTablesClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region(region)
.build()) {
IcebergCompactionSettings compactionSettings = IcebergCompactionSettings.newBuilder()
.targetFileSizeMB(targetFileSizeMB)
.strategy(strategy)
.build();
TableMaintenanceSettings settings = TableMaintenanceSettings.newBuilder()
.icebergCompaction(compactionSettings)
.build();
TableMaintenanceConfigurationValue value = TableMaintenanceConfigurationValue.newBuilder()
.status(status)
.settings(settings)
.build();
PutTableMaintenanceConfigurationRequest request = PutTableMaintenanceConfigurationRequest.newBuilder()
.tableBucketARN(tableBucketARN)
.namespace(namespace)
.name(name)
.type(type)
.value(value)
.build();
PutTableMaintenanceConfigurationResult result = client.putTableMaintenanceConfiguration(request);
System.out.printf("Status code:%d, request id:%s%n",
result.statusCode(), result.requestId());
System.out.printf("Successfully put table maintenance configuration for table: %s/%s, type: %s%n", namespace, name, type);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}API
壓縮配置通過以下API操作:
快照管理
快照管理是表層級的最佳化操作。Apache Iceberg每次寫入操作都會產生一個新的快照(Snapshot),用於記錄資料的某個時間點狀態。隨著寫入次數增加,過多的快照會消耗大量儲存空間並增加中繼資料操作的開銷。快照管理自動清理到期快照,在保證資料安全的前提下減少儲存成本。
快照管理採用資料安全優先原則:當minSnapshotsToKeep和maxSnapshotAgeHours兩個條件衝突時,優先滿足最小保留快照數量,確保不會因清理快照而丟失必要的資料恢複能力。
快照管理的配置參數如下表所示。
參數 | 說明 | 取值範圍 | 預設值 |
status | 是否啟用快照管理。 | enabled或disabled。 | enabled |
maxSnapshotAgeHours | 快照最長保留時間(單位:小時)。超過該時間的快照將被清理。 | 1~2147483647。 | 120 |
minSnapshotsToKeep | 最少保留的快照數量。即使快照已超過最大保留時間,也會保留該數量的最近快照。 | 1~2147483647。 | 1 |
查看和配置快照管理。
控制台
在Table的資料維護頁面查看和修改快照管理配置。
登入OSS管理主控台,在左側導覽列選擇Table Bucket 列表。
單擊目標Table Bucket名稱,在Table 列表中單擊目標Table名稱進入Table詳情頁。
選擇資料維護 Tab頁。
在快照管理地區,查看當前配置狀態:
狀態:顯示功能是否已啟用。
最大快照保留時間:快照的最大保留時間,預設為120小時。
最少保留快照數:至少保留的快照數量,預設為1個。
作業狀態:顯示最近一次快照清理作業的執行狀態和時間。
單擊編輯按鈕,修改快照管理的配置參數,完成後單擊儲存。
ossutil
通過ossutil修改快照管理配置:
ossutil tables-api put-table-maintenance-configuration \
--table-bucket-arn {ARN} \
--namespace mynamespace \
--name mytable \
--type icebergSnapshotManagement \
--value '{"status":"enabled","settings":{"icebergSnapshotManagement":{"maxSnapshotAgeHours":72,"minSnapshotsToKeep":3}}}'SDK
以下代碼展示了如何通過SDK查詢和修改Table的快照管理配置。快照管理與壓縮使用相同的表級維護配置介面(GetTableMaintenanceConfiguration / PutTableMaintenanceConfiguration)。
Python
查詢快照管理配置:
import argparse
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.tables as oss_tables
parser = argparse.ArgumentParser(description="get table maintenance configuration sample")
parser.add_argument('--region', help='The region in which the table bucket is located.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS Tables.')
parser.add_argument('--table-bucket-arn', help='The ARN of the table bucket.', required=True)
parser.add_argument('--namespace', help='The namespace of the table.', required=True)
parser.add_argument('--name', help='The name of the table.', required=True)
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss_tables.Client(cfg)
result = client.get_table_maintenance_configuration(
oss_tables.models.GetTableMaintenanceConfigurationRequest(
table_bucket_arn=args.table_bucket_arn,
namespace=args.namespace,
name=args.name,
)
)
print(f'status code: {result.status_code},'
f' request id: {result.request_id},'
f' table arn: {result.table_arn},'
f' configuration: {result.configuration}')
if __name__ == "__main__":
main()修改快照管理配置:
import argparse
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.tables as oss_tables
parser = argparse.ArgumentParser(description="put table maintenance configuration sample")
parser.add_argument('--region', help='The region in which the table bucket is located.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS Tables.')
parser.add_argument('--table-bucket-arn', help='The ARN of the table bucket.', required=True)
parser.add_argument('--namespace', help='The namespace of the table.', required=True)
parser.add_argument('--name', help='The name of the table.', required=True)
parser.add_argument('--type', help='The maintenance type, e.g., icebergSnapshotManagement.', required=True)
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss_tables.Client(cfg)
value = {
'status': 'enabled',
'settings': {
'icebergSnapshotManagement': {
'maxSnapshotAgeHours': 72,
'minSnapshotsToKeep': 3
}
}
}
result = client.put_table_maintenance_configuration(
oss_tables.models.PutTableMaintenanceConfigurationRequest(
table_bucket_arn=args.table_bucket_arn,
namespace=args.namespace,
name=args.name,
type=args.type,
value=value,
)
)
print(f'status code: {result.status_code},'
f' request id: {result.request_id}')
print(f'successfully updated maintenance configuration for: {args.namespace}/{args.name}')
if __name__ == "__main__":
main()Go
查詢快照管理配置:
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/tables"
)
var (
region string
tableBucketArn string
namespace string
name string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&tableBucketArn, "table-bucket-arn", "", "The arn of the table bucket.")
flag.StringVar(&namespace, "namespace", "", "The name of the namespace.")
flag.StringVar(&name, "name", "", "The name of the table.")
}
func main() {
flag.Parse()
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
if len(tableBucketArn) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table bucket arn required")
}
if len(namespace) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, namespace name required")
}
if len(name) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table name required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := tables.NewTablesClient(cfg)
result, err := client.GetTableMaintenanceConfiguration(context.TODO(), &tables.GetTableMaintenanceConfigurationRequest{
TableBucketARN: oss.Ptr(tableBucketArn),
Namespace: oss.Ptr(namespace),
Name: oss.Ptr(name),
})
if err != nil {
log.Fatalf("failed to get table maintenance configuration %v", err)
}
log.Printf("get table maintenance configuration result:%#v\n", result)
}修改快照管理配置:
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/tables"
)
var (
region string
tableBucketArn string
namespace string
name string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&tableBucketArn, "table-bucket-arn", "", "The arn of the table bucket.")
flag.StringVar(&namespace, "namespace", "", "The name of the namespace.")
flag.StringVar(&name, "name", "", "The name of the table.")
}
func main() {
flag.Parse()
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
if len(tableBucketArn) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table bucket arn required")
}
if len(namespace) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, namespace name required")
}
if len(name) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table name required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := tables.NewTablesClient(cfg)
// icebergSnapshotManagement type
result, err := client.PutTableMaintenanceConfiguration(context.TODO(), &tables.PutTableMaintenanceConfigurationRequest{
TableBucketARN: oss.Ptr(tableBucketArn),
Namespace: oss.Ptr(namespace),
Name: oss.Ptr(name),
Type: oss.Ptr("icebergSnapshotManagement"),
Value: &tables.TableMaintenanceValue{
Status: oss.Ptr("enabled"),
Settings: &tables.TableMaintenanceSettings{
IcebergSnapshotManagement: &tables.IcebergSnapshotManagementSettingsDetail{
MaxSnapshotAgeHours: oss.Ptr(350),
MinSnapshotsToKeep: oss.Ptr(1),
},
},
},
})
if err != nil {
log.Fatalf("failed to put table maintenance configuration %v", err)
}
log.Printf("put table maintenance configuration result:%#v\n", result)
}Java
查詢快照管理配置:
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.tables.OSSTablesClient;
import com.aliyun.sdk.service.oss2.tables.models.*;
public class GetTableMaintenanceConfigurationSample {
public static void main(String[] args) throws Exception {
String region = "cn-hangzhou";
String tableBucketARN = "acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket";
String namespace = "mynamespace";
String name = "mytable";
try (OSSTablesClient client = OSSTablesClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region(region)
.build()) {
GetTableMaintenanceConfigurationRequest request = GetTableMaintenanceConfigurationRequest.newBuilder()
.tableBucketARN(tableBucketARN)
.namespace(namespace)
.name(name)
.build();
GetTableMaintenanceConfigurationResult result = client.getTableMaintenanceConfiguration(request);
System.out.printf("Status code:%d, request id:%s%n",
result.statusCode(), result.requestId());
System.out.printf("Table ARN: %s%n", result.tableARN());
if (result.configuration() != null && !result.configuration().isEmpty()) {
System.out.println("Maintenance configurations:");
result.configuration().forEach((type, config) -> {
System.out.printf(" Type: %s, Status: %s%n", type, config.status());
if (config.settings() != null) {
if (config.settings().icebergCompaction() != null) {
System.out.printf(" Compaction - TargetFileSizeMB: %d, Strategy: %s%n",
config.settings().icebergCompaction().targetFileSizeMB(),
config.settings().icebergCompaction().strategy());
}
if (config.settings().icebergSnapshotManagement() != null) {
System.out.printf(" SnapshotManagement - MinSnapshotsToKeep: %d, MaxSnapshotAgeHours: %d%n",
config.settings().icebergSnapshotManagement().minSnapshotsToKeep(),
config.settings().icebergSnapshotManagement().maxSnapshotAgeHours());
}
}
});
} else {
System.out.println("No maintenance configuration found.");
}
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}修改快照管理配置:
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.tables.OSSTablesClient;
import com.aliyun.sdk.service.oss2.tables.models.*;
public class PutTableMaintenanceConfigurationSample {
public static void main(String[] args) throws Exception {
String region = "cn-hangzhou";
String tableBucketARN = "acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket";
String namespace = "mynamespace";
String name = "mytable";
String type = "icebergSnapshotManagement";
String status = "enabled";
int maxSnapshotAgeHours = 72;
int minSnapshotsToKeep = 3;
try (OSSTablesClient client = OSSTablesClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region(region)
.build()) {
IcebergSnapshotManagementSettings snapshotManagementSettings = IcebergSnapshotManagementSettings.newBuilder()
.maxSnapshotAgeHours(maxSnapshotAgeHours)
.minSnapshotsToKeep(minSnapshotsToKeep)
.build();
TableMaintenanceSettings settings = TableMaintenanceSettings.newBuilder()
.icebergSnapshotManagement(snapshotManagementSettings)
.build();
TableMaintenanceConfigurationValue value = TableMaintenanceConfigurationValue.newBuilder()
.status(status)
.settings(settings)
.build();
PutTableMaintenanceConfigurationRequest request = PutTableMaintenanceConfigurationRequest.newBuilder()
.tableBucketARN(tableBucketARN)
.namespace(namespace)
.name(name)
.type(type)
.value(value)
.build();
PutTableMaintenanceConfigurationResult result = client.putTableMaintenanceConfiguration(request);
System.out.printf("Status code:%d, request id:%s%n",
result.statusCode(), result.requestId());
System.out.printf("Successfully put table maintenance configuration for table: %s/%s, type: %s%n", namespace, name, type);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}API
快照管理配置通過以下API操作:
運行機制
觸發時機:維護任務由系統後台自動調度執行,非固定時間點觸發。通常情況下,針對同一個Table的同一種維護任務大約每24小時運行一次。當系統負載高時,維護任務可能會延後啟動。
資源佔用:自動維護任務不佔用使用者側的吞吐和QPS配額,不影響生產任務的正常運行。
事務隔離:維護任務基於Iceberg的快照隔離機制運行。在合并檔案的同時,生產任務可以繼續寫入新資料。讀取任務也不受影響,查詢引擎會自動選擇最新的有效快照。
查詢維護任務狀態
查詢Table的三種維護任務(壓縮、快照管理、未引用檔案清理)的執行狀態。每種維護任務獨立返回各自的狀態資訊。
控制台
在Table的資料維護頁面查看各維護任務的作業狀態。
登入OSS管理主控台,在左側導覽列選擇Table Bucket 列表。
單擊目標Table Bucket名稱,在Table 列表中單擊目標Table名稱進入Table詳情頁。
選擇資料維護 Tab頁。
在各維護模組地區查看作業狀態資訊:
壓縮:顯示壓縮任務的執行狀態(如"失敗"或"成功")及最後已耗用時間。
快照管理:顯示快照清理任務的執行狀態及最後已耗用時間。
未引用檔案清理:Bucket層級配置,單擊在Table Bucket編輯配置連結跳轉到Table Bucket的資料維護頁面查看。
ossutil
ossutil tables-api get-table-maintenance-job-status \
--table-bucket-arn acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket \
--namespace mynamespace \
--name mytableSDK
Python
import argparse
import alibabacloud_oss_v2 as oss
import alibabacloud_oss_v2.tables as oss_tables
parser = argparse.ArgumentParser(description="get table maintenance job status sample")
parser.add_argument('--region', help='The region in which the table bucket is located.', required=True)
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS Tables.')
parser.add_argument('--table-bucket-arn', help='The ARN of the table bucket.', required=True)
parser.add_argument('--namespace', help='The namespace of the table.', required=True)
parser.add_argument('--name', help='The name of the table.', required=True)
def main():
args = parser.parse_args()
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
if args.endpoint is not None:
cfg.endpoint = args.endpoint
client = oss_tables.Client(cfg)
result = client.get_table_maintenance_job_status(
oss_tables.models.GetTableMaintenanceJobStatusRequest(
table_bucket_arn=args.table_bucket_arn,
namespace=args.namespace,
name=args.name,
)
)
print(f'status code: {result.status_code},'
f' request id: {result.request_id},'
f' table arn: {result.table_arn},'
f' status: {result.status}')
if __name__ == "__main__":
main()Go
package main
import (
"context"
"flag"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/tables"
)
var (
region string
tableBucketArn string
namespace string
name string
)
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&tableBucketArn, "table-bucket-arn", "", "The arn of the table bucket.")
flag.StringVar(&namespace, "namespace", "", "The name of the namespace.")
flag.StringVar(&name, "name", "", "The name of the table.")
}
func main() {
flag.Parse()
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
if len(tableBucketArn) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table bucket arn required")
}
if len(namespace) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, namespace name required")
}
if len(name) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, table name required")
}
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
client := tables.NewTablesClient(cfg)
result, err := client.GetTableMaintenanceJobStatus(context.TODO(), &tables.GetTableMaintenanceJobStatusRequest{
TableBucketARN: oss.Ptr(tableBucketArn),
Namespace: oss.Ptr(namespace),
Name: oss.Ptr(name),
})
if err != nil {
log.Fatalf("failed to get table maintenance job status %v", err)
}
log.Printf("get table maintenance job status result:%#v\n", result)
}Java
import com.aliyun.sdk.service.oss2.credentials.EnvironmentVariableCredentialsProvider;
import com.aliyun.sdk.service.oss2.tables.OSSTablesClient;
import com.aliyun.sdk.service.oss2.tables.models.*;
public class GetTableMaintenanceJobStatusSample {
public static void main(String[] args) throws Exception {
String region = "cn-hangzhou";
String tableBucketARN = "acs:osstables:cn-hangzhou:1234567890:bucket/mytable-bucket";
String namespace = "mynamespace";
String name = "mytable";
try (OSSTablesClient client = OSSTablesClient.newBuilder()
.credentialsProvider(new EnvironmentVariableCredentialsProvider())
.region(region)
.build()) {
GetTableMaintenanceJobStatusRequest request = GetTableMaintenanceJobStatusRequest.newBuilder()
.tableBucketARN(tableBucketARN)
.namespace(namespace)
.name(name)
.build();
GetTableMaintenanceJobStatusResult result = client.getTableMaintenanceJobStatus(request);
System.out.printf("Status code:%d, request id:%s%n",
result.statusCode(), result.requestId());
System.out.printf("Table ARN: %s%n", result.tableARN());
if (result.jobStatus() != null && !result.jobStatus().isEmpty()) {
System.out.println("Maintenance job status:");
result.jobStatus().forEach((type, status) -> {
System.out.printf(" Type: %s, Status: %s%n", type, status.status());
if (status.lastRunTimestamp() != null) {
System.out.printf(" LastRunTimestamp: %s%n", status.lastRunTimestamp());
}
if (status.failureMessage() != null && !status.failureMessage().isEmpty()) {
System.out.printf(" FailureMessage: %s%n", status.failureMessage());
}
});
} else {
System.out.println("No maintenance job status found.");
}
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}API
使用GetTableMaintenanceJobStatus介面查詢維護任務狀態。