暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

OB 4.3.5 堆表功能研究(二)

148

接上文继续研究 OB 4.3.5 的堆表特性。 这次主要是研究分区表的数据和索引存储。研究方法依然是先看索引组织表的特性,然后在堆表跟索引组织表之间进行对比。

索引组织表和堆表实验(二)

创建索引组织表分区表

为了简化细节,分区表用行存格式,不开租户自动并行,创建的是 RANGE 分区表,初始记录 10w。

-- 创建分区表,使用 UNIX_TIMESTAMP(c4) 作为分区键
CREATETABLE iot_pt (
    c1 CHAR(36) NOTNULL,
    c2 VARCHAR(50),
    c3 VARCHAR(500),
    c4 TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP
)  ORGANIZATION INDEX
PARTITIONBYRANGE (UNIX_TIMESTAMP(c4)) (
    PARTITION p202501 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-02-01 00:00:00')),
    PARTITION p202502 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-03-01 00:00:00')),
    PARTITION p202503 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-04-01 00:00:00')),
    PARTITION p202504 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-05-01 00:00:00')),
    PARTITION p202505 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-06-01 00:00:00')),
    PARTITION p202506 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-07-01 00:00:00')),
    PARTITION p202507 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-08-01 00:00:00')),
    PARTITION p202508 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-09-01 00:00:00')),
    PARTITION p202509 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-10-01 00:00:00')),
    PARTITION p202510 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-11-01 00:00:00')),
    PARTITION p202511 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-12-01 00:00:00')),
    PARTITION p202512 VALUESLESSTHAN (UNIX_TIMESTAMP('2026-01-01 00:00:00'))
) WITH COLUMNGROUP(all columns);

创建存储过程插入 10 万条测试数据

-- 创建存储过程插入 10 万条测试数据
DELIMITER $$

CREATE PROCEDURE InsertIOTData()
BEGIN
    DECLARE i INTDEFAULT0;
    DECLARE total_rows INTDEFAULT100000;
    DECLARE rows_per_month INTDEFAULT8333; -- 每个分区约8333条
    DECLARE month_offset INT;
    DECLARE base_unix_time BIGINT;
    DECLARE target_unix_time BIGINT;
    DECLARE insert_date TIMESTAMP;
    
    -- 设置基础时间(2025年1月1日)
    SET base_unix_time = UNIX_TIMESTAMP('2025-01-01 00:00:00');
    
    -- 显示开始时间
    SELECTCONCAT('开始插入数据: ', NOW()) AS'开始时间';
    
    -- 禁用自动提交,提高插入性能
    SET autocommit = 0;
    STARTTRANSACTION;
    
    -- 循环插入10万条数据
    WHILE i < total_rows DO
        -- 计算月份偏移(0-11个月)
        SET month_offset = FLOOR(i rows_per_month);
        
        -- 确保month_offset在0-11范围内
        IF month_offset > 11 THEN
            SET month_offset = 11;
        ENDIF;
        
        -- 计算目标时间戳(在指定月份内均匀分布)
        SET target_unix_time = base_unix_time + 
                               (month_offset * 30 * 24 * 3600) +  -- 月份偏移
                               (MOD(i, 28) * 24 * 3600) +         -- 0-27天的偏移
                               (MOD(i, 24) * 3600) +              -- 小时偏移
                               (MOD(i, 60) * 60) +                -- 分钟偏移
                               MOD(i, 60);                        -- 秒偏移
        
        -- 将UNIX时间戳转换为TIMESTAMP
        SET insert_date = FROM_UNIXTIME(target_unix_time);
        
        -- 插入数据
        INSERTINTO iot_pt (c1, c2, c3, c4)
        VALUES (
            UUID(),  -- 生成UUID
            CONCAT('Device_', LPAD(MOD(i, 1000), 4, '0')),  -- 设备ID
            CONCAT('Sensor data: ', FLOOR(RAND() * 1000), 
                   ', Temp: ', ROUND(20 + RAND() * 20, 2), 
                   '°C, Humidity: ', ROUND(30 + RAND() * 50, 2), '%',
                   ', Status: ', IF(RAND() > 0.5, 'ON', 'OFF')),
            insert_date
        );
        
        -- 每10000条记录显示进度
        IF i % 10000 = 0 AND i > 0 THEN
            SELECTCONCAT('已插入 ', i, ' 条记录 (', ROUND(i/1000, 1), '%)') AS'进度';
        ENDIF;
        
        SET i = i + 1;
    ENDWHILE;
    
    -- 提交事务
    COMMIT;
    SET autocommit = 1;
    
    SELECTCONCAT('数据插入完成! 总共插入 ', i, ' 条记录') AS'完成';
