KingbaseES数据库批量加载数据的最佳方法

发布时间 2023-09-18 13:36:06作者: KINGBASE研究院

前言

本文讨论在KingbaseES数据库服务器中批量加载数据的方法,以及空数据库中的初始数据加载和增量数据加载的最佳做法。

批量加载方法

以下数据加载方法按照从最耗时到最不耗时的顺序排列:

1.运行单记录 INSERT 命令。
2.在每次提交中分批成 100 到 1000 行。可以使用事务块在每次提交时包含多条记录。
3.运行INSERT命令,使用多个行的value值。
4.运行 COPY 命令。
将数据加载到数据库的首选方法是使用 COPY 命令。如果无法使用 COPY 命令,则使用分批提交处理 INSERT 是次优方法。
另外使用 COPY 命令进行多线程处理是批量加载数据的最佳方法。

初始数据加载的最佳做法

删除索引
在执行初始数据加载之前,建议删除表中的所有索引。在加载数据后创建索引,这样加载速度更快。

删除约束
下面描述了有关删除约束:

唯一键约束
建议在执行初始数据加载之前删除唯一键约束,并在数据加载完成后重新创建这些约束。但是删除唯一键约束会引发重复数据。

外键约束
建议执行初始数据加载之前删除外键约束,并在数据加载完成后重新创建这些约束。
将session_replication_role参数更改为replica也会禁用所有外键。注意,如果使用不当,进行更改可能会导致数据处于不一致状态。

无日志记录表
在初始数据加载中使用无日志记录表之前,考虑使用此类表的优点和缺点。
优点:
使用无日志记录表可以加快数据加载速度。写入无日志记录表的数据不会写入预写日志。
缺点:
数据库崩溃时不安全。发生崩溃或者执行不彻底的关机后,无日志记录表将被自动截断,无法恢复。
无日志记录表中的数据无法复制到备用服务器。

使用以下语法创建新的无日志记录表:

CREATE UNLOGGED TABLE <tablename>;

使用以下语法将现有日志记录表转换为无日志记录表:

ALTER TABLE <tablename> SET UNLOGGED;

服务器参数优化

autovacuum:在初始数据加载期间,最好关闭 autovacuum进程,初始加载完成后,建议对数据库中的所有表手动运行VACUUM ANALYZE,然后打开autovacuum进程。

备注:
请仅在内存和磁盘空间足够时遵循以下的建议。

maintenance_work_mem:建议在数据库服务器上设置为2GB。

maintenance_work_mem有助于加快autovacuum,索引和外键的创建。

checkpoint_timeout:在数据库服务器上,可将checkpoint_timeout值从默认设置5分钟增加到最大24小时。建议最初在灵活服务器上加载数据之前将该值增加到1小时。
最终生产上设置多大值需要结合业务上dml操作的频繁度,在安全和性能之间权衡。

checkpoint_completion_target:建议设置为值0.9。

max_wal_size:可设置为允许的最大值(执行初始数据加载时设置为64 GB)。

在业务高峰期,该参数通过以下方法得出一个合理值:
a. 运行以下查询获取当前的WAL日志序列号(LSN):

SELECT sys_current_wal_lsn ();

b. 等待checkpoint_timeout的秒数。运行以下查询获取当前的WAL LSN:

SELECT sys_current_wal_lsn ();

c. 使用这两个结果来检查差异(以 GB 为单位):

SELECT round (sys_wal_lsn_diff('LSN value when run second time','LSN value when run first time')/1024/1024/1024,2) WAL_CHANGE_GB;

wal_compression:可以打开。启用此参数可能会导致WAL日志记录期间的压缩和WAL日志重放期间的解压缩产生一些额外的CPU成本。

推荐
在数据库上开始初始数据加载之前建议:
在服务器上禁用高可主备流复制。在主库上完成初始加载后,再启用流复制。
在初始数据加载期间尽量减少日志记录量或完全禁用它(例如:禁用pgaudit、sys_stat_statements)。
加载数据后重新创建索引并添加约束
可以修改以下参数,在初始数据加载后更快地创建并行索引:

max_parallel_workers:可为并行查询提供的最大工作进程数。
max_parallel_maintenance_workers:控制可在CREATE INDEX中使用的最大工作进程数。

还可以通过在会话级别设置创建索引。 以下示例演示如何执行此操作:

SET maintenance_work_mem = '2GB';
SET max_parallel_workers = 16;
SET max_parallel_maintenance_workers = 8;
CREATE INDEX test_index ON test_table(test_column);

增量数据加载的最佳做法

将表分区
建议将大型表分区(尤其是千万行以上的大表)。 分区的一些优点(尤其是在增量加载期间)包括:
1.基于新增量数据创建新分区可以更高效地将新数据添加到表中。
2.为了表的维护更容易,可以在增量数据加载期间删除分区,以避免在大型表维护中进行耗时的删除。
3.仅增量加载期间更改或添加的分区上会触发Autovacuum,这使得可以更轻松地维护表中的统计信息。监视和维护表统计信息准确对于数据库的查询性能至关重要。

基于外键约束创建索引
以下情况下,在子表中基于外键创建索引可能有好处:
在父表中更新或删除数据。在父表中更新或删除数据时,将对子表执行查找,为加快查找速度,可以对子表的外键进行索引。
父表和子表的联接出现在键列上的查询。

识别未使用的索引
识别数据库中未使用的索引并删除。索引是数据加载的开销。表中的索引越少,数据加载期间的性能就越好。

索引使用情况
使用以下查询来识别未使用的索引:

SELECT 
    t.schemaname, 
    t.tablename, 
    c.reltuples::bigint                            AS num_rows, 
    sys_size_pretty(sys_relation_size(c.oid))        AS table_size, 
    ps.indexrelname                              AS index_name, 
    sys_size_pretty(sys_relation_size(i.indexrelid)) AS index_size, 
    CASE WHEN i.indisunique THEN 'Y' ELSE 'N' END AS "unique", 
    ps.idx_scan                                  AS idx_number_of_scans, 
    ps.idx_tup_read                              AS idx_tuples_read, 
    ps.idx_tup_fetch                             AS idx_tuples_fetched 
FROM 
    sys_tables t 
    LEFT JOIN sys_class c ON t.tablename = c.relname 
    LEFT JOIN sys_index i ON c.oid = i.indrelid 
    LEFT JOIN sys_stat_all_indexes ps ON i.indexrelid = ps.indexrelid 
WHERE 
    t.schemaname NOT IN ('sys_catalog','pg_catalog','information_schema') 
ORDER BY 1, 2; 
idx_number_of_scans、idx_tuples_read 和 idx_tuples_fetched 列将指示索引使用情况。idx_number_of_scans 列值为零表示未使用的索引。