172 похожих чатов

Добрый день, коллеги. В словах могу путаться, так как новичок, не

бейте палками 😂

Делаю структуру БД для микросервиса технической поддержки.
Можете, пожалуйста, посмотреть - правильно ли сделал.

У текущей реализации есть некоторые проблемы
1. Вносить данные нужно с помощью транзакций, так как при создании тикета на сайте мне нужно заполнить несколько таблиц - создать тикет, создать сообщение в тикете, обновить статус тикета, назначить менеджера на тикет. Если хотя бы одно из этих действий в бд не прошло - от остальных толку нет.
2. При выводе пользователю его тикетов вместе со статусом и прочими данным в запосе будет слишком много JOIN'ов.

Правильно / логично составлена схема?
Что можно перестроить, чтобы сократить кол-во JOIN'ов?

Если вы знаете готовые варианты схем для технической поддержки - поделитесь, пожалуйста.

15 ответов

12 просмотров

А почему бы не хранить заполнении тикета в Redis, а в psql сохранять уже готовый тикет?

Alex-Sinyaev Автор вопроса
Dmitry Chexkh
А почему бы не хранить заполнении тикета в Redis, ...

Что вы имеете ввиду под "заполнении тикета в Redis"?

Alex Sinyaev
Что вы имеете ввиду под "заполнении тикета в Redis...

Пользователь вводит первое поле - клиентская апка срочно бежит сохранять это дело в редис. Сущностью анкеты в пг это пока не является. Кэш, всего лишь временный кэш, незаполненная инпрогресс тикета. Хранить часов 150 и хорош. Клиент прервался - вышел. Зашел снова - спрашиваем данные из хранилища. Нету - идем в поисках инпрогресс анкеты в редис. находим - вываливаем данные в апку. Пользователь продолжает заполнение. Закончил заполнение анкеты? Отправляем все на сервер, который сохраняет уже в psql. Меньше сущностей, связей и тд

Alex-Sinyaev Автор вопроса
Dmitry Chexkh
Пользователь вводит первое поле - клиентская апка ...

Теперь понял, спасибо. Я у себя в примере не рассматриваю незаполненные тикеты. Пример - создание тикета. При создании тикета пользователь должен указать первое сообщение и приложить файлы. Предположим, что всё успешно и валидно. Что теперь нужно сделать: 1. создать запись в таблице ticket 2. создать запись в таблице ticket_message 3. создать запись в таблице ticket_message_attachmet 4. создать запись в таблице ticket_ticket_status 5. создать запись в таблице ticket_manager все действия внутри транзакции

Dmitry Chexkh
Пользователь вводит первое поле - клиентская апка ...

зачем в редис? можно же просто в localStorage браузера когда все поля заполнены, тогда уже в сеть идти (редис/бд там или апи бэкенда)

1) Как часто вы хотите менять систему рейтингов и статусов? Я как-то сомневаюсь, что вы будете редактировать ticket_status, mime_type и ticket_rating через админку чаще, чем раз в год - два. 2) Почему вам нужно прямо сразу кого-то назначить на задачу? Ситуация "все операторы заняты" всё же вполне реальна. 3) Не вижу поля "вот сейчас вашей задачей занимается Петя" ) Лишние соединения можно срезать уже на стороне приложения. Например, можно пройтись по выбранным из базы сообщениям и сделать SELECT * FROM ticket_message_attachment WHERE ticket_message_id IN (<список сообщений на экране>). ticket_status, mime_type и ticket_rating включать их в JOIN смысла нет - таблицы из < 100 записей лучше целиком в память утащить один раз и использовать где надо.

Alex-Sinyaev Автор вопроса
Евгений Смирнов
1) Как часто вы хотите менять систему рейтингов и ...

Спасибо за ответ)) 1. Да, редко. Скорее всего только в самом начале. 2. Не обязательно, чтобы одному менеджеру был назначен один тикет. Можно назначить сколько угодно - для этого я и сделал промежуточную таблицу. А метка времени там для того, чтобы переназначать менеджеров на тикеты. То есть сейчас занимается тикетом тот у кого самая последняя метка времени. 3. Я думал это сделать через JOIN к таблице ticket_manager Если честно - не очень пронял про лишние соединения. Можете, пожалуйста, уточнить. Про таблицы из < 100 записей понял, спасибо. Так и сделаю.

Alex Sinyaev
Спасибо за ответ)) 1. Да, редко. Скорее всего тол...

По лишним соединениям - иногда несколько независимых SELECT будут быстрее JOIN. Например, как вы себе представляете самую жирную цепочку JOIN'ов?

Alex-Sinyaev Автор вопроса
Евгений Смирнов
По лишним соединениям - иногда несколько независим...

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

Alex Sinyaev
Самая жирная будет при отображении пользователю ег...

Поскольку ticker_id для нас константа, я бы делал так: 1. SELECT FROM ticket_manager WHERE ticket_id [ORDER BY created_at FETCH FIRST 1 WITH TIES] 2. SELECT FROM ticket_ticket_status WHERE ticket_id 3.1. SELECT ticket_message JOIN user WHERE ticket_id, если у вас менеджер - тоже пользователь, или просто SELECT ticket_message т.к. автор заявки и так всё про себя знает. 3.2. собираете уникальные ticket_message_id из 3.1 и делаете SELECT FROM ticket_message_attachment Не знаю как вы, а я вижу только один JOIN, с ним любая база справится на счёт "раз".

чем нарисована эта схема?

Alex-Sinyaev Автор вопроса

Похожие вопросы

Обсуждают сегодня

30500 за редактор? )
Владимир
47
а через ESC-код ?
Alexey Kulakov
29
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
Добрый день! Скажите пожалуйста, а какие программы вы бы рекомендовали написать для того, чтобы научиться управлять памятью? Можно написать динамический массив, можно связный ...
Филипп
7
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
14
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Ребят в СИ можно реализовать ООП?
Николай
33
Как передать управляющий символ в открытую через CreateProcess консоль? Собсна, есть процедура: procedure TRedirectThread.WriteData(Data: OEMString); var Written: Cardinal;...
Serjone
6
https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
Всем привет! Имеется функция: function IsValidChar(ch: UTF8Char): Boolean; var i: Integer; ValidChars: AnsiString; begin ValidChars := 'abcdefghijklmnopqrstuvwxyzABCDE...
Евгений
44
Карта сайта