END
$$

DELIMITER ;

-- 执行存储过程插入数据
CALL InsertIOTData();

主键和唯一约束测试

给分区表加主键和唯一约束。

alter table iot_pt add primary key (c1);

alter table iot_pt add unique key (c1);

create index iot_pt_uk_local on iot_pt(c1) local;

IOT 分区表加主键和唯一约束

结论:

  • 分区表的主键和唯一键约束要求约束列包含所有分区列。
select /*+ opt_param('hidden_column_visible','true') */ __pk_increment , c1, c2, c4 from iot_pt where __pk_increment = 10 ;

IOT 无主键分区表隐藏列

结论

  • IOT 分区表无主键时会内部有一个隐藏列 __pk_increment
     并且这个列是单个分区内部自增且唯一,全局层面不唯一。
select c1, c2, c4 from iot_pt where c1='632f3f53-e78c-11f0-a1d1-005056bed6e6' ;
select dbms_xplan.display_cursor()\G

IOT 分区表无主键时查询全表扫描

结论:

  • IOT 无主键表全表扫描是各个分区并行扫描,分区内扫描顺序是隐藏列 __pk_increment
     顺序。

IOT 分区表唯一索引测试

create unique index iot_pt_uk on iot_pt(c1);
create unique index iot_pt_uk_local on iot_pt(c1,c4) local ;
show indexes from iot_pt_uk;

IOT 无主键分区表加本地唯一索引

结论:

  • IOT 分区表加本地唯一索引要求索引列包含所有分区列。
select c1, c2, c4 from iot_pt where c1='632f3f53-e78c-11f0-a1d1-005056bed6e6' ;
select dbms_xplan.display_cursor()\G

IOT 分区表查询使用本地唯一索引

结论:

  • IOT 无主键表唯一索引列会自动新增隐藏列 shadow_pk_0
     ,其原因是 OB 的索引可以存储 NULL 值。据说为了实现唯一加一个隐藏列。这个隐藏列不同于 _pk_increment
     。
alter table iot_pt alter index iot_pt_uk_local invisible ;
show indexes from iot_pt;

OB 表索引隐藏示例

结论:

  • OB 表的索引可以隐藏,不被优化器使用。
create unique index iot_pt_uk_global on iot_pt(c1) global;

IOT 无主键表加全局唯一索引
select c1, c4 from iot_pt where c1='632f3f53-e78c-11f0-a1d1-005056bed6e6' ;
select dbms_xplan.display_cursor()\G
select c1, c2, c4 from iot_pt where c1='632f3f53-e78c-11f0-a1d1-005056bed6e6' ;
select dbms_xplan.display_cursor()\G


IOT 无主键表查询使用全局唯一索引覆盖
IOT 无主键表查询使用全局唯一索引回表

结论:

  • IOT 分区表全局唯一索引包含两个隐藏列:shaodow_pk_0
     和 shadow_pk_1
     ,不同于隐藏列 __pk_increment
     。
  • IOT 分区表全局唯一索引里应该是包含了分区列,所以第一个索引查询不用回表。

IOT 分区表普通本地索引测试

alter table iot_pt add index iot_pt_ind1(c2);

IOT 无主键表普通本地索引
select c1, c2, c4 from iot_pt where c2='Device_0676' and c4 BETWEEN '2025-01-01 00:00:00' and '2025-01-10 00:00:00' ;

IOT 无主键表查询使用本地索引

结论:

  • IOT 分区表本地普通索引包含隐藏列 __pk_increment
     。
  • 该查询条件有分区字段做了分区裁剪,所以最终是单分区的索引扫描执行计划(TABLE RANGE SCAN(索引名)
    )。

IOT 分区表变有主键表后测试

alter table iot_pt add primary key(c1,c4);

obclient(mq@SERVICE:s_mysql)[test]> select /*+ opt_param('hidden_column_visible','true') */ __pk_increment , c1, c2, c4 from iot_pt where __pk_increment = 110 ;
ERROR 1054 (42S22): Unknown column '__pk_increment' in 'where clause'

