Масштабирование базы данных через шардирование и...
TRANSCRIPT
Масштабирование базы данныхчерез шардирование ипартиционированиеИванов Денис
2gis.ru
2GIS WebAPI
Данные
• 8 стран
• 250 городов
• 50 тыс населенных пунктов
• 5 млн организаций
• 50 млн гео-объектов
И еще много всего
3
Нагрузка
• 25 млн. пользователей в месяц
• 2000 RPS
• 3 датацентра
4
Много данных
Долгие запросы
Масштабирование БД как решениепроблемы
• Шардинг
• Вертикальный
• Горизонтальный
• Репликация
7
Партицирование(вертикальный шардинг)
Горизонтальный шардинг
Репликация
1. Партицирование
CREATE TABLE news (
id bigint not null,
category_id int not null,
author character varying not null,
rate int not null,
title character varying
)
01.
02.
03.
04.
05.
06.
07.
15
CREATE TABLE news_1 (
) INHERITS (news)
01.
02.
16
Наследованная таблица• Будет:
• Все колонки от родителя
• "Свои" колонки
• Не будет:
• Ограничений (Constraint)
• Индексов (Index)
• Триггеров (Trigger)
17
CREATE TABLE news_1 (
CHECK ( category_id = 1 )
) INHERITS (news)
01.
02.
03.
18
Строгое значение
CHECK ( author = 'Denis' )
Список значений
CHECK ( author IN ('Denis', 'Anton', 'Oleg') )
Диапазон значений
CHECK ( rate > 100 AND rate <= 200 )
19
CREATE TABLE news_rate_100_200 (
CHECK ( rate BETWEEN 100 AND 200 )
) INHERITS (news)
CREATE TABLE news_rate_200_300 (
CHECK ( rate BETWEEN 200 AND 300 )
) INHERITS (news)
01.
02.
03.
04.
05.
06.
07.
20
CREATE TABLE news_rate_100_200 (
CHECK ( rate > 100 AND rate <= 200 )
) INHERITS (news)
CREATE TABLE news_rate_201_300 (
CHECK ( rate > 200 AND rate <= 300 )
) INHERITS (news)
01.
02.
03.
04.
05.
06.
07.
21
CREATE TABLE news_1 (
CHECK ( category_id = 1 )
) INHERITS (news)
CREATE TABLE news_rate_100 (
CHECK ( rate = 100 )
) INHERITS (news)
01.
02.
03.
04.
05.
06.
07.
22
CREATE TABLE news_1 (
CHECK ( category_id = 1 )
) INHERITS ( news )
01.
02.
03.
23
CREATE RULE news_insert_to_ 1 AS ON INSERT TO news
WHERE ( category_id = 1 )
DO INSTEAD INSERT INTO news_1 VALUES (NEW.*)
01.
02.
03.
24
CREATE TABLE news_1 (
CHECK ( category_id = 1 )
) INHERITS ( news )
CREATE TABLE news_2 (
CHECK ( category_id = 2 )
) INHERITS ( news )
01.
02.
03.
04.
05.
06.
07.
25
CREATE RULE news_insert_to_ 1 AS ON INSERT TO news
WHERE ( category_id = 1 )
DO INSTEAD INSERT INTO news_1 VALUES (NEW.*)
CREATE RULE news_insert_to_ 2 AS ON INSERT TO news
WHERE ( category_id = 2 )
DO INSTEAD INSERT INTO news_2 VALUES (NEW.*)
01.
02.
03.
04.
05.
06.
07.
26
INSERT INTO news
(id, category_id , title, author, rate)
VALUES
(1, 1 , 'Моя новость #1', 'Ivan', 1)
INSERT INTO news
(id, category_id , title, author, rate)
VALUES
(2, 2 , 'Моя новость #2', 'Oleg', 1)
01.
02.
03.
04.
01.
02.
03.
04.
27
INSERT INTO news
(id, category_id , title, author, rate)
VALUES
(3, 3 , 'Моя новость #3', 'Petr', 1)
01.
02.
03.
04.
28
SELECT * FROM news
id category_id title author rate
1 1 Моя новость #1 Ivan 1
2 2 Моя новость #2 Oleg 1
3 3 Моя новость #3 Petr 1
29
SELECT * FROM news WHERE category_id = 1
id category_id title author rate
1 1 Моя новость #1 Ivan 1
SELECT * FROM news WHERE category_id IN (2, 3)
id category_id title author rate
2 2 Моя новость #2 Oleg 1
3 3 Моя новость #3 Petr 1
30
SELECT * FROM news_1
id category_id title author rate
1 1 Моя новость #1 Ivan 1
SELECT * FROM news_2
id category_id title author rate
2 2 Моя новость #2 Oleg 1
31
SELECT * FROM news_3
ERROR: relation "news_3" does not exist
SELECT * FROM ONLY news
id category_id title author rate
3 3 Моя новость #3 Petr 1
32
INSERT INTO news_1
(id, category_id , title, author, rate)
VALUES
(4, 1 , 'Моя новость #4', 'Ivan', 1)
01.
02.
03.
04.
33
INSERT INTO news_1
(id, category_id , title, author, rate)
VALUES
(4, 4 , 'Моя новость #4', 'Ivan', 1)
ERROR: new row for relation " news_1 " violates
check constraint " news_1_category_id_check "
01.
02.
03.
04.
01.
02.
34
INSERT INTO news
(id, category_id , title, author, rate)
VALUES
(5, 1 , 'Моя новость #5', 'Ivan', 1),
(6, 2 , 'Моя новость #6', 'Oleg', 1),
(7, 3 , 'Моя новость #7', 'Petr', 1)
01.
02.
03.
04.
05.
06.
35
SELECT * FROM news WHERE category_id = 1 LIMIT 1
id category_id title author rate
1 1 Моя новость #1 Ivan 1
SELECT * FROM news_1 LIMIT 1
id category_id title author rate
1 1 Моя новость #1 Ivan 1
36
EXPLAIN ANALYZE
SELECT * FROM news WHERE category_id = 1
...
-> Seq Scan on news ...
Filter: (category_id = 1)
-> Seq Scan on news_1 ...
Filter: (category_id = 1)
...
01.
02.
01.
02.
03.
04.
05.
06.
37
EXPLAIN ANALYZE
SELECT * FROM news_1
...
Seq Scan on news_1
...
01.
02.
01.
02.
03.
38
Точно также:• UPDATE
• DELETE
39
CREATE INDEX news_rate_idx ON news (rate)
CREATE INDEX news_1_rate_idx ON news_1 (rate)
CREATE INDEX news_2_rate_idx ON news_2 (rate)
40
PartitionMagic
CREATE TABLE news (
id bigint not null,
category_id int not null,
author character varying not null,
rate int not null,
title character varying
)
01.
02.
03.
04.
05.
06.
07.
42
_2gis_partition_magic(' news ', ' category_id ')
43
INSERT INTO news
(id, category_id , title, author, rate)
VALUES
(1, 1 , 'Моя новость #1', 'Ivan', 1),
(2, 2 , 'Моя новость #2', 'Oleg', 1),
(3, 3 , 'Моя новость #3', 'Petr', 1),
(4, 1 , 'Моя новость #4', 'Ivan', 1),
(5, 1 , 'Моя новость #5', 'Ivan', 1),
(6, 2 , 'Моя новость #6', 'Oleg', 1),
(7, 3 , 'Моя новость #7', 'Petr', 1)
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
44
SELECT * FROM news WHERE category_id = 1
id category_id title author rate
1 1 Моя новость #1 Ivan 1
5 1 Моя новость #5 Ivan 1
SELECT * FROM news_1
id category_id title author rate
1 1 Моя новость #1 Ivan 1
5 1 Моя новость #5 Ivan 1
45
Преимущества:
• Создание недостающих партиций при вставке
• Актуальная структура таблиц:
• Колонки
• Индексы
• Проверки
• Триггеры
46
https://github.com/2gis/partition_magic
2. Горизонтальный шардинг
CREATE TABLE news (
id bigint not null,
category_id int not null
CONSTRAINT category_id_check CHECK (category_id = 1 ),
author character varying not null,
rate int not null,
title character varying
);
01.
02.
03.
04.
05.
06.
07.
08.
50
CREATE INDEX news_category_id_idx ON news
USING btree( category_id );
01.
02.
51
CREATE EXTENSION postgres_fdw;
CREATE SERVER news_1_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host ' 127.0.0.1 ', port ' 5432 ', dbname ' news_1 ');
CREATE USER MAPPING FOR postgres
SERVER news_1_server
OPTIONS (user ' postgres ', password ' postgres ');
01.
02.
03.
04.
05.
06.
07.
08.
09.
52
CREATE FOREIGN TABLE news_1 (
id bigint not null,
category_id int not null,
author character varying not null,
rate int not null,
title character varying
)
SERVER news_1_server
OPTIONS (schema_name ' public ', table_name ' news ')
01.
02.
03.
04.
05.
06.
07.
08.
09.
53
CREATE VIEW news AS
SELECT * FROM news_1
UNION ALL
SELECT * FROM news_2
01.
02.
03.
04.
54
CREATE RULE news_insert AS ON INSERT TO news
DO INSTEAD NOTHING;
CREATE RULE news_update AS ON UPDATE TO news
DO INSTEAD NOTHING;
CREATE RULE news_delete AS ON DELETE TO news
DO INSTEAD NOTHING;
01.
02.
03.
04.
05.
06.
07.
08.
55
CREATE RULE news_insert_to_1 AS ON INSERT TO news
WHERE ( category_id = 1 )
DO INSTEAD INSERT INTO news_1 VALUES (NEW.*);
CREATE RULE news_insert_to_2 AS ON INSERT TO news
WHERE ( category_id = 2 )
DO INSTEAD INSERT INTO news_2 VALUES (NEW.*);
01.
02.
03.
04.
05.
06.
07.
56
INSERT INTO news
(id, category_id , title, author, rate)
VALUES
(1, 1 , 'Моя новость #1', 'Ivan', 1),
(2, 2 , 'Моя новость #2', 'Oleg', 1),
(3, 3 , 'Моя новость #3', 'Petr', 1),
(4, 1 , 'Моя новость #4', 'Ivan', 1),
(5, 1 , 'Моя новость #5', 'Ivan', 1),
(6, 2 , 'Моя новость #6', 'Oleg', 1),
(7, 3 , 'Моя новость #7', 'Petr', 1)
01.
02.
03.
04.
05.
06.
07.
08.
09.
10.
57
SELECT * FROM news
id category_id title author rate
1 1 Моя новость #1 Ivan 1
4 1 Моя новость #4 Ivan 1
5 1 Моя новость #5 Ivan 1
2 2 Моя новость #2 Oleg 1
6 2 Моя новость #6 Oleg 1
58
SELECT * FROM news WHERE category_id = 2
id category_id title author rate
2 2 Моя новость #2 Oleg 1
6 2 Моя новость #6 Oleg 1
SELECT * FROM news_2
id category_id title author rate
2 2 Моя новость #2 Oleg 1
6 2 Моя новость #6 Oleg 1
59
EXPLAIN ANALYZE
SELECT * FROM news
...
-> Foreign Scan on news_1
-> Foreign Scan on news_2
...
01.
02.
01.
02.
03.
04.
60
Подведём итоги• Партицирование
• Просто
• Один сервер
• Прирост производительности в 3-4 раза
• Горизонтальный шардинг
• Немного сложнее партицирования
• Разные сервера для разных шардов
61