Дизайн бази даних - Зберігати стан або обчислювати стан кожного разу?


17

Скажімо, у мене є реляційна програма бази даних та об’єкт "user" та об'єкт "message". Тепер я хочу показати цьому користувачеві кількість непрочитаних повідомлень.

Який найкращий спосіб архівувати це? Чи потрібно вводити поле користувачеві і рахувати його, якщо користувач отримує повідомлення, і зменшує кількість, якщо він читає його? Або я виконую запит щоразу, щоб обчислити кількість повідомлень для користувача, позначених як непрочитані?

Я думаю, що перший підхід є складнішим та схильним до помилок, але буде краще, ніж другий.

Як це зазвичай робиться чи який кращий підхід?


1
Залежить від ряду факторів: чи розділений ваш БД? Скільки рядків / користувача ви очікуєте? Якого розміру загальної БД ви очікуєте (або скільки всього користувачів)? Скільки запитів за секунду ви очікуєте? Все це не повинно бути точним, але деякі грубі ідеї ...
Омер Ікбал,

10
+1 Це класичне запитання щодо реляційних баз даних. Нормалізувати, чи не нормалізувати? Це питання. Чи це благородник у схемі, щоб зазнати стропів та стріл шаленого дублювання, або взяти на себе тригери та, застосувавши, покінчити з ними?
Росс Паттерсон

Я заперечую, якщо це неприйнятний класичний відн. дб. питання, на сайті вже повинна бути відповідь, це має бути закрито як DUP, або у нас немає відповіді, і це слід залишати відкритим.
mattnz

Відповіді:


14

Як це зазвичай робиться чи який кращий підхід?

Найкращий підхід - спробувати спершу без додаткового поля, виміряти продуктивність, і якщо воно справді виявляється занадто повільним, ви намагаєтеся оптимізувати. Це може означати перехід на ваш перший підхід, використовуючи додаткове поле, але вам слід також розглянути можливість тестування інших варіантів, наприклад, додавання додаткового індексу на комбіновані поля ("непрочитані", "userID") у ваших повідомленнях.


2
Найкращий підхід - це (спочатку перейти з більш простим методом). Загальні правила кращі, ніж конкретизація, підлітка. (+1 за "тест!").
DougM

9

Рішення підручника, що відповідає теорії баз даних, означає, що у вашій базі даних немає значень, які залежать від значень інших даних, оскільки це транзитивні залежності . Наявність полів, які обчислюють значення на основі інших полів, є порушенням нормалізації, оскільки це призводить до надмірної інформації.

Однак іноді те, що йдеться в підручнику, і який найбільш практичний метод на практиці, відрізняються. Підрахунок кількості непрочитаних повідомлень на кожному перегляді сторінки може бути досить дорогою операцією. Зростання числа в user-таблиці було б набагато кращим для продуктивності. Вартість полягатиме в тому, що в базі даних можуть існувати невідповідності. Можливо, повідомлення буде видалено, додано чи прочитано, не пам'ятаючи також про оновлення непрочитаного лічильника.


4
Проблему узгодженості легко злизувати за допомогою тригерів, які регулюють лічильник на INSERTабо DELETE. (Або UPDATEдля врахування зміни власника повідомлення.). Хороша СУБД зробить операцію та запустить тригери в одній транзакції, тому або все, або жодне з них не станеться.
Blrfl

4

Потенційна проблема - це ефективність, але у вас ще немає проблеми з продуктивністю. Ви можете зробити багато речей, залежно від вибору бази даних, щоб вирішити цю проблему у рішенні №1: індексація, апаратне забезпечення, кешування тощо. Все це залежить від того, наскільки часто користувачеві потрібно отримувати поточну кількість непрочитаних повідомлень. Для багатьох з цих варіантів не потрібне спеціальне кодування з боку програми, тому ви можете реалізувати їх із зміною коду або дуже мало. Це полегшує зростання за допомогою програми.

Після того, як користувач підключається / входить в систему, отримання рахунку з бази даних один раз не так вже й погано. Чи буде у вашому додатку постійно оновлюватися список повідомлень, як-от електронна пошта? Звідси отримання непрочитаних даних не потребує чергової поїздки в базу даних, а для отримання нових повідомлень все одно піде поїздка.

Вирушаючи в db щоразу, коли читається повідомлення, щоб позначити IsRead? поля достатньо без перерахунку іншого поля.

З рішенням №2 (ведення рахунку в полі / на диску), чи буде вам потрібна процедура, щоб періодично перебудовувати / переглядати це поле, коли виникає проблема? І проблеми завжди є. Чи збираєтесь ви все це загорнути в транзакцію? Кожен раз, коли хтось надсилає комусь іншому повідомлення, це може бути невдалим, оскільки він не може оновити UnreadCount отримувача через блокування таблиці користувача? Або збираєтесь створити окрему таблицю для цього поля?


+1 за згадування проблем щодо продуктивності з оновленням полів для підрахунку
winkbrace

0

Я б це зробив, виконуючи запит кожен раз, тобто ваш другий підхід. Просто переконайтеся, що ви додаєте індекс у свою таблицю повідомлень у стовпці, який виконує роль стороннього ключа до таблиці користувачів, щоб покращити ефективність вашого запиту.

Потім, як каже Док, виміряйте ефективність такого підходу, і тоді ви зможете сказати, чи потрібно вам піти іншим шляхом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.