更新时间:2024-05-20 GMT+08:00

DQL/DML-DDL并发

表级DDL会给分区表施加8级锁,阻塞全部的DQL/DML操作。

分区级DDL会给分区表施加4级锁,并给目标分区施加8级锁。当DQL/DML与DDL作用不同分区时,支持二者执行层面的并发;当DQL/DML与DDL作用相同分区时,后触发业务会被阻塞。

如果并发的DDL与DQL/DML作用目标分区有重叠,由于串行阻塞,DQL/DML既可能先于DDL发生,也可能后于DDL发生,用户应该明确知晓其可能的预期结果。比如当Truncate与Insert作用同一分区时,如果Truncate先于Insert触发,则业务完成后目标分区存在数据,如果Truncate后于Insert触发,则业务完成后目标分区不存在数据。

业务在进行分区DDL维护操作时,应尽可能避免期间同时对目标分区进行DQL/DML操作。

DQL/DML-DDL跨分区并发

GaussDB Kernel支持跨分区的DQL/DML-DDL并发。

例如,定义如下分区表range_sales,下面给出了一些支持并发的例子。
CREATE TABLE range_sales
(
    product_id     INT4 NOT NULL,
    customer_id    INT4 NOT NULL,
    time_id        DATE,
    channel_id     CHAR(1),
    type_id        INT4,
    quantity_sold  NUMERIC(3),
    amount_sold    NUMERIC(10,2)
)
PARTITION BY RANGE (time_id)
(
    PARTITION time_2008 VALUES LESS THAN ('2009-01-01'),
    PARTITION time_2009 VALUES LESS THAN ('2010-01-01'),
    PARTITION time_2010 VALUES LESS THAN ('2011-01-01'),
    PARTITION time_2011 VALUES LESS THAN ('2012-01-01')
);
分区表支持的并发业务可以为如下场景。
--并发case1,插入分区time_2011与清空分区time_2008互不阻塞
\parallel on
INSERT INTO range_sales VALUES (455124, 92121433, '2011-09-17', 'X', 4513, 7, 17);
ALTER TABLE range_sales TRUNCATE PARTITION time_2008 UPDATE GLOBAL INDEX;
\parallel off

--并发case2,指定分区time_2010查询与交换分区time_2009互不阻塞
\parallel on
SELECT COUNT(*) FROM range_sales PARTITION (time_2010);
ALTER TABLE range_sales EXCHANGE PARTITION (time_2009) WITH TABLE temp UPDATE GLOBAL INDEX;
\parallel off

--并发case3,对分区表range_ sales做更新与删除分区time_2008互不阻塞,这是因为更新SQL带条件剪枝到分区time_2010和time_2011上
\parallel on
UPDATE range_sales SET channel_id = 'T' WHERE channel_id = 'X' AND time_id > '2010-06-01';
ALTER TABLE range_sales DROP PARTITION time_2008 UPDATE GLOBAL INDEX;
\parallel off

--并发case4,对分区表range_ sales的任何DQL/DML操作与新增分区time_2012互不阻塞,这是因为新增分区对其他进行的业务不可见
\parallel on
DELETE FROM range_sales WHERE channel_id = 'T';
ALTER TABLE range_sales ADD PARTITION time_2012 VALUES LESS THAN ('2013-01-01');
\parallel off

DQL/DML-DDL同分区并发

GaussDB Kernel不支持同分区的DQL/DML-DDL并发,后触发业务会被先触发业务阻塞。

原则上,不建议用户在进行分区DDL时,同时对该分区进行DQL/DML操作,因为目标分区存在一个状态的突变过程,可能会导致业务的查询结果不符合预期。

如果由于业务模型不合理、无法剪枝等场景导致的DQL/DML和DDL作用分区有重叠时,考虑两种场景:

场景一:先触发DQL/DML,再触发DDL。DDL会被阻塞,等DQL/DML提交后再进行。

场景二:先触发DDL,再触发DQL/DML。DQL/DML会被阻塞,等DDL提交后再进行,由于分区元信息发生了变更,可能导致预期不合理。为了保证数据一致性,预期结果按照如下规则制定。

  • ADD分区

    ADD分区会产生一个新的分区,这个新分区对期间触发的DQL/DML操作均是不可见的,无阻塞期。

  • DROP分区

    DROP分区会将已有分区进行删除,期间触发的目标分区DQL/DML操作会被阻塞,阻塞完成后跳过对该分区的处理。

  • TRUNCATE分区

    TRUNCATE分区会将已有分区清空数据,期间触发的目标分区DQL/DML操作会被阻塞,阻塞完成后继续对该分区进行处理。

    注意期间触发的目标分区查询是查不到数据的,因为TRUNCATE操作提交后目标分区中不存有任何数据。

  • EXCHANGE分区

    EXCHANGE分区会将一个已有分区与普通表进行交换,期间触发的目标分区DQL/DML操作会被阻塞,阻塞完成后继续对该分区进行处理,该分区的实际数据对应原普通表。

    例外:如果分区表上存在GLOBAL索引,EXCHANGE命令带来UPDATE GLOBAL INDEX子句,且期间触发的分区表查询使用了GLOBAL索引,由于无法查询到交换后分区上的数据,在阻塞完成后查询业务会报错。

    ERROR: partition xxxxxx does not exist on relation "xxxxxx"

    DETAIL: this partition may have already been dropped by cocurrent DDL operations EXCHANGE PARTITION

  • SPLIT分区

    SPLIT分区会将一个分区分割为多个分区,即使其中一个新分区与旧分区名字相同,也视为不同的分区。期间触发的目标分区DQL/DML操作会被阻塞,阻塞完成后业务报错。

    ERROR: partition xxxxxx does not exist on relation "xxxxxx"

    DETAIL: this partition may have already been dropped by cocurrent DDL operations SPLIT PARTITION

  • MERGE分区

    MERGE分区会将多个分区合并为一个分区,如果合并后的分区与其中一个旧分区A名字相同,逻辑上视为相同分区。期间触发的目标分区DQL/DML操作会被阻塞,阻塞完成后,根据目标分区类型判断,如果目标分区是旧分区A,则作用于新分区;如果目标分区为其他旧分区,则业务报错。

    ERROR: partition xxxxxx does not exist on relation "xxxxxx"

    DETAIL: this partition may have already been dropped by cocurrent DDL operations MERGE PARTITION

  • RENAME分区

    RENAME分区不会变更分区结构信息,期间触发的DQL/DML操作不会出现任何异常,但会被阻塞,直到RENAME操作提交。

  • MOVE分区

    MOVE分区不会变更分区结构信息,期间触发的DQL/DML操作不会出现任何异常,但会被阻塞,直到MOVE操作提交。