postgres 中的行级安全性 (RLS) 性能明显较慢。

Posted

技术标签:

【中文标题】postgres 中的行级安全性 (RLS) 性能明显较慢。【英文标题】:Row level security(RLS) performance is significantly slower in postgres. 【发布时间】:2017-05-02 09:31:36 【问题描述】:

说明: 这是性能问题的示例演示。

我们首先创建了两个表,启用了行级安全性并创建了策略。

表定义:

create table sample_schema.sample_table1(ID numeric(38) PRIMARY KEY NOT NULL,
                 tenant_id VARCHAR(255) NOT NULL,
                 Description VARCHAR(255)
        );

create table sample_schema.sample_table2(ID2 numeric(38) PRIMARY KEY NOT NULL,
                 tenant_id VARCHAR(255) NOT NULL,
                 table1_id numeric (38),
                 Description2 VARCHAR(255)
        );    

索引创建:

CREATE UNIQUE INDEX sample_table1_idx1 ON sample_schema.sample_table1(tenant_id,id);            

启用行级安全性:

ALTER TABLE sample_schema.sample_table1 ENABLE ROW LEVEL SECURITY;   

创建角色:

CREATE ROLE tenant_grp_role_p_id;    

创建策略:我想要一个策略来选择其中tenant_id 列值具有与已登录用户相同的角色的数据。

CREATE POLICY Tenant_Roles ON  sample_schema.sample_table1 TO tenant_grp_role_p_id USING ((tenant_id) IN ( SELECT rolname FROM pg_roles WHERE    pg_has_role( current_user, oid, 'member')));

创建样本数据:

insert into sample_schema.sample_table1 values (1,'user1_tenant1',1,'Table1 Data');
insert into sample_schema.sample_table2 values (2,'user1_tenant1',1,'Table2 Data');

问题:下面的查询没有使用主键索引。

SELECT * FROM sample_schema.sample_table1 ST1,  sample_schema.sample_table2 T2 WHERE ST1.id = ST2.table1_id  AND ST1.id = 1;    

问题:如果我禁用 RLS 则使用主键索引。为什么启用 RLS 时不使用主键索引扫描?

注意: A.如果我禁用行级安全性并运行上述查询,它将使用索引。 B.下面是禁用低级安全性时的解释计划输出。

Nested Loop  (cost=0.29..19.19 rows=1 width=1129)  ->  Index Scan using sample_table1_pkey on sample_table1 st1  (cost=0.29..8.30 rows=1 width=37)
    Index Cond: (id = '1'::numeric)  ->  Seq Scan on sample_table2 st2  (cost=0.00..10.88 rows=1 width=1092)        Filter: (table1_id = '1'::numeric);    

C.如果我启用低级别安全性并运行它不使用索引的查询。 以下是启用低级别安全性时的解释计划输出。

 Nested Loop  (cost=1.03..946.65 rows=79 width=1129) ->  Seq Scan on sample_table2 st2  (cost=0.00..10.88 rows=1 width=1092)  Filter: (table1_id = '1'::numeric)  ->  Subquery Scan on st1  (cost=1.03..934.98 rows=79 width=37)
    Filter: (st1.id = '1'::numeric)        ->  Hash Join  (cost=1.03..738.11 rows=15750 width=37)              Hash Cond: ((st1_1.tenant_id)::name = pg_authid.rolname)              ->  Seq Scan on sample_table1 st1_1  (cost=0.00..578.00 rows=31500 width=37)              ->  Hash  (cost=1.01..1.01 rows=1 width=68)                    ->  Seq Scan on pg_authid  (cost=0.00..1.01 rows=1 width=68)                          Filter: pg_has_role("current_user"(), oid, 'member'::text);   

请帮我解决这个问题..

【问题讨论】:

请保留执行计划的格式和缩进。您将它们添加到问题中的方式使它们毫无用处。 这个和这个问题一样:***.com/questions/41169479/… 【参考方案1】:

有关详细信息,请参阅 pgsql-general 邮件列表上的 this message thread。

我最近将 RLS 应用于几个大型(数百万行)表 在我的 9.5 数据库中,并注意到针对单个大型 RLS 的查询 受保护的表执行良好,但是连接几个大的查询 RLS 保护的表性能很差。解释计划显示 优化器正在扫描整个表以执行 RLS 策略 在执行会减少查询的主键连接之前 结果到每个表的单行。显然性能将是 如果它在策略检查之前执行连接会更好。

据我所知,RLS 实施力求执行 在用户提供谓词检查之前进行策略检查,以避免 泄露受保护的数据。

以及回应:

目前,使用 RLS 的联接案例并未得到很好的优化。有 正在努力改进这一点 - 见 https://www.postgresql.org/message-id/flat/8185.1477432701%40sss.pgh.pa.us - 但它不会在 v10 之前投入生产。

还有:

您可以使用同一用户拥有的安全屏障视图 下面的表归其所有,这将绕过 RLS 表本身,因此您需要实现 安全屏障视图中的适当 quals。

所以你可以等待 PG10,或者尝试使用 security barrier view 代替。那篇博文还解释了为什么 Postgres 不尝试结合(和优化)安全条件和用户指定的条件:自定义函数可用于泄露原本对用户隐藏的值。

要创建这样的视图,只需在定义中添加with (security_barrier)

rhaas=# create or replace view unclassified_emp with (security_barrier) as
        select * from emp where organization <> 'CIA';
CREATE VIEW

this detailed blog post 也有更多信息。

【讨论】:

在 PG 10 中,参考消息中提到的改进现已生效:github.com/postgres/postgres/commit/… 我有类似的问题。升级到 postgres 10 会有很大的不同。

以上是关于postgres 中的行级安全性 (RLS) 性能明显较慢。的主要内容,如果未能解决你的问题,请参考以下文章

Postgres 规范化表上的行级安全性

JOOQ 中的行级安全性实现

PostgreSQL 行级安全性涉及与其他表的外键

为啥没有为 Postgres 视图启用行级安全性?

PostgreSQL 行级安全性涉及视图或带有连接的选择

启用/禁用 RLS 和授予/撤销权限有啥区别