结论:

  • IOT 分区表加了主键后,内部隐藏列 __pk_increment
     列消失。
IOT 分区表后期加主键
IOT 分区表有主键后查询使用本地普通索引覆盖
IOT 分区表有主键后查询使用本地普通索引回表

结论:

  • IOT 分区表有主键时普通本地索引里包含主键列信息。
select c1, c2, c4 from iot_pt where c1='632f3f53-e78c-11f0-a1d1-005056bed6e6' ;

IOT 分区表有主键时主键索引扫描

结论:

  • IOT 分区表有主键时,查询条件只有主键列部分列,走主键索引扫描:TABLE RANGE SCAN(主键索引名)
    ,其中主键索引名就是表名。

HEAP 表测试

新建 HEAP 分区表

obclient(mq@SERVICE:s_mysql)[test]> alter table iot_pt organization heap;
ERROR 1235 (0A000): organization in the alter table statement not supported

结论:

  • 暂不支持 IOT 表变更为堆表。
CREATE TABLE heap_pt (
    c1 CHAR(36) NOTNULL,
    c2 VARCHAR(50),
    c3 VARCHAR(500),
    c4 TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP
)  ORGANIZATION HEAP
PARTITIONBYRANGE (UNIX_TIMESTAMP(c4)) (
    PARTITION p202501 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-02-01 00:00:00')),
    PARTITION p202502 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-03-01 00:00:00')),
    PARTITION p202503 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-04-01 00:00:00')),
    PARTITION p202504 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-05-01 00:00:00')),
    PARTITION p202505 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-06-01 00:00:00')),
    PARTITION p202506 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-07-01 00:00:00')),
    PARTITION p202507 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-08-01 00:00:00')),
    PARTITION p202508 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-09-01 00:00:00')),
    PARTITION p202509 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-10-01 00:00:00')),
    PARTITION p202510 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-11-01 00:00:00')),
    PARTITION p202511 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-12-01 00:00:00')),
    PARTITION p202512 VALUESLESSTHAN (UNIX_TIMESTAMP('2026-01-01 00:00:00'))
) WITH COLUMNGROUP(all columns);

insert/*+ append enable_parallel_dml parallel(4) */into heap_pt select * from iot_pt;

select/*+ opt_param('hidden_column_visible','true') */ __pk_increment , c1, c2, c4 from heap_pt where __pk_increment = 10 ;

HEAP 分区表无主键建表

结论:

  • HEAP 分区表也有内部隐藏列 __pk_increment
     ,并且只在分区内部自增和唯一。

HEAP 分区表无主键时查询测试

select c1, c2, c4 from heap_pt where c1='6864d475-e78c-11f0-a1d1-005056bed6e6' ;
select dbms_xplan.display_cursor()\G

HEAP 分区表无主键时查询走全表扫描

结论:

  • HEAP 分区表所有分区并行扫描,每个分区按照隐藏列 __pk_increment
     顺序扫描。
create unique index heap_pt_uk on heap_pt(c1);
create unique index heap_pt_uk_local on heap_pt(c1,c4) local ;
show indexes from heap_pt;

HEAP 分区表无主键时加本地唯一索引约束
HEAP 分区表无主键时查询使用本地唯一索引

结论:

  • HEAP 分区表的本地唯一索引也必须包含所有分区列。
  • HEAP 分区表的本地唯一索引同样有隐藏列 shadow_pk_0
     ,不同于隐藏列 __pk_increment
     。
  • HEAP 分区表查询走本地唯一索引时,需要并行扫描所有分区。
alter table heap_pt alter index heap_pt_uk_local invisible;
create unique index heap_pt_uk_global on heap_pt(c1) global ;

HEAP 分区表本地唯一索引隐藏
HEAP 分区表无主键查询走全局唯一索引

结论:

  • HEAP 分区表全局唯一索引同样有两个隐藏列(shadow_pk_0
     和 shadow_pk_1
    )。
  • HEAP 分区表全局索引扫描能返回具体的分区,所以回表的时候是单个分区的索引扫描,但是这个是分布式的执行计划:DISTRIBUTED TABLE GET
     。
