当前位置: 首页 > 面试题库 >

启用RLS(行级安全性)时,PostgreSQL查询未使用INDEX

杨志强
2023-03-14
问题内容

我正在使用 PostgreSQL 10.1 ,直达关键点…

可以说我有一张table :

CREATE TABLE public.document (
    id uuid PRIMARY KEY,

    title   text,
    content text NOT NULL
);

连同上面的 GIN INDEX

CREATE INDEX document_idx ON public.document USING GIN(
    to_tsvector(
        'english',
        content || ' ' || COALESCE(title, '')
    )
);

和一个基本的全文搜索查询:

SELECT * FROM public.document WHERE (
    to_tsvector(
        'english',
        content || ' ' || COALESCE(title, '')
    ) @@ plainto_tsquery('english', fulltext_search_documents.search_text)
)

无论 public.document 表的大小如何,查询都非常快(您已经知道了)!计划人员使用INDEX,一切都很好。

现在,我通过 RLS(行级安全性) 介绍一些基本的访问控制,首先启用它:

ALTER TABLE public.document ENABLE ROW LEVEL SECURITY;

然后添加策略:

CREATE POLICY document_policy ON public.document FOR SELECT
    USING (EXISTS (
        SELECT 1 FROM public.user WHERE (is_current_user) AND ('r' = ANY(privileges))
    ));

为了简单 起见,is_current_user 是另一个查询,它精确地检查了这一点。

现在, 全文搜索查询 将通过 document_policy查询进行 平整,这样计划人员将执行 Seq扫描 而不是
索引扫描, 从而使查询速度降低300倍!

我认为问题很明显,如何解决这个问题,以便全文搜索查询保持快速?

提前致谢!


问题答案:

我已经从发布之日起解决了这个问题…任何面临此问题的人,这就是我的做法:

我的解决方案是拥有一个包含“ propper”查询的 私有* SECURITY DEFINER“包装器”函数,以及另一个调用该 私有
函数和需要访问控制的表的 公共 函数。
*INNER JOINS

因此,在上面的特定情况下,将是这样的:

CREATE FUNCTION private.filter_document() RETURNS SETOF public.document AS
$$
    SELECT * FROM public.document WHERE (
        to_tsvector(
            'english',
            content || ' ' || COALESCE(title, '')
        ) @@ plainto_tsquery('english', fulltext_search_documents.search_text)
    )
$$
LANGUAGE SQL STABLE SECURITY DEFINER;
----
CREATE FUNCTION public.filter_document() RETURNS SETOF public.document AS
$$
    SELECT filtered_d.* FROM private.filter_documents() AS filtered_d
        INNER JOIN public.document AS d ON (d.id = filtered_d.id)
$$
LANGUAGE SQL STABLE;

由于我使用的是Postgraphile(
超级棒的 BTW!),因此我可以省去 私有
模式的自省,使“危险”功能无法访问!通过适当的安全性实现,最终用户将只能看到最终的GraphQL模式,从而将 Postgres 从外界中删除。

这工作得很漂亮! 直到最近 发布并修复 Postgres 10.3 时,才不再需要这种hack。

另一方面,我的RLS策略非常复杂,嵌套并且非常深入。它们再次运行的表也很大(总共要运行50,000多个条目才能针对RLS运行)。即使采用了超级复杂和嵌套的策略,我也设法将性能保持在合理的范围内。

使用RLS时,请记住以下几点:

  1. 建立适当的 INDEXES
  2. 在任何地方都首选内联查询 !(即使这意味着重写N次相同的查询)
  3. 务必避免使用策略中的功能!如果您绝对必须在内部安装它们,请确保它们STABLE高且高COST(例如@mkurtz指出);或者是IMMUTABLE
  4. 从策略中提取查询,直接与查询一起运行,EXPLAIN ANALYZE并尝试尽可能对其进行优化

希望你们能像我一样找到有用的信息!



 类似资料:
  • 问题内容: 我需要严格控制Postgres数据的读写。可更新的视图始终能够很好,严格地控制我的数据读取,并允许我添加有价值的计算列。在Postgres 9.5中,行级安全性引入了一种新的强大的方法来控制我的数据。但是我不能同时使用两种技术视图和行级安全性。为什么? 问题答案: 基本上是因为不可能追溯更改视图的工作方式。我希望能够支持(或等效)视图,但是据我所知,目前尚不存在这样的功能。 您可以使用

  • PostgreSQL 使用用户和组管理数据库访问权限。用户拥有数据库对象(例如:数据库)及可以在这些对象上指定权限给其他用户,以控制谁人访问到哪些对象。 注意:由 PostgreSQL 8.1 版本开始,用户和组不再是不同的实体,现在只有角色。任何角色可以作为一个用户、一个组,或两者皆可。角色的概念是包含用户和组的概念。 只有超级用户(用户拥有全部权利)可以新建或删除用户。在默认情况下,Postg

  • 首先,我是Spring启动框架的新手。我已经在执行器上工作了几天,并且能够设置endpoint来监控系统。当我集成JWT以确保安全性时,我的所有执行器endpoint都坏了。 如何禁用位于Spring启动安全性之上的执行器endpoint的JWT安全性?以下是我的application.yml文件属性。

  • 问题内容: 可以说我有这样的第一张桌子 分支表 第二张桌子是这样的 余额表 我想查询余额表,其中每一行包含分支表的名称。例如分支表中的“ 123456ABC”,我想从余额表中获取“ ABC”行 我怎样才能做到这一点?到目前为止,我还没有尝试过这个查询 有什么建议吗? 问题答案: 你应该 转换 了的名称模式: 联接看起来更易读:

  • 问题内容: 我有一个PostgreSQL函数,可以将查询结果返回到pgadmin结果网格中。在内部,这是一个简单的函数,使用a连接到另一个数据库并返回查询,以便我可以简单地运行 它的运行就像基本的表查询一样。 问题是当我使用该子句时。因此,我想运行以下查询,但它要花很多时间: 我怎样才能加快速度?有什么比这种情况下的子句快的吗? 问题答案: 不是表-可能是一些存储过程,因此查询并不是很简单。您需要

  • 问题内容: 我希望表中的行仅可由组成员访问。我创建用户并通过以下方法将其添加到组中, 然后,我编写的策略使其仅对当前用户可用。但是我需要整个小组来访问它。 pgUser命名可以访问该行的用户。我希望将pgUser列替换为pgRole,其中提到了组的名称,该组的成员可以访问该特定行。任何使行可被整个组访问的提示或方法都是值得赞赏的。 问题答案: 这似乎可行: