Skip to content

Схемы реляционных баз данных ​

1. Концепция Database-per-Service в архитектуре платформы ​

Микросервисная платформа «UrFU HeatBalance» строго следует паттерну Database-per-Service (изолированная база данных для каждого микросервиса). В качестве основной системы управления базами данных используется PostgreSQL 16.

Ключевые преимущества архитектурного решения: ​

  1. Полная независимость развёртывания и миграций: изменение схемы таблиц IoTService не требует остановки или блокировки базы DatasetService.
  2. Исключение распределённых взаимоблокировок (Deadlocks): аналитические выборки и фоновые очереди не влияют на скорость записи телеметрических пакетов.
  3. Изоляция сбоев: аварийное переполнение журнала логов телеметрии не препятствует аутентификации пользователей через AuthService.

2. Комплексная ER-диаграмма реляционных моделей ​


3. Спецификация схем баз данных и SQL DDL ​

3.1. База данных heat_balance_datasets ​

Хранит нормализованные описания агрегатов и сохранённые технологические карты.

Структура таблицы InputDataSets: ​

КолонкаТип данныхNullableОграничения / ИндексыОписание
IdUUIDНетPRIMARY KEYУникальный идентификатор датасета
OwnerIdVARCHAR(450)НетИндекс IX_InputDataSets_OwnerId_NameИдентификатор владельца (пользователя)
NameVARCHAR(200)НетИндекс IX_InputDataSets_OwnerId_NameПользовательское название датасета
CalculationTypeINTEGERНет—Тип печи: 1..4
CommentVARCHAR(4000)НетDEFAULT ''Описание технологического режима
SchemaVersionINTEGERНетDEFAULT 1Версия схемы сериализованного JSON
InputJsonTEXTНет—Сериализованные входные физические параметры
CreatedAtTIMESTAMPTZНет—Метка времени создания записи (UTC)
UpdatedAtTIMESTAMPTZНет—Метка времени последней модификации (UTC)

Сценарий генерации DDL (PostgreSQL): ​

sql
CREATE TABLE IF NOT EXISTS "InputDataSets" (
    "Id" uuid NOT NULL,
    "OwnerId" character varying(450) NOT NULL,
    "Name" character varying(200) NOT NULL,
    "CalculationType" integer NOT NULL,
    "Comment" character varying(4000) NOT NULL DEFAULT '',
    "SchemaVersion" integer NOT NULL DEFAULT 1,
    "InputJson" text NOT NULL,
    "CreatedAt" timestamp with time zone NOT NULL,
    "UpdatedAt" timestamp with time zone NOT NULL,
    CONSTRAINT "PK_InputDataSets" PRIMARY KEY ("Id")
);

CREATE INDEX IF NOT EXISTS "IX_InputDataSets_OwnerId_Name" 
ON "InputDataSets" ("OwnerId", "Name");

3.2. База данных heat_balance_runs ​

Обеспечивает долгосрочный аудит выполненных теплотехнических вычислений.

Структура таблицы CalculationRuns: ​

КолонкаТип данныхNullableИндексыОписание
IdUUIDНетPRIMARY KEYИдентификатор запуска вычисления
InputDataSetIdUUIDНетIX_CalculationRuns_InputDataSetId_ExecutedAtЛогическая ссылка на базовый датасет
OwnerIdVARCHAR(450)НетIX_CalculationRuns_OwnerIdИдентификатор пользователя
ExecutedAtTIMESTAMPTZНетIX_CalculationRuns_InputDataSetId_ExecutedAtТочное время выполнения расчёта
ResultJsonTEXTНет—Полный результат теплового баланса (JSON)
NotesVARCHAR(2000)Да—Заметки оператора к результатам

Сценарий генерации DDL: ​

sql
CREATE TABLE IF NOT EXISTS "CalculationRuns" (
    "Id" uuid NOT NULL,
    "InputDataSetId" uuid NOT NULL,
    "OwnerId" character varying(450) NOT NULL,
    "ExecutedAt" timestamp with time zone NOT NULL,
    "ResultJson" text NOT NULL,
    "Notes" character varying(2000) NULL,
    CONSTRAINT "PK_CalculationRuns" PRIMARY KEY ("Id")
);

CREATE INDEX IF NOT EXISTS "IX_CalculationRuns_InputDataSetId_ExecutedAt" 
ON "CalculationRuns" ("InputDataSetId", "ExecutedAt");

CREATE INDEX IF NOT EXISTS "IX_CalculationRuns_OwnerId" 
ON "CalculationRuns" ("OwnerId");

3.3. База данных heat_balance_iot ​