create index heap_pt_ind1 on heap_pt(c2) local;
select c1, c2, c4 from heap_pt where c2='Device_0676' and c4 BETWEEN '2025-01-01 00:00:00' and '2025-01-10 00:00:00' ;
select dbms_xplan.display_cursor()\G

HEAP 分区表无主键查询走本地普通索引

结论:

  • HEAP 分区表普通本地索引列包含隐藏列 __pk_increment
     列。
  • 查询有分区字段先做分区裁剪,最后只扫描单个分区的索引并回表,所以执行计划是:TABLE RANGE SCAN
     ,没有并行。

HEAP 分区表有主键时查询测试

CREATE TABLE heap_pt_with_pk (
    c1 CHAR(36) NOTNULL,
    c2 VARCHAR(50),
    c3 VARCHAR(500),
    c4 TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP
)  ORGANIZATION HEAP
PARTITIONBYRANGE (UNIX_TIMESTAMP(c4)) (
    PARTITION p202501 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-02-01 00:00:00')),
    PARTITION p202502 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-03-01 00:00:00')),
    PARTITION p202503 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-04-01 00:00:00')),
    PARTITION p202504 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-05-01 00:00:00')),
    PARTITION p202505 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-06-01 00:00:00')),
    PARTITION p202506 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-07-01 00:00:00')),
    PARTITION p202507 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-08-01 00:00:00')),
    PARTITION p202508 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-09-01 00:00:00')),
    PARTITION p202509 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-10-01 00:00:00')),
    PARTITION p202510 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-11-01 00:00:00')),
    PARTITION p202511 VALUESLESSTHAN (UNIX_TIMESTAMP('2025-12-01 00:00:00')),
    PARTITION p202512 VALUESLESSTHAN (UNIX_TIMESTAMP('2026-01-01 00:00:00'))
) WITH COLUMNGROUP(all columns);
insert/*+ append enable_parallel_dml parallel(4) */into heap_pt_with_pk select * from heap_pt;

select c1, c2, c4 from heap_pt_with_pk where c1='6864d475-e78c-11f0-a1d1-005056bed6e6' ;
select dbms_xplan.display_cursor()\G

HEAP 分区表有主键时查询条件只包含部分主键列
HEAP 分区表有主键时索引情况

结论:

  • HEAP 分区表查询查询条件只有主键列的一部分,所以是对主键索引的扫描,算子是:TABLE RANGE SCAN
    。
  • HEAP 分区表查询走的主键索引扫描,主键索引不是数据,所以还有索引回表操作。
select c1, c2, c4 from heap_pt_with_pk where c1='6864d475-e78c-11f0-a1d1-005056bed6e6' and c4='2025-11-02 05:05:05';
select dbms_xplan.display_cursor()\G

HEAP 分区表有主键时主键查询

