1.Base基础/3.Icon图标/操作/search备份
1.Base基础/3.Icon图标/操作/search备份
EN
文档
关于AntDB
部署与升级
快速入门
使用教程
SQL语言
Oracle兼容
驱动使用说明
运维
调优
工具和插件
高级服务
数据安全
参考
  • 文档首页 /
  • 使用教程 /
  • 分布式功能 /
  • 辅助索引表

辅助索引表

更新时间:2024-07-01 14:39:47

使用场景

很多分布式场景下,一个分片字段并不能满足所有的场景。

比如历史业务账单按用户 ID 来分片很适合做用户统计,但还有一些情况需要按订单号查询。这种情况下为了找到一个订单对应的记录,因为不知这条记录在哪个节点上,所以则需要全节点扫描。这里就无法体现分布式的优点,造成资源浪费(有效工作节点只有一个),特别是在高并发时,就很可能会造成 CPU 不够用。

基于这种情况,AntDB 的辅助表功能可以有效的应对。

原理

假如有表 t1(a,b,c,...),a 为分片列,b 为上面场景中的“订单”列。

  1. 针对非分片列 b 增加一张辅助索引表。辅助索引表的分片列为 b,和非分片字段 a,所有的数据都与主表 t1 保持同步。
  2. 如果查询 t1 表时包含的过滤条件中有 “b=xxx” 这样的表达式,则把xxx带入到对应的辅助索引表中,查询出列 a 的值所在的节点。
  3. 修改执行计划,只在上一步查找出的节点上执行查询。

最终的执行时间分三种情况:

  • 一般情况:本需要全节点扫描的查询,现在只需要在两个节点上扫描。单次查询时间变长,高并发查询时间变短。
  • 最优情况:第二步查询不到符合条件的数据,则不需要再扫描主表。单次查询时间不变,高并发查询时间变短。
  • 最坏情况:第二步查到的数据在所有节点上,仍需要全节点扫描主表。单次查询时间变长,高并发查询时间变长。

所以创建辅助索引表和创建索引相同,要找那些重复率低的列。否则最终反而会使查询时间变长。

因为有辅助表的存在,主表数据的更新操作会变的更慢(主表数据更新时会同步更新辅助表)。

用例

创建主表

CREATE TABLE tb(id int, name text, age int) DIESTRIBUTE BY hash(id);

name 上创建辅助索引

CREATE auxiliary TABLE ON tb(name);
\d+ tb
antdb=# \d+ tb
                                    Table "public.tb"
 Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description
--------+---------+-----------+----------+---------+----------+--------------+-------------
 id     | integer |           |          |         | plain    |              |
 name   | text    |           |          |         | extended |              |
 age    | integer |           |          |         | plain    |              |
Auxiliary table:
    "tb_name_aux" on tb(name)
DISTRIBUTE BY HASH(id) TO NODE(dn1, dn2, dn3)
Access method: heap

在主表 tb 上插入一些数据

INSERT INTO tb SELECT n,'name'||n,random()*100 FROM generate_series(1,10) AS n;
SELECT *,adb_node_oid() AS node FROM tb;
  id |  name  | age | node
----+--------+-----+-------
  2 | name2  |  87 | 16388
  6 | name6  |  76 | 16388
  7 | name7  |  77 | 16388
  1 | name1  |  70 | 16389
  4 | name4  |  29 | 16389
  8 | name8  |  16 | 16389
  9 | name9  |  95 | 16389
 10 | name10 |  66 | 16389
  3 | name3  | 100 | 16387
  5 | name5  |  12 | 16387
(10 rows)

非分片字段等值执行计划

--查询结果
SELECT * FROM tb WHERE name='name2';
 id | name  | age
----+-------+-----
  2 | name2 |  87
(1 row)

--执行计划:name为非分片字段,最终执行计划只在16388这个节点上执行
EXPLAIN (verbose,analyze,costs off,timing off) SELECT * FROM tb WHERE name='name2';
                      QUERY PLAN
