Я делаю rest-api, который получает список инструкций, которые конкретный поток на сервере должен выполнять в течение 24 часов, назовите это ежедневным расписанием. Эта же инструкция выполняется в течение промежутка времени:
[
{
instructionName: string
args : [
string
...
]
startHh : int
startMm : int
endHh : int
endMm : int
}
...
]
Содержимое args варьируется в зависимости от instructionName.
Расписание должно быть сохранено в MySql. Каждые x секунд поток должен запрашивать у db текущую инструкцию и выполнять некоторую работу.
Моя проблема в том, что я не уверен, как лучше всего сохранить список инструкций в базе данных. На мой взгляд, у меня есть два варианта:
Используя первый подход, все, что мне нужно сделать, это объединить args с одной строкой, а затем напрямую проанализировать json до объекта DTO и сохранить его, я должен быть осторожен, чтобы не хранить имена инструкций и аргументы, которые рабочий поток позже не сможет интерпретировать. Рабочий поток может легко запросить таблицу инструкций и получить текущую инструкцию с учетом временного интервала.
Во втором подходе мне нужно сначала выяснить таблицу с помощью имя_ инструкции, посмотреть, действительны ли аргументы для этой таблицы, а затем вставить ее. Рабочий поток не имеет возможности получить текущую инструкцию простым способом, потому что инструкции разделены в разных таблицах. Когда рабочий поток выясняет, какую таблицу запрашивать, поток может быть уверен, что аргументы сформированы правильно, поскольку они разделены на отдельные столбцы.
В моем приложении будет много типов инструкций, и новые типы инструкций будут постоянно добавляться в течение всего жизненного цикла приложения.
Кажется, что у обоих подходов есть большие проблемы, я не могу найти лучший подход для моего конкретного случая использования. Мне интересно, стоит ли мне вообще использовать реляционную базу данных для этих типов данных.
Любой вклад приветствуется.






