【发布时间】:2021-07-28 15:44:49
【问题描述】:
我目前正在开发一个 Postgresql 数据库,我们在其中实施了多租户基础架构。
这是它的工作原理:
我们有几个表(table1、tables2、...),我们在其中添加了 tenant 列。此列用于根据不同的数据库用户过滤行。我们有几个用户(tenant1、tenant2、...)和一个超级用户(没有应用租户)。
我想优化以下简单查询:
SELECT id
FROM table
WHERE UPPER("table"."column"::text) = UPPER('blablabla')
因此,我创建了一个函数索引:
CREATE INDEX "upper_idx" ON "table" (upper("column") );
如果我以superuser 身份连接到数据库并执行我的 SELECT 查询,它会运行平稳且快速。
Bitmap Heap Scan on table (cost=71.66..9225.47 rows=2998 width=4)
Recheck Cond: (upper((column)::text) = 'blablabla'::text)
-> Bitmap Index Scan on upper_idx (cost=0.00..70.91 rows=2998 width=0)
Index Cond: (upper((column)::text) = 'blablabla'::text)
但是,当我以tenant1 连接时,索引没有被拾取,而数据库运行顺序扫描:
Gather (cost=1000.00..44767.19 rows=15 width=4)
Workers Planned: 2
-> Parallel Seq Scan on table (cost=0.00..43765.69 rows=6 width=4)
Filter: (((tenant)::text = (CURRENT_USER)::text) AND (upper((column)::text) = 'blablabla'::text))
你知道如何让它在这种情况下工作吗?
编辑 - 添加解释(分析,缓冲区)
Gather (cost=1000.00..44767.19 rows=15 width=4) (actual time=502.601..503.466 rows=0 loops=1)
Workers Planned: 2
Workers Launched: 2
Buffers: shared hit=781 read=36160
-> Parallel Seq Scan on table (cost=0.00..43765.69 rows=6 width=4) (actual time=498.978..498.978 rows=0 loops=3)
Filter: (((tenant)::text = (CURRENT_USER)::text) AND (upper((column)::text) = 'blablabla'::text))
Rows Removed by Filter: 199846
Buffers: shared hit=781 read=36160
Planning Time: 1.650 ms
Execution Time: 503.510 ms
EDIT 2 - 添加了(截断的)CREATE TABLE 语句
-- public.table definition
-- Drop table
-- DROP TABLE public.table;
CREATE TABLE public.table (
id serial NOT NULL,
created timestamptz NOT NULL,
modified timestamptz NOT NULL,
...
column varchar(100) NULL,
"tenant" tenant NOT NULL,
...
);
...
CREATE INDEX upper_idx ON public.table USING btree (upper((column)::text), tenant);
CREATE INDEX table_column_91bdd18f ON public.table USING btree (column);
CREATE INDEX table_column_91bdd18f_like ON public.table USING btree (column varchar_pattern_ops);
...
-- Table Triggers
create trigger archive_deleted_rows after
delete
on
public.table for each row execute procedure archive.archive('{id}');
create trigger set_created_modified before
insert
or
update
on
public.table for each row execute procedure set_created_modified();
create trigger set_tenant before
insert
or
update
on
public.table for each row execute procedure set_tenant();
-- public.table foreign keys
...
编辑 3 - \d table 的转储
Table "public.table"
Column | Type | Collation | Nullable | Default
------------------------------------------+--------------------------+-----------+----------+--------------------------------------------
id | integer | | not null | nextval('table_id_seq'::regclass)
........
column | character varying(100) | | |
tenant | tenant | | not null |
........
Indexes:
"table_pkey" PRIMARY KEY, btree (id)
..........
"table__column_upper_idx" btree (upper(column::text), tenant)
"table_column_91bdd18f" btree (column)
"table_column_91bdd18f_like" btree (column varchar_pattern_ops)
.........
Check constraints:
.........
Foreign-key constraints:
.........
Referenced by:
.........
Policies:
POLICY "tenant_policy"
TO tenant1,tenant2
USING (((tenant)::text = (CURRENT_USER)::text))
Triggers:
........
set_tenant BEFORE INSERT OR UPDATE ON table FOR EACH ROW EXECUTE PROCEDURE set_tenant()
编辑 4 - 添加租户数据类型
CREATE TYPE tenant AS ENUM (
'tenant1',
'tenant2');
【问题讨论】:
-
你跑
ANALYZE "table";了吗? -
@LaurenzAlbe 是的,同样的输出
-
请同时添加
CREATE TABLE声明。 -
我添加了声明
-
难以置信。
psql中的表的\d输出是什么样的?
标签: postgresql query-optimization multi-tenant database-indexes