Содержит конфигурации полевого оборудования, токены безопасности, входящую телеметрию и историю групповых вычислений.

Таблица Devices (Реестр датчиков): ​

sql
CREATE TABLE IF NOT EXISTS "Devices" (
    "Id" uuid NOT NULL,
    "OwnerId" text NOT NULL,
    "Name" text NOT NULL,
    "CalculationType" integer NOT NULL,
    "DatasetId" uuid NOT NULL,
    "SensorType" integer NOT NULL DEFAULT 0,
    "AccessToken" text NOT NULL,
    "CreatedAt" timestamp with time zone NOT NULL,
    "LastSeenAt" timestamp with time zone NULL,
    CONSTRAINT "PK_Devices" PRIMARY KEY ("Id"),
    CONSTRAINT "AK_Devices_AccessToken" UNIQUE ("AccessToken")
);

Таблица DeviceGroups (Технологические группы): ​

sql
CREATE TABLE IF NOT EXISTS "DeviceGroups" (
    "Id" uuid NOT NULL,
    "OwnerId" text NOT NULL,
    "Name" text NOT NULL,
    "CalculationType" integer NOT NULL,
    "DatasetId" uuid NOT NULL,
    "DeviceIds" uuid[] NOT NULL,
    "CreatedAt" timestamp with time zone NOT NULL,
    CONSTRAINT "PK_DeviceGroups" PRIMARY KEY ("Id")
);

Таблица TelemetryLogs (Журнал телеметрии датчиков): ​

sql
CREATE TABLE IF NOT EXISTS "TelemetryLogs" (
    "Id" uuid NOT NULL,
    "DeviceId" uuid NOT NULL,
    "Timestamp" timestamp with time zone NOT NULL,
    "PayloadJson" text NOT NULL,
    "Status" integer NOT NULL DEFAULT 0,
    "CalculationRunId" uuid NULL,
    "Efficiency" double precision NULL,
    "ResultJson" text NULL,
    "ErrorMessage" text NULL,
    CONSTRAINT "PK_TelemetryLogs" PRIMARY KEY ("Id"),
    CONSTRAINT "FK_TelemetryLogs_Devices_DeviceId" 
        FOREIGN KEY ("DeviceId") REFERENCES "Devices" ("Id") ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS "IX_TelemetryLogs_DeviceId_Timestamp" 
ON "TelemetryLogs" ("DeviceId", "Timestamp");

Таблица GroupTelemetryLogs (Журнал групповых балансов): ​

sql
CREATE TABLE IF NOT EXISTS "GroupTelemetryLogs" (
    "Id" uuid NOT NULL,
    "GroupId" uuid NOT NULL,
    "Timestamp" timestamp with time zone NOT NULL,
    "Status" integer NOT NULL DEFAULT 0,
    "MergedPayloadJson" text NULL,
    "ResultJson" text NULL,
    "Efficiency" double precision NULL,
    "ErrorMessage" text NULL,
    CONSTRAINT "PK_GroupTelemetryLogs" PRIMARY KEY ("Id"),
    CONSTRAINT "FK_GroupTelemetryLogs_DeviceGroups_GroupId" 
        FOREIGN KEY ("GroupId") REFERENCES "DeviceGroups" ("Id") ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS "IX_GroupTelemetryLogs_GroupId_Timestamp" 
ON "GroupTelemetryLogs" ("GroupId", "Timestamp");

4. Регламент миграций и резервного копирования ​

Автоматическое применение миграций (Code-First) ​

Каждый микросервис при инициализации в контейнере Docker запускает автоматическое создание и обновление таблиц через механизм Entity Framework Core:

csharp
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<IoTDbContext>();
await dbContext.Database.MigrateAsync();

Резервное копирование баз данных ​

Для обеспечения катастрофоустойчивости на сервере настраивается автоматическое резервное копирование с использованием утилиты pg_dump:

bash
pg_dump -h localhost -U postgres -d heat_balance_datasets -F c -b -v -f "/backup/datasets_$(date +%Y%m%d_%H%M%S).dump"
pg_dump -h localhost -U postgres -d heat_balance_runs -F c -b -v -f "/backup/runs_$(date +%Y%m%d_%H%M%S).dump"
pg_dump -h localhost -U postgres -d heat_balance_iot -F c -b -v -f "/backup/iot_$(date +%Y%m%d_%H%M%S).dump"

Глубина хранения инкрементальных и полных бэкапов в цеховом архиве составляет 90 календарных дней.

Разработано в рамках проекта моделирования тепловых балансов и промышленного мониторинга