结论

  • 当 HEAP 分区表有主键时查询的查询条件是所有主键列时,执行计划算子变为:TABLE GET
     。但是这个主键索引数据应该是跟分区表的分区数据在一起的,所以执行计划算子不是 DISTRIBUTED TABLE GET
    。
  • HEAP 分区表的主键索引也包含有隐藏列 shadow_pk_0
     。
  • HEAP 分区表的主键索引查询也有索引回表逻辑。
  • 对于 HEAP 普通表也是同理,主键索引扫描返回时很有可能也要回表(除非查询列就是主键列的部分或全部。
create unique index heap_pt_with_pk_uk_global on heap_pt_with_pk(c1) global;
select c1, c2, c4 from heap_pt_with_pk where c1='6864d475-e78c-11f0-a1d1-005056bed6e6' ;
select dbms_xplan.display_cursor()\G

HEAP 分区表有主键时创建全局唯一索引
HEAP 分区表有主键时查询走全局唯一索引

结论:

  • HEAP 分区表的全局唯一索引同样包含两个隐藏列(shaodow_pk_0
     出现两次)。这个跟 IOT 分区表不一样。
  • 这个 HEAP 分区表查询走全局唯一索引虽然只返回一笔记录,但要回表。全局索引跟表是不同的 TABLET ,所以执行计划算子是 DISTRIBUTED TABLE GET
     。
create index heap_pt_with_pk_ind1 on heap_pt_with_pk(c2) local;
select c1, c2, c4 from heap_pt_with_pk where c2='Device_0676' and c4 BETWEEN '2025-01-01 00:00:00' and '2025-01-10 00:00:00' ;
select dbms_xplan.display_cursor()\G

HEAP 分区表有主键时查询走本地普通索引

结论:

  • HEAP 分区表有主键时,普通本地索引里包含的是隐藏列 __pk_increment
     而不是主键列。
  • HEAP 分区表普通本地索引跟分区数据在一起,所以执行计划算子是 TABLE RANGE SCAN
     而不是 DISTRIBUTED TABLE RANGE SCAN
     。

全局索引测试

create index iot_pt_ind2_global on iot_pt(c4) parallel4global;
createindex iot_pt_with_pk_ind2_global on iot_pt_with_pk(c4) parallel4global;
createindex heap_pt_ind2_global on heap_pt(c4) parallel4global;
createindex heap_pt_with_pk_ind2_global on heap_pt_with_pk(c4) parallel4global;


select c1, c2, c4 from iot_pt where c4='2025-11-02 05:05:05' limit 2;   
select dbms_xplan.display_cursor()\G 

select c1, c2, c4 from iot_pt_with_pk where c4='2025-11-02 05:05:05' limit 2;   
select dbms_xplan.display_cursor()\G 

IOT 分区表无主键查询走普通全局索引
IOT 分区表有主键时走普通全局索引

结论:

  • IOT 分区表无主键时,普通全局索引包含隐藏列 __pk_increment
     ;IOT 分区表有主键时,普通全局索引包含主键列。
  • 索引创建时开启了并行,所以全局普通索引扫描是索引块内并行扫描。
  • 普通全局索引扫描有回表。
select c1, c2, c4 from heap_pt where c4='2025-11-02 05:05:05' limit 2;
select dbms_xplan.display_cursor()\G 

select c1, c2, c4 from heap_pt_with_pk where c4='2025-11-02 05:05:05' limit 2;
select dbms_xplan.display_cursor()\G 

HEAP 分区表有主键查询走普通全局索引

结论:

  • HEAP 分区表不管有无主键,普通全局索引包含隐藏列 __pk_increment
     。由于索引创建时开了并行,所以执行计划是索引块级别并行扫描。

数据空间对比测试

将 4 各表索引只保留主键索引、本地普通索引、全局唯一索引、全局普通索引。然后数据量都增大到 100 万以上

alter table heap_pt dropkey heap_pt_uk_local;
altertable iot_pt dropkey`iot_pt_uk_local`;
createtable iot_pt_with_pk like iot_pt;
insert/*+ append enable_parallel_dml parallel(4) */into iot_pt_with_pk select * from iot_pt;


insert/*+ append enable_parallel_dml parallel(4) */into iot_pt selectuuid(),c2,c3,c4 from iot_pt;
insert/*+ append enable_parallel_dml parallel(4) */into iot_pt_with_pk selectuuid(),c2,c3,c4 from iot_pt_with_pk;
insert/*+ append enable_parallel_dml parallel(4) */into heap_pt selectuuid(),c2,c3,c4 from heap_pt;
insert/*+ append enable_parallel_dml parallel(4) */into heap_pt_with_pk selectuuid(),c2,c3,c4 from heap_pt_with_pk;


查看各个表和索引的存储空间大小。

WITH table_locs AS (
SELECT
 t.tenant_id,
 t.database_name,
 t.table_id,
 t.table_name,
 t.table_type tablet_type,
 t.tablet_id,
REPLACE(concat(t.table_name,':',t.partition_name,':',t.subpartition_name),':NULL','') tablet_name,
 t.tablegroup_name,
 t.ls_id,
 t.ZONE, 
 t.ROLE,
 t.svr_ip
FROM
 oceanbase.CDB_OB_TABLE_LOCATIONS t
WHERE
 t.data_table_id ISNULL
UNION
SELECT
 i.tenant_id,
 i.database_name,
 i.table_id,
 t.table_name,
 i.table_type tablet_type,
 i.tablet_id,
REPLACE(
REPLACE(concat(i.table_name,':',i.partition_name,':',i.subpartition_name) ,concat('__idx_', i.data_table_id, '_'),'')
  ,':NULL',''
 ) tablet_name,
 i.tablegroup_name,
 i.ls_id,
 i.ZONE, 
 i.ROLE,
 i.svr_ip
FROM
 oceanbase.CDB_OB_TABLE_LOCATIONS i
INNERJOIN oceanbase.__all_virtual_table t ON
 ( i.tenant_id = t.tenant_id
AND i.data_table_id = t.table_id  )
WHERE i.data_table_id ISNOTNULL
)
SELECT
 t.database_name,
 t.ls_id,
 t.ROLE,
 t.svr_ip,
 t.table_name,
concat(t.table_id, ':', t.tablet_id) ids,
 substring_index(t.tablet_name, ':', 1) tablet_name,
-- group_concat(s.table_type,',') tablet_types,
round(sum(s.size)/1024/1024/1024,2) size_gb,
count(*) tablet_cnt
FROM
 table_locs t JOIN oceanbase.GV$OB_SSTABLES s 
ON (t.tenant_id=s.tenant_id AND t.ls_id=s.ls_id AND t.svr_ip=s.svr_ip AND t.tablet_id=s.tablet_id)
WHERE
 t.tenant_id = 1002
AND t.database_name IN ('test')
AND s.table_type NOTIN ('MEMTABLE')
-- AND t.ROLE IN ('LEADER')
GROUPBY
 t.database_name,
 t.ls_id,
 t.ROLE,
 t.svr_ip,
 t.table_name,
 substring_index(t.tablet_name, ':', 1)
ORDERBY
 t.database_name,
 t.ls_id,
 t.ROLE,
 t.svr_ip,
 t.table_name,
 t.tablet_name
;

结论:

  • IOT 表无主键时有隐藏列 __pk_increment
     ;有主键时没有隐藏列,主键即数据(同一个 TABLET )。
  • HEAP 表不管有没有主键,都有隐藏列 __pk_increment
    ,有主键时,主键数据是独立的 TABLET (额外的存储空间),但是跟表分区是在一起的,所以主键索引回表操作都不是分布式执行计划算子。
  • IOT 表无主键时数据大小跟堆表的数据大小一样,IOT 表有主键时数据大小反而要小一点点。

总体结论

综上所述,总结索引组织表(IOT)和堆表(HEAP)特点如下:

  • 堆表的实现原理看起来跟无主键的索引组织表一样,都是使用了内部隐藏列 __pk_increment
     ,不同之处是有主键的时候,堆表的主键跟表数据是分开独立的存储(TABLET
    )。但是,依然是在同一个节点上。堆表的主键索引看起来跟本地分区索引特性是一样的。所以,堆表形式的分区表的主键依然要求主键列包含所有的分区列。堆表形式的分区表的本地唯一约束/索引也是同类要求。
  • 索引组织表的在有主键的时候,普通的索引列会包含主键列信息,并在回表的时候再次扫描主键索引(也就是表数据),无主键的时候则没有这个问题。堆表不管有没有主键,普通索引都是包含隐藏列 __pk_increment  。
  • OB 的表的唯一索引列都会包含至少一个隐藏列 shaodow_pk_0
    ,分区表的本地唯一索引也会包含一个隐藏列 shadow_pk_0
    ,分区表的全局索引包含至少两个隐藏列(shadow_pk_0
     和 shadow_pk_1
    )。关于这个列的存在说法是因为 OB 的索引允许存储 NULL 值,所以需要借助这个字段来确保有 NULL 值时的唯一性。分区表的唯一索引里两个隐藏列,跟隐藏列 __pk_increment
     和分区列有什么关系这个目前文档里没有信息。
  • 索引组织表形式的分区表的无主键表以及堆表形式的分区表(不管有无主键)中的隐藏列 __pk_increment
     特性跟本地唯一索引很向,是分区内部自增数字,不同分区间独立自增,一定有重复数据。这种特性对性能的影响如何在小数据量下看不出来,等以后再用大批量数据做测试看看。
  • OB 分区表的主键和唯一约束要求包含所有分区列,这个跟原生 ORACLE 不一样,实际使用比较别扭。所以个人觉得在有些场景下分区表可以不要主键,将原有主键改为全局唯一索引。不管是索引组织表还是堆表,都可以这么做。不过分区表的全局索引很可能会导致分区表的事务变成分布式事务(还依赖于租户主副本的分布策略 PRIMARY_ZONE 和资源单元数量 UNIT_NUM 的设置)。除此之外是否还有其他问题,欢迎用过的朋友留言讨论。

更多阅读


文章转载自数据库技术闲谈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论