Идея инструмента SaaS состоит в том, чтобы иметь динамические c таблицы с динамическими c настраиваемыми полями и значениями различных типов, мы думали использовать пример «force.com/salesforce.com», но, похоже, слишком сложный, чтобы продолжать двигаться вперед, а также создавать некоторые отчеты с огромным уровнем абстракции, поэтому мы пришли к простой идее, но должны быть уверены, что это хороший подход.
Это архитектура, которую мы иметь сегодня (за несколько шагов).
- У каждого арендатора есть своя отдельная база данных в кластере (Postgres 12).
- Таблица TABLE, используемая для хранения всех этих таблиц в качестве ссылки, этот объект имеет отношение ManyToOne к таблице META и отношение OneToMany с таблицей DATA.
- Таблица META используется для настройки метаданных, имеет отношение OneToMany с FIELDS (в котором есть имя полей, а также тип поле, например
TEXT/INTEGER/BOOLEAN/DATETIME
et c. и значение атрибута - в виде строки, только в качестве ссылки). - Таблица DATA имеет отношение ManyToOne к TABLES и
50 character varying
столбцы с именами, такими как: attribute1...50
, которые могут иметь значение NULL.
Пример потока сегодня:
- Когда пользователь хочет открыть ТАБЛИЦУ ДАННЫХ, например " АВТОМОБИЛИ ", мы загружаем таблицу META со всеми ПОЛЯМИ (чтобы получить поля для этого запроса). Пользователь указал, что он хочет выполнить запрос к:
Brand, Class, Year, Price
столбцам. - Мы проверяем по логи c, ссылку на бренд, класс, год и цену в таблице META> FIELDS, поэтому мы знаем, что
Brand = attribute2, Class = attribute 5, Year = attribute6 and Price = attribute7
. - Мы анализируем его запрос в запросе, например:
SELECT [attr...2,5,6,7] FROM DATA
, и затем показываем результаты пользователю, если пользователь решает применить к нему некоторые фильтры, основываясь на этих данных, например, Year > 2017 AND Class = 'A'
, которые мы используем CAST()
функциональность SQL
, например SELECT CAST(attribute6 AS int) AND attribute5 FROM DATA WHERE CAST(attribute6 AS int) > 2017 AND attribute5 = 'A';
, поэтому мы можем фактически поддерживать большинство принципов SQL.
Однако, продвигаясь вперед, мы немного напуганы:
- Управляйте такой средой для большего количества арендаторов, в то время как у нас будет больше таблиц
(e.g. 50 per customer, with roughly 1-5 mil per TABLE (
5mil - это максимум, который мы допускаем, для больших данных у нас есть BigQuery ) which is giving us 50-250 mil rows in single table DATA_X)
, который может повлиять на производительность запросов, особенно когда мы дали возможности управления простыми операторами WHERE (меньше, равно, ноль и т. д. c.) с использованием некоторого языка абстракции, например, GET CARS [BRAND,CLASS,PRICE...] FILTER [EQ(CLASS,A),MT(YEAR,2017)]
, разработанного, чтобы быть похожим на JQL (язык запросов Jira). - Блокировка транзакций, как w мы разрешаем пакетную загрузку CSV в
DATA_X
, поэтому, как только они захотят загрузить, например, 1 ГБ данных, он как бы блокирует таблицу для доступа других систем к таблице DATA. - Сохранение нескольких столбцов NULL, которые могут повлиять немного (пока мы не так напуганы, как при создании TABLE, клиент может решить, сколько столбцов он хочет, поэтому на основании этого мы присваиваем этот TABLE одной из жестко закодированных сущностей
DATA_5, DATA_10, DATA_15, DATA_20, DATA_30, DATA_50
, где числа соответствуют ограничениям столбцы атрибутов, и эти объекты различны, мы также поддерживаем опцию миграции, если они решают переключиться с 5 на 10 атрибутов и т. д. c.
Мы находимся на очень ранней стадии, поэтому мы можем / должны сделайте это, прежде чем масштабировать, поскольку мы знали, что , скорее всего, не лучший подход, но мы сохранили его, чтобы запустить проект для небольших клиентов, который сейчас работает просто отлично.
Мы также думали об объектах JSONB, но это не вариант, так как мы хотим упростить получение данных.
Что вы думаете об этом решении (к вашему сведению, DATA имеет первичный ключ из 2 таблиц - (ID, TABLEID) и встроенный столбец CreatedAt, который используется для большинства запросов, поэтому максимум будет 3 индекса)?
Если это покажется плохим, что бы вы порекомендовали в качестве альтернативы этому решению, основываясь на подробностях, которыми я поделился (в основном СУБД без схемы)?