Безопасность на уровне строк, низкая производительность - PullRequest
0 голосов
/ 05 мая 2018

Я оцениваю возможности использования возможностей PostgreSQL Row Level Security (RLS) для мягкого удаления клиентов. К сожалению, у меня проблемы с низкой производительностью. Вот простая настройка теста в PostgreSQL версии 9.5.10:

Таблица, содержащая 10 000 000 клиентов:

CREATE TABLE customers (
    customer_id integer PRIMARY KEY,
    name text,
    hidden boolean DEFAULT FALSE
);

INSERT INTO customers (customer_id, name) SELECT generate_series(0, 9999999), 'John Doe';
ANALYZE customers;

Таблица, содержащая один заказ для каждого клиента:

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    customer_id integer REFERENCES customers (customer_id)
);

INSERT INTO orders (order_id, customer_id) SELECT generate_series(0, 9999999), generate_series(0, 9999999);
ANALYZE orders;

Ненадежный пользователь, который будет выполнять только SELECT:

CREATE ROLE untrusted;
GRANT SELECT ON customers TO untrusted;
GRANT SELECT ON orders TO untrusted;

Политика, которая делает скрытых клиентов невидимыми для недоверенного пользователя:

CREATE POLICY no_hidden_customers ON customers FOR SELECT TO untrusted USING (hidden IS FALSE);
ALTER TABLE customers ENABLE ROW LEVEL SECURITY;

Простой тестовый запрос: как зовут клиента, который сделал заказ, с order_id = 4711?

Без RLS:

EXPLAIN ANALYZE SELECT name FROM orders JOIN customers USING (customer_id) WHERE order_id = 4711;
                                                           QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.87..16.92 rows=1 width=9) (actual time=0.121..0.123 rows=1 loops=1)
   ->  Index Scan using orders_pkey on orders  (cost=0.43..8.45 rows=1 width=4) (actual time=0.078..0.078 rows=1 loops=1)
         Index Cond: (order_id = 4711)
   ->  Index Scan using customers_pkey on customers  (cost=0.43..8.45 rows=1 width=13) (actual time=0.039..0.040 rows=1 loops=1)
         Index Cond: (customer_id = orders.customer_id)
 Planning time: 0.476 ms
 Execution time: 0.153 ms
(7 rows)

С RLS:

EXPLAIN ANALYZE SELECT name FROM orders JOIN customers USING (customer_id) WHERE order_id = 4711;
                                                           QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=8.46..291563.48 rows=1 width=9) (actual time=1.494..2565.121 rows=1 loops=1)
   Hash Cond: (customers.customer_id = orders.customer_id)
   ->  Seq Scan on customers  (cost=0.00..154055.00 rows=10000000 width=13) (actual time=0.010..1784.086 rows=10000000 loops=1)
         Filter: (hidden IS FALSE)
   ->  Hash  (cost=8.45..8.45 rows=1 width=4) (actual time=0.015..0.015 rows=1 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 9kB
         ->  Index Scan using orders_pkey on orders  (cost=0.43..8.45 rows=1 width=4) (actual time=0.012..0.013 rows=1 loops=1)
               Index Cond: (order_id = 4711)
 Planning time: 0.358 ms
 Execution time: 2565.170 ms
(10 rows)

Как можно избежать последовательного сканирования при присоединении к таблице? Я перепробовал каждый индекс, который смог придумать. Безрезультатно.

1 Ответ

0 голосов
/ 06 мая 2018

Рекомендую обновить до последней версии Postgres 10.3. Начиная с версии 9.5 были сделаны значительные улучшения в отношении производительности функций защиты на уровне строк. Например, проверьте это улучшение, которое доступно только с Postgres 10.0: https://github.com/postgres/postgres/commit/215b43cdc8d6b4a1700886a39df1ee735cb0274d

Я не думаю, что имеет смысл пытаться оптимизировать запросы RLS в Postgres 9.5, поскольку тогда это была очень новая функция и еще не была оптимизирована для производительности. Просто обновите.

...