From 6b1c388c710bec95dca1b33ae76b2eb37c7085df Mon Sep 17 00:00:00 2001 From: mnoskov Date: Sun, 29 Mar 2026 21:15:04 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=BA=D1=82=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20'=D0=9F=D0=B0=D1=80=D1=82=D0=B8=D1=86=D0=B8=D0=B8'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2. Партиции/practice_partitions.sql | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 mnoskov/Теория БД/1. Ускорение и масштабирование/2. Партиции/practice_partitions.sql diff --git a/mnoskov/Теория БД/1. Ускорение и масштабирование/2. Партиции/practice_partitions.sql b/mnoskov/Теория БД/1. Ускорение и масштабирование/2. Партиции/practice_partitions.sql new file mode 100644 index 0000000..5445669 --- /dev/null +++ b/mnoskov/Теория БД/1. Ускорение и масштабирование/2. Партиции/practice_partitions.sql @@ -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, и запросы с сортировкой по дате или с фильтрацией по интервалам \ No newline at end of file