Любая модель функционирует при достаточно низком использовании. Но когда использование является значительным, использование DDL, такого как «ALTER TABLE» для добавления новых аргументов, становится если не чрезмерно, то болезненно дорогостоящим. Схемы таблиц должны меняться как можно меньше. Таким образом, я бы предпочел ваш первый вариант, если бы мне пришлось выбирать между двумя.
Достаточно ли это для меня, зависит от того, действительно ли вы хотите запрашивать аргументы и потенциально моделировать высокодинамичные данные, или вам просто нужно место, чтобы вставить какой-то текст и не особо заботиться о том, что в нем.
Если, например, вы хотите иметь возможность ответить на такие вопросы, как «для каких запусков конкретного задания fuelCount был установлен на 3?». Для такого рода вопросов вам нужно будет найти существование fuelCount и его значение из по существу неструктурированной текстовой строки. Для сохранения анализа на сервере потребуется неэлегантная гимнастика, но возвращение каждой строки обратно клиенту mysql для анализа аргументов также неприемлемо для всех наборов данных, кроме самых маленьких.
Другой вариант - использовать Возможности mysql json, если ваша версия базы данных поддерживает его. Это позволяет вам моделировать аргументы, как вы хотите, без необходимости изменять формат таблицы при появлении новых значений. Однако вы должны быть достаточно умными, чтобы старые запросы не ломались при изменении модели. Поддержка json для mysql означает возможность запрашивать данные в json без необходимости извлекать, анализировать и агрегировать все отдельные записи в клиенте базы данных, так что это довольно удобная функция. У Postgres это тоже есть.
Например, вы можете сохранить произвольные данные о команде, которая будет запускаться в столбце runtime JSON, и, пока вы соблюдаете некоторые простые правила, у вас может быть необязательный аргумент для любых аргументов, а также (например) переменные среды, которые также может потребоваться для программы. Со временем могут возникнуть другие параметры среды выполнения, которые заставят вас добавить дополнительные аргументы к определенным заданиям. тип JSON отлично подходит для этого. Если вы хотите запросить JSON, вы можете. Это позволяет вам наложить структуру некоторые на данные (например, все аргументы будут в пределах ключа args словаря верхнего уровня), при этом не нужно заранее определять каждый аргумент, который может быть передан.
Это хорошо проиллюстрировано в приведенной выше ссылке, если идея кажется вам хорошей. Кажется, вы думаете о чем-то вроде json, так что это может быть простой переход. Это дает дополнительное преимущество в том, что он очень удобен для Интернета, как если бы вы создавали REST API, вы, вероятно, в любом случае планируете обменять JSON.
Если вы столкнулись с более старой версией mysql и не можете выйти из нее или иным образом перегружены вашими запросами, я все же предлагаю вам придерживаться первого подхода. Если вы хотите пойти дальше, вы можете добавить таблицу
CREATE TABLE args ( instruction_id int, argkey varchar, argval varchar)
и используйте, например, GROUP_CONCAT, чтобы объединить их вместе, если максимальная длина group_concat не является ограничивающим фактором. В противном случае вы все равно можете объединить их во время выполнения. Мне это кажется неуклюжим, но он хранит данные переменных в строках и позволяет запрашивать данные на стороне сервера.
Спасибо за подробный ответ, я просто смотрел на тип данных JSON для переменной args. Похоже, это было бы хорошо для моего варианта использования из-за динамического характера данных. Использование первого варианта также обеспечивает хорошее сопоставление между таблицей базы данных и строкой JSON, полученной в остальном api.
о да, это также очень удобно для Интернета. Должен был упомянуть об этом. Спасибо
In my application there are going to be many types of instructions and new instruction types will be added continuously during the lifetime of the application.
Какое решение выбрать, немного зависит от ваших определений много и непрерывно. База данных очень хорошо справляется с запросами к существующим данным. Это довольно хорошо для изменения сохраненных данных и добавления новых данных. Плохо менять макет базы данных. Поэтому вам следует избегать изменения макета.
Если изменение непрерывно означает несколько раз в день, я бы не советовал создавать таблицу для каждого приложения.
Даже в этом случае, если приложения много означают тысячи конфигураций приложений / параметров, то таблица для каждого приложения приведет к тысячам таблиц, что довольно нежелательно.
С другой стороны, если вы выберете свой первый метод, то, как вы заявили, вам придется позаботиться о правильном хранении команды и ее параметров.
Если у вас есть какой-то шаблон репозитория, который заботится о вариантах использования вашей проблемы, вы можете позволить репозиторию проверять параметры, прежде чем они будут сохранены в вашей базе данных.
void ScheduleBackupTask(TimeSpan startTime, TimeSpan stopTime, ... <backup parameters>)
{
// check the parameter list, to see if they match the parameters of a backup task
// and create the command
var command = CreateBackupCommand(<backup parameters>);
ScheduleCommand(startTime, stopTime, command);
}
void ScheduleCleaningTask(TimeSpan startTime, TimeSpan stopTime, <cleaning parameters>)
{
// check the parameter list, to see if they match the parameters of a clean task
// and create the command
var command = CreateCleanCommand(<cleaning parameters>);
ScheduleCommand(startTime, stopTime, command);
}
void ScheduleCommand(TimeSpan startTime, TimeSpan stopTime, Command command)
{
using (var dbContext = new MyDbContext()
{
Schedule schedule = new Schedule(startTime, stopTime, command);
dbContext.Schedules.Add(shedule);
dbContext.SaveChanges();
}
}
Каждый раз, когда вам нужно будет поддерживать новую команду или изменять параметры команды, вам придется создать или изменить функцию Create...Command. Есть только одно место, где вам нужно будет проверить параметры.
Даже если бы вы выбрали второе решение, вам потребовалась бы функция, которая проверила бы ваши параметры и поместила бы их в правильном порядке. Так что ваше второе решение не помогло бы.
Очевидно, что запросить, какие команды должны быть выполнены, проще и быстрее при использовании вашего первого метода. После того, как вы получили команду, включая commandType, ее легко выполнить:
IEnumerable<Command> commandsToExecute = FetchCommandsToExecute(TimeSpan time);
foreach (Command command in commandsToExecute)
{
switch (command.CommandType)
{
case CommandType.Backup:
ExecuteBackup(...);
break;
case CommandType.Clean:
ExecuteClean(...);
break;
}
}
Очевидно, вам придется изменить переключатель при поддержке новой команды. Однако во втором решении вам также придется изменить функцию выполнения.
Summarized: if you think of a lot of commands to support, regularly changing parameters or kind of commands to support, may advice would be to have one table containing all commands to support. Let your repository pattern check the parameters before adding / updating / executing
Спасибо за подробный ответ, в котором также рассказывается, как выполнить инструкцию. Тип инструкции не изменится после того, как она будет введена, формат аргументов всегда будет оставаться неизменным для этого типа инструкции. Это больше касается простой поддержки новых типов инструкций. Но все же я последую вашему совету. В моем случае лучше всего не разделять типы инструкций в разных таблицах.