-------------------------------------------------------
 Cluster Gather (actual rows=1 loops=1)
   Remote node: 16388
   ->  Tid Scan on public.tb (actual rows=0 loops=1)
         Output: id, name, age
         TID Cond: (tb.ctid = '(0,1)'::tid)
         Filter: (tb.name = 'name2'::text)
         Remote node: 16388
         Node 16388: (actual rows=1 loops=1)
 Planning Time: 0.728 ms
 Execution Time: 1.664 ms
(10 rows)

--关闭使用辅助表功能后的执行计划
SET use_aux_type =off;
EXPLAIN (verbose,analyze,costs off,timing off) SELECT * FROM tb WHERE name='name2';
                                       QUERY PLAN
-----------------------------------------------------------------------------------------
 Data Node Scan on "__REMOTE_FQS_QUERY__" (actual rows=1 loops=1)
   Output: tb.id, tb.name, tb.age
   Primary node/s: dn1
   Node/s: dn1, dn2, dn3
   Remote query: SELECT id, name, age FROM public.tb tb WHERE (name = 'name2'::text)
 Planning Time: 0.083 ms
 Execution Time: 9.138 ms
(7 rows)

--对in表达式同样支持,两条记录在同一个节点上
SET use_aux_type =on;
EXPLAIN (verbose,analyze,costs off,timing off) SELECT * FROM tb WHERE name IN('name2','name7');
                            QUERY PLAN
------------------------------------------------------------------
 Cluster Gather (actual rows=2 loops=1)
   Remote node: 16388
   ->  Tid Scan on public.tb (actual rows=0 loops=1)
         Output: id, name, age
         TID Cond: (tb.ctid = ANY ('{"(0,1)","(0,3)"}'::tid[]))
         Filter: (tb.name = ANY ('{name2,name7}'::text[]))
         Remote node: 16388
         Node 16388: (actual rows=2 loops=1)
 Planning Time: 11.996 ms
 Execution Time: 1.516 ms
(10 rows)

--两条记录在两个不同节点上
EXPLAIN (verbose,analyze,costs off,timing off) SELECT * FROM tb WHERE name IN('name2','name5');
                            QUERY PLAN
------------------------------------------------------------------
 Cluster Gather (actual rows=2 loops=1)
   Remote node: 16387,16388
   ->  Tid Scan on public.tb (actual rows=0 loops=1)
         Output: id, name, age
         TID Cond: (tb.ctid = ANY ('{"(0,1)","(0,2)"}'::tid[]))
         Filter: (tb.name = ANY ('{name2,name5}'::text[]))
         Remote node: 16387,16388
         Node 16387: (actual rows=1 loops=1)
         Node 16388: (actual rows=1 loops=1)
 Planning Time: 1.465 ms
 Execution Time: 2.651 ms
(11 rows)

语法

创建辅助索引表的语法为:

CREATE AUXILIARY TABLE [auxiliary_table_name]
ON master_table_name ( column_name [ index_options ])
[ TABLESPACE tablespace_name ]
[ DISTRIBUTE BY OptDistributeType ]
[ TO NODE (node_name [, ...] ) | TO GROUP pgxcgroup_name ]

index_options:
/* empty */
| INDEX [ CONCURRENTLY ] [ name ] [ USING method ]
    [ WITH ( storage_parameter = value [, ... ] ) ]
    [ TABLESPACE tablespace_name ]

说明:
AUXILIARY 为辅助表关键字
auxiliary_table_name 为辅助表表名,可选,若不指定则按照:主表名+辅助字段名+tbl 命名。
master_table_name 为主表表名,必选。
column_name 为主表字段名(需要建辅助表的字段),必选。
index_options 辅助表索引信息,指定时按照指定语法创建索引。未指定时,则按照默认参数创建索引。(辅助表的column_name字段必然创建索引。默认时,索引方法默认btree,命名空间与辅助表保持一致。)
tablespace_name 辅助表命名空间,可选。未指定时,为当前默认tablespace。
DISTRIBUTE BY OptDistributeType 辅助表分片方式,可选。未指定时,以辅助字段HASH分片。
TO NODE (node_name [, ...] ) | TO GROUP pgxcgroup_name 辅助表分片节点,可选。未指定时,默认ALL DATANODES。
问题反馈