практика 'Партиции'

This commit is contained in:
mnoskov
2026-03-29 21:15:04 +03:00
parent 783c875925
commit 6b1c388c71
@@ -0,0 +1,190 @@
/* Задание 1. Какой вид партиционирования выбрать
1)
* range по полю order_date
* запросы с between в год
1. where order_date between '2022-01-01' and '2023-01-01'
2. where extract('year' from order_date) = '2025'
3. where order_date < '2026-01-01' and order_date > '2025-01-01'
2)
* тут два типа партиционирования подходят. range по created_at или list по country.
* если выбирать list по country то плюс - это при фильтрах по стране будут отсекаться лишние партиции, а минус, что год/месяц регистрации не будет разделен по партициям.
3)
* логично range по created_at - чтобы разделить по месяцам/годам, а hash по user_id - почему так, точно сказать не могу, но в теории сказано что hash используется когда нет диапазона или списка и это частый сценарий для логов
* с range выиграют запросы с фильтрами по created_at, а с hash выигают запросы с фильтрами по user_id, а все что не указаны как поле для партиций, не почувствуют разницы.*/
/* Задание 2. Размер партиций
2) количество партициый за 10 лет по:
* годам - 10, тут вообще все четко, и удалять не сложно, и поддержка проще, и запросы будут хорошо делаться
* месяцам - 120, тут с удалением тарых данных труднее, поддержка тяжелая, но скорость запросов будет лучше, если фильтрация будет по месяцам
* дням - примерно 3650, а это я чувствую пиздец, с удалением и поддержкой жопа, и со скоростью запросов тоже жопа - данные будут читаться из кучи партиций что не есть хорошо
3)
Я думаю прям для очень большого магазина оптимальнее будет партиционирование по месяцам - тк очень много покупателей и большинство статистик будет по месяцам, по годам тоэе конечно, но думаю приоритетнее по месяцам*/
-- Задание 3. RANGE‑партиции по дате заказов
DROP TABLE IF EXISTS orders_part;
CREATE TABLE orders_part (
id bigint,
user_id bigint,
order_date timestamptz NOT NULL,
status varchar(20),
PRIMARY KEY (id, order_date),
FOREIGN KEY (user_id) REFERENCES users_1(id)
) PARTITION BY RANGE (order_date);
CREATE TABLE orders_2024 PARTITION OF orders_part
FOR VALUES FROM ('2024_01-01') TO ('2025-01-01');
CREATE TABLE orders_2025 PARTITION OF orders_part
FOR VALUES FROM ('2025_01-01') TO ('2026-01-01');
CREATE TABLE orders_2026 PARTITION OF orders_part
FOR VALUES FROM ('2026_01-01') TO ('2027-01-01');
-- сделал insert который попросил сгенерировать нейронку
EXPLAIN ANALYZE
SELECT
*
FROM
orders_part
WHERE
order_date BETWEEN '2025-01-01' AND '2025-12-31 23:59:59'; -- пришлось сделать так, тк дата 2026-01-01 тоже есть в таблице
-- в этом запросе за год - читатется только одна партиция - orders_2025, НО если в условии все же написать AND '2026-01-01', то читаются две партиции orders_2025 и orders_2026 и время выполнения запроса намного больше тк выполняется full scan обеих таблиц
EXPLAIN ANALYZE
SELECT
*
FROM
orders_part
WHERE
EXTRACT('year' FROM order_date) IN ('2024', '2025'); -- так читаются все три партиции, 2026 почему-то тоже захватывается, но по итогу после сканирование у таблицы orders_2026 - rows = 0
EXPLAIN ANALYZE
SELECT
*
FROM
orders_part;
-- так просто сканируются все три партиции, но без фильтров, как было в предидущем варианте
-- Задание 4. LIST‑партиции по стране пользователей
DROP TABLE IF EXISTS users_part;
CREATE TABLE users_part (
id int8 NOT NULL,
"name" varchar(100) NOT NULL,
age int2 NULL,
email varchar(100) NULL,
country bpchar(2) NULL,
is_active bool NULL,
created_at timestamptz NOT NULL,
PRIMARY KEY (id, country)
) PARTITION BY LIST (country);
CREATE TABLE users_ru PARTITION OF users_part
FOR VALUES IN ('RU');
CREATE TABLE users_us PARTITION OF users_part
FOR VALUES IN ('US');
CREATE TABLE users_gb PARTITION OF users_part
FOR VALUES IN ('GB');
CREATE TABLE users_de PARTITION OF users_part
FOR VALUES IN ('DE');
CREATE TABLE users_fr PARTITION OF users_part
FOR VALUES IN ('FR');
CREATE TABLE users_jp PARTITION OF users_part
FOR VALUES IN ('JP');
CREATE TABLE users_uk PARTITION OF users_part
FOR VALUES IN ('UK');
CREATE TABLE users_kz PARTITION OF users_part
FOR VALUES IN ('KZ');
-- сделал insert который попросил сгенерировать нейронку
EXPLAIN ANALYZE
SELECT
*
FROM
users_part
WHERE
country = 'RU'
-- запрос скаинровал только партицию users_ru
EXPLAIN ANALYZE
SELECT
*
FROM
users_part
WHERE
country IN ('RU', 'KZ')
-- запрос сканировал только партиции users_ru и users_kz
EXPLAIN ANALYZE
SELECT
*
FROM
users_part
WHERE
age BETWEEN 44 AND 66;
-- сканировал не просто users_part, а каждую партицию по отдельности
-- Задание 5. HASH‑партиции по user_id в логах
DROP TABLE IF EXISTS user_logs_part;
CREATE TABLE user_logs_part (
id int8,
user_id bigint NOT NULL,
created_at timestamp NOT null,
event_type text NOT NULL,
payload jsonb,
PRIMARY KEY (id, user_id)
) PARTITION BY hash (user_id)
CREATE TABLE user_logs_p1 PARTITION OF user_logs_part
FOR VALUES WITH (modulus 4, remainder 0);
CREATE TABLE user_logs_p2 PARTITION OF user_logs_part
FOR VALUES WITH (modulus 4, remainder 1);
CREATE TABLE user_logs_p3 PARTITION OF user_logs_part
FOR VALUES WITH (modulus 4, remainder 2);
CREATE TABLE user_logs_p4 PARTITION OF user_logs_part
FOR VALUES WITH (modulus 4, remainder 3);
-- сделал insert который попросил сгенерировать нейронку
EXPLAIN ANALYZE
SELECT * FROM user_logs_part WHERE user_id = 228;
-- читает только user_logs_p2
EXPLAIN ANALYZE
SELECT * FROM user_logs_part WHERE created_at >= current_date - INTERVAL '7 days';
-- читает все партиции
-- Задание 6
-- email, чтобы искать пользователя быстрее, created_at для сортировки и where
CREATE UNIQUE INDEX idx_users_ru_email ON users_ru(email);
CREATE INDEX idx_users_ru_created_at ON users_ru(created_at);
CREATE UNIQUE INDEX idx_users_us_email ON users_us(email);
CREATE INDEX idx_users_us_created_at ON users_us(created_at);
CREATE UNIQUE INDEX idx_users_gb_email ON users_gb(email);
CREATE INDEX idx_users_gb_created_at ON users_gb(created_at);
CREATE UNIQUE INDEX idx_users_de_email ON users_de(email);
CREATE INDEX idx_users_de_created_at ON users_de(created_at);
CREATE UNIQUE INDEX idx_users_fr_email ON users_fr(email);
CREATE INDEX idx_users_fr_created_at ON users_fr(created_at);
CREATE UNIQUE INDEX idx_users_jp_email ON users_jp(email);
CREATE INDEX idx_users_jp_created_at ON users_jp(created_at);
CREATE UNIQUE INDEX idx_users_uk_email ON users_uk(email);
CREATE INDEX idx_users_uk_created_at ON users_uk(created_at);
CREATE UNIQUE INDEX idx_users_kz_email ON users_kz(email);
CREATE INDEX idx_users_kz_created_at ON users_kz(created_at);
-- должны ускориться запросы с поиском пользователей по email, и запросы с сортировкой по дате или с фильтрацией по интервалам