Чим глобалісти відрізняються від бази даних?


250

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

Коли ваша програма працює з даними з бази даних, вам не байдуже, чи змінює їх інший код у вашій системі чи навіть якщо зовсім інша програма змінює її. Вам байдуже, що це за дані; у цьому вся суть. Важливо лише те, що ваш код правильно поводиться з даними, з якими він стикається. (Очевидно, я переглядаю часто тернисту проблему кешування тут, але давайте на даний момент ігноруємо це.)

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


117
Приємно бачити, що учасники ветеранів трохи кидають виклик догмам ...
svidgen

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

45
Глобальний стан - це як мати єдину базу даних з єдиною таблицею з єдиним рядком з нескінченно великою кількістю стовпців, до яких одночасно звертається довільна кількість програм.
BevynQ

42
Бази даних також є злими.
Stig Hemmer

27
Забавно "перевернути" аргумент, який ви тут висловлюєте, і піти в інший бік. Структура, яка має вказівник на іншу структуру, логічно є лише зовнішнім ключем в одному рядку однієї таблиці, який переходить до іншого рядка іншої таблиці. Як працювати з будь-яким кодом, у тому числі з пов'язаними списками, що відрізняються від маніпулювання даними в базі даних? Відповідь: це не так. Запитання: чому ми маніпулюємо структурами даних в пам'яті та структурами даних в базі даних, використовуючи такі різні інструменти? Відповідь: Я справді не знаю! Схоже, випадковість історії, а не хороший дизайн.
Ерік Ліпперт

Відповіді:


118

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

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

  • Об'єктно-орієнтовані системи дозволяють замінити об’єкт іншим класом об'єктів, якщо він є підтипом оригінального типу. Це дозволяє змінювати поведінку , а не лише дані .

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

Крім того, ми можемо бачити стан бази даних як необхідне зло; усунути це з наших систем неможливо. Однак глобальна держава непотрібна. Ми можемо цілком її усунути. Тож навіть проблеми з базою даних були такими ж поганими, ми все одно можемо усунути деякі потенційні проблеми, і часткове рішення краще, ніж жодне рішення.


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

24
@DavidHammen Я фактично працював над моделюванням світової держави для онлайн-гри, яка чітко входить у категорію додатків, про які ви говорите, і навіть там я б не (і не використовував) глобальну державу для цього. Навіть якщо деякі ефективність можна досягти, використовуючи глобальний стан, проблема полягає в тому, що глобальний стан не є масштабованим . Користуватися стає важко, перейшовши від однопотокової до багатопотокової архітектури. Це стає неефективним, коли ви переходите до архітектури NUMA. Це стає неможливим, коли ви переходите до розподіленої архітектури. Папір, яку ви цитуєте, датується ...
Jules

24
1993. Ці проблеми тоді були меншими проблемами. Автори працювали над єдиною процесорною системою, імітуючи взаємодії 1000 об’єктів. У сучасній системі ви, ймовірно, запускаєте подібне моделювання принаймні двоядерної системи, але цілком ймовірно, що це може бути принаймні 6 ядер в одній системі. Якщо все-таки виникають більші проблеми, можна запустити його на кластері. Для такого роду змін ви повинні уникати глобальної держави, оскільки глобальну державу не можна ефективно ділити.
Жуль

19
Я вважаю, що називати стан бази даних "необхідним злом" - це трохи розтягнення. Я маю на увазі, відколи державі стало зло? Держава - це ціле призначення бази даних. Держава - це інформація. Без держави все, що у вас є, є операторами. Чим корисні оператори, без чого працювати? Цей стан має кудись піти. Зрештою, функціональне програмування - це лише засіб для досягнення мети, і без стану мутації не було б сенсу робити щось взагалі. Це трохи схоже на те, що пекар називає торт необхідним злом - це не зло. Це вся суть справи.
J ...

5
@DavidHammen "Є ще якийсь об'єкт, який хоч трохи знає про кожен об'єкт у грі" Не обов'язково правда. Основна техніка сучасного розподіленого моделювання - це скористатися місцевістю та зробити наближення таким чином, що віддаленим об’єктам не потрібно знати про все далеко, лише які дані їм надають власники цих віддалених об’єктів.
JAB

75

По-перше, які проблеми з глобальними змінними ґрунтуються на прийнятій відповіді на пов'язане з вами питання?

Коротше кажучи, це робить стан програми непередбачуваним.

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

Крім того, глобальна держава шкодить читабельності вашого коду.

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

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

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


4
+1 Що ви говорите, це те, що в базах даних є транзакції, завдяки чому можна читати та записувати декілька фрагментів глобального стану атомним шляхом. Хороший момент, який можна обійти лише за допомогою глобальних змінних для кожної абсолютно незалежної інформації.
l0b0

1
@ l0b0 транзакції - це механізм, який дозволяє досягти більшості цілей ACID, правильно. Але сам інтерфейс БД робить код яснішим, вводячи дані в локальну область. Подумайте про використання JDBC RecordSet з блоком спробу використання ресурсів або функцією ORM, яка отримує частину даних за допомогою одного виклику функції. Порівняйте це з керуванням даними далеко від коду, який ви читаєте десь у глобальному масштабі.

1
Тож було б добре використовувати глобальні змінні, якщо я копіюю значення в локальну змінну (з mutex) на початку функції, змінюю локальну змінну, а потім копіюючи значення назад у глобальну змінну в кінці функція? (... запитав він риторично.)
RM

1
@RM Він згадав два моменти. Те, що ви викинули, може стосуватися першого (стан програми непередбачуваний), але не стосується другого (читабельність вашого коду). Насправді це може погіршити читабельність вашої програми: P.
riwalk

1
@RM Ваша функція працюватиме послідовно, так. Але тоді у вас виникне питання, чи щось ще змінило глобальну змінну тим часом, і ця модифікація була важливішою, ніж те, що ви їй пишете. Бази даних, звичайно, можуть мати таку ж проблему.
Грем

45

Пропоную кілька спостережень:

Так, база даних є глобальним станом.

Насправді це суперглобальна держава, як ви вказали. Це універсально! Її сфера охоплює все, що завгодно, або будь-кого , хто підключається до бази даних. І я підозрюю, що багато людей з багаторічним досвідом можуть розповісти вам страхіття про те, як "дивні речі" в даних призвели до "несподіваної поведінки" в одному або кількох відповідних додатках ...

Одним з потенційних наслідків використання глобальної змінної є те, що два різних "модулі" будуть використовувати цю змінну для власних цілей. І в цій мірі таблиця бази даних не відрізняється. Це може стати жертвою тієї ж проблеми.

Хм ... Ось річ:

Якщо модуль деяким чином не працює, він нічого не робить.

Корисний модуль може надати дані або він може їх знайти . І він може повернути дані або змінити стан. Але, якщо він якимось чином не взаємодіє із зовнішнім світом, він також може нічого не робити.

Тепер наша перевага - отримувати дані та повертати дані. Більшість модулів просто простіше писати, якщо їх можна записати з цілком зневагою до того, що робить зовнішній світ. Але в кінцевому підсумку потрібно щось знайти дані та змінити цей зовнішній, глобальний стан.

Крім того, в реальних додатках дані існують, щоб їх можна було читати та оновлювати різними операціями. Деякі проблеми запобігають блокування та транзакції. Але, заважаючи цим операціям конфліктувати між собою в принципі , в кінці дня просто передбачає ретельне мислення. (І помилки ...)

Але також ми взагалі не працюємо безпосередньо зі світовою державою.

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

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

І , нарешті, ми зазвичай робимо різні речі з базами даних , ніж ми могли б з нехорошими глобал.

Неслухняний, розбитий глобальний виглядає так:

Int32 counter = 0;

public someMethod() {
  for (counter = 0; counter < whatever; counter++) {
    // do other stuff.
  }
}

public otherMethod() {
  for (counter = 100; counter < whatever; counter--) {
    // do other stuff.
  }
}

Ми просто не використовуємо бази даних для подібних поточних та оперативних речей. І це може бути повільним характером бази даних і відносною зручністю простої змінної: наша млява, незграбна взаємодія з базами даних просто робить їх поганими кандидатами для багатьох помилок, які ми історично робили зі змінними.


3
Спосіб гарантувати (оскільки ми не можемо припустити) «що дані, які нам було надано, не змінилися» в базі даних буде транзакцією.
l0b0

Так ... це повинно було матися на увазі під "таким же, як-небудь замком".
svidgen

Але, в кінці дня може бути важко продумати ретельно.

Так, бази даних - це справді глобальна держава - саме тому так спокусливо ділитися даними, використовуючи щось на зразок git чи ipfs.
Вільям Пейн

21

Я не згоден з основним твердженням, що:

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

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

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

Наприклад, використання глобальної змінної може виглядати приблизно так

int looks_ok_but_isnt() {
  return global_int++;
}

int somewhere_else() {
  ...
  int v = looks_ok_but_isnt();
  ...
}

Але робити те саме з базою даних повинно бути більш чітко про те, що вона робить

int looks_like_its_using_a_database( MyDB * db ) {
   return db->get_and_increment("v");
}

int somewhere_else( MyBD * db ) { 
   ...
   v = looks_like_its_using_a_database(db);
   ...
}

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

int looks_like_it_uses_explicit_state( MyState * state ) {
   return state->v++;
}


int somewhere_else( MyState * state ) { 
   ...
   v = looks_like_it_uses_explicit_state(state);
   ...
}

Тому я б стверджував, що використання бази даних набагато більше схоже на використання явного стану, ніж використання глобальних змінних.


2
Так, я вважав, що це цікаво, коли ОП сказала: " Вам байдуже, що це за дані; у цьому вся суть " - якщо нас не хвилює, то навіщо їх зберігати? Ось думка: давайте просто перестанемо використовувати змінні та дані взагалі . Це повинно зробити речі набагато простішими. "Зупиніть світ, я хочу зійти!"

1
+1 Різні потоки чи програми, які пишуть та читають з однієї бази даних, є потенційним джерелом великої кількості відомих проблем, тому завжди має бути стратегія вирішення цього питання на рівні бази даних чи програми, або і те й інше. Тож, безумовно, НЕ правда, що вам (розробнику додатків) не байдуже, хто ще читає чи пише з бази даних.
Андрес Ф.

1
+1 З боку, ця відповідь в значній мірі пояснює те, що я найбільше ненавиджу щодо введення залежності. Він приховує подібні залежності.
jpmc26

@ jpmc26 Я можу позначати слова, але чи не є вищезгаданим хорошим прикладом того, як введення залежності (на відміну від глобального пошуку) допомагає зробити залежності явними? Мені здається, що ви скоріше ставитесь до певних API, як, можливо, магія анотацій, яка використовується JAX-RS та Spring.
Еміль Лундберг

2
@EmilLundberg Ні, проблема полягає в тому, коли у вас є ієрархія. Ін'єкційна залежність приховує залежності нижчих рівнів від коду у вищих ярусах, що ускладнює відстеження того, які речі взаємодіють. Наприклад, якщо від цього MakeNewThingзалежить MakeNewThingInDbі використовує мій клас контролера MakeNewThing, то з коду в моєму контролері не зрозуміло, що я змінюю базу даних. Тоді що робити, якщо я використовую інший клас, який фактично здійснює мою поточну транзакцію в БД? DI дуже ускладнює контроль за обсягом об'єкта.
jpmc26

18

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

Бази даних - це інша справа, оскільки вони розроблені для того, щоб мати доступ до так званого "глобального".

Наприклад:

  • Бази даних, як правило, мають вбудований тип і структуру перевірки, що йде далі, ніж мова, що має доступ до них
  • Бази даних майже одноголосно оновлюються на основі транзакцій, що запобігає непослідовним станам, де немає гарантій, як буде виглядати кінцевий стан у глобальному об'єкті (якщо це не приховано за однотонним)
  • Структура бази даних принаймні неявно задокументована на основі таблиці або структури об'єкта, тим більше, ніж програма, яка її використовує

Найголовніше, що бази даних служать іншій цілі, ніж глобальна змінна. Бази даних служать для зберігання та пошуку великої кількості організованих даних, де глобальні змінні обслуговують конкретні ніші (коли це виправдано).


1
Ага. Ти побив мене до цього, поки я на півдорозі писав майже однакову відповідь. :)
Жуль

@Jules ваша відповідь надає детальнішу інформацію про речі; тримай це.
Джефрі Свіні

Але, якщо ви повністю не залежати від збережених процедур доступу до даних, уся ця структура все ще не зможе виконати використання таблиць за призначенням. Або що операції виконуються у відповідному порядку. Або що замки (транзакції) створюються за потребою.
svidgen

Привіт, чи все ще застосовні пункти 1 і 3, якщо ви використовуєте статичну мову, як Java?
Jesvin Jose Jose

@aitchnyu Не обов'язково. Слід зазначити, що бази даних будуються з метою надійного обміну даними, де глобальних змінних зазвичай немає. Об'єкт, що реалізує інтерфейс самодокументування, суворою мовою, виконує інше призначення, ніж навіть друкована база даних NoSQL.
Jeffrey Sweeney

10

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

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

Для мене різниця, яка має значення для глобальних змінних, полягає в тому, що вони утворюють приховані та незахищені лінії зв'язку. Читання з клавіатури дуже очевидно і захищено. Мені потрібно здійснити певний виклик функції, і я не можу отримати доступ до драйвера клавіатури. Це ж стосується доступу до файлів, спільної пам'яті та вашого прикладу, баз даних. Для читача коду очевидно, що ця функція читає з клавіатури, що функція отримує доступ до файлу, якась інша функція має доступ до спільної пам'яті (і навколо цього краще захищати), а ще якась інша функція має доступ до бази даних.

З іншого боку, для глобальних змінних це зовсім не очевидно. API каже телефонувати foo(this_argument, that_argument). У послідовності виклику немає нічого, що говорить про те, що глобальна змінна g_DangerWillRobinsonповинна бути встановлена ​​на якесь значення, але перед викликом foo(або вивчити після виклику foo).


Google заборонив використовувати нестандартні довідкові аргументи в C ++ насамперед тому, що читачеві коду не очевидно, що foo(x)він зміниться, xтому що він fooбере аргумент непостійне посилання. (Порівняйте з C #, який диктує, що і визначення функції, і сайт виклику повинні кваліфікувати посилальний параметр із refключовим словом.) Хоча я не згоден зі стандартом Google щодо цього, я розумію їхню думку.

Код пишеться один раз і змінюється кілька разів, але якщо він взагалі хороший, він читається багато-багато разів. Приховані лінії зв'язку - це дуже погана карма. Несформовані посилання на C ++ являють собою незначну приховану лінію зв'язку. Хороший API або хороший IDE покажуть мені, що "О! Це дзвінок за посиланням". Глобальні змінні - це величезна прихована лінія зв'язку.


Ваша відповідь має більше сенсу.
Біллал Бегерадж

8

Я вважаю, що цитуване пояснення спрощує питання до того моменту, коли міркування стають смішними. Звичайно, стан зовнішньої бази даних сприяє глобальному стану. Важливе питання - якваша програма залежить від (мінливого) глобального стану. Якщо функція бібліотеки для розділення рядків на пробіл залежатиме від посередницьких результатів, що зберігаються в базі даних, я заперечую проти цього дизайну хоча б настільки ж, наскільки я заперечую проти глобального масиву символів, використовуваного для тієї ж мети. З іншого боку, якщо ви вирішите, що вашій програмі не потрібна повномасштабна СУБД для зберігання бізнес-даних в цей момент, і це зробить глобальна структура ключових значень пам'яті, це не обов'язково є ознакою поганого дизайну. Важливо те, що - незалежно від того, яке рішення ви обираєте для зберігання своїх даних - цей вибір залежить від дуже невеликої частини системи, тому більшість компонентів можуть бути агресивними щодо рішення, обраного для розгортання та перевіреного в одиниці, і розгорнуто. рішення можна змінити пізніше з невеликими зусиллями.


8

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

Недоліком цього є те, що ми дійсно повинні розглянути , як використовуються ці змінні, і багато , що зводиться до того ж виду , що думка йде в БД-сперечань. Будь-яке асинхронне зчитування / запис змінних ОБОВ'ЯЗКОВО має бути атомним. Якщо змінна може писати більше, ніж одне місце, певна думка повинна переконатися, що вони завжди записують дійсні дані, тому попереднє запис не замінюється довільно (або це довільна заміна - це безпечна річ). Якщо одна і та ж змінна читається не один раз, деякі думки повинні вивчити, що відбувається, якщо змінна змінює значення між читаннями або копію змінної потрібно взяти на початку, щоб обробка проводилася за допомогою послідовного значення, навіть якщо це значення стає несвіжим під час обробки.

(Для цього останнього, в перший день мого контракту, що працював над системою протидії повітряним суднам, настільки сильно пов'язаної з безпекою, команда програмного забезпечення переглядала звіт про помилку, який вони намагалися з'ясувати протягом тижня або близько того. У мене було достатньо часу, щоб завантажити інструменти для розробників та копію коду. Я запитав "чи не могла ця змінна бути оновлена ​​між читаннями та викликати її?", Але насправді не отримала відповіді. Ей, що означає новий хлопець знає, зрештою? Тому, поки вони все ще обговорювали це, я додав захисний код, щоб прочитати змінну атомно, зробив локальну побудову, і в основному сказав "ей, хлопці, спробуйте це". . :)

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


7

Залежно від того, який аспект ви судите, глобальні змінні та доступ до бази даних можуть бути відокремленими, але поки ми оцінюємо їх як залежності, вони однакові.

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

Коли функція залежить від глобальної змінної, вона більше не може вважатися чистою, оскільки для одного і того ж набору або аргументів вона може давати різні результати, оскільки значення глобальної змінної може змінюватися між викликами.

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

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

Те саме відбувається, коли ми приховуємо залежність від бази даних. У нас можуть бути функції або об’єкти, які здійснюють прямі дзвінки на запити та команди БД, приховуючи ці залежності і завдаючи нам ті самі проблеми, що викликають глобальні змінні; або ми можемо зробити їх явними, що, як виявляється, вважається найкращою практикою, яка має багато імен, таких як шаблон сховища, сховище даних, шлюз тощо.

PS: Є інші аспекти, які мають важливе значення для цього порівняння, наприклад, чи є участь паралельність, але це питання охоплене іншими відповідями тут.


Мені подобається, що ти сприйняв це з кута залежності.
cbojar

6

Гаразд, почнемо з історичної точки зору.

Ми знаходимося в старій програмі, написаній у вашому типовому поєднанні збірки та C. Немає функцій, а лише процедури . Коли ви хочете передати аргумент або повернути значення з процедури, ви використовуєте глобальну змінну. Потрібно говорити, що це досить важко відстежувати, і загалом кожна процедура може робити все, що хоче, з кожною глобальною змінною. Не дивно, що люди зверталися до передачі аргументів і повернення значень по-іншому, як тільки це було можливо (якщо тільки це не було критично важливим для цього не робити - наприклад, подивіться вихідний код Build Engine (Duke 3D)). Тут народилася ненависть до глобальних змінних - ви мали дуже мало уявлення про те, який фрагмент глобального стану читатиме та змінюватиме кожна процедура, і ви не могли реально вписати процедуру гніздування.

Чи означає це, що глобальна змінна ненависть - це минуле? Не зовсім.

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

Більш суть полягає в тому, що додаючи глобальні змінні, ви розширюєте сферу дії кожного окремого коду, який має доступ до цієї глобальної змінної. Пам’ятаєте всі ті рекомендації, як-от "тримайте свої методи короткими"? Якщо у вас є 600 глобальних змінних (знову ж таки, приклад із реального світу: /), всі ваші області методів неявно розширені цими 600 глобальними змінними, і немає простого способу відстежити, хто має доступ до чого.

Якщо це зробити неправильно (звичайним способом :)), глобальні змінні можуть мати зв'язок між собою. Але ви поняття не маєте, як вони поєднуються, і немає механізму, який би забезпечував, щоб глобальна держава була завжди послідовною. Навіть якщо ви введете критичні розділи, щоб спробувати підтримувати речі, ви побачите, що він погано порівнює з належною базою даних ACID:

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

Чи можливо вирішити ці питання? Не зовсім. Для цього вам потрібна інкапсуляція, або справді сувора дисципліна. Важко зробити все правильно, і це, як правило, не дуже вдалий рецепт успіху в розробці програмного забезпечення :)

Менший діапазон, як правило, полегшує обґрунтування коду. Глобальні змінні змушують навіть найпростіші фрагменти коду включати величезні розмаїття.

Звичайно, це не означає, що глобальний обсяг є злом. Це просто не повинно бути першим рішенням, яке ви шукаєте - це типовий приклад "простого в реалізації, важкого в обслуговуванні".


Дуже схоже на фізичний світ: дуже важко відкочувати речі назад.

Це хороша відповідь, але вона може сформулювати висловлювання тези (розділ TL; DR) на самому початку.
jpmc26

6

Глобальна змінна - це інструмент, її можна використовувати для добра і зла.

База даних - це інструмент, її можна використовувати для добра і зла.

Як зазначається в оригінальному плакаті, різниця не все така велика.

Недосвідчені студенти часто думають, що помилки - це те, що трапляється з іншими людьми. Вчителі використовують "Глобальні змінні - це зло" як спрощену причину штрафувати за поганий дизайн. Студенти, як правило, не розуміють, що те, що їхня 100-лінійна програма відсутня помилок, не означає, що ті ж методи можуть бути використані для 10000-лінійних програм.

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

Якби люди використовували підхід ACID до глобальних змінних, вони не були б такими поганими.

З іншого боку, якщо ви погано проектуєте бази даних, вони можуть бути кошмарами.


3
Типова претензія студентів на stackoverflow: Допоможи мені! Мій код ідеальний, але він не працює!
Девід Хаммен

"ACID-підхід до глобальних змінних" - див. Посилання у Clojure.
Чарльз Даффі

@DavidHammen, а ви вважаєте, що професіонали мають мозок на відміну від студентів?
Біллал Бегерадж

@BillalBEGUERADJ - Це різниця між професіоналами та студентами. Ми знаємо, що незважаючи на багаторічний досвід роботи і незважаючи на всі зусилля з перегляду коду, тестування тощо, наш код не є ідеальним.
Девід Хаммен


5

Для мене першочергове зло - це те, що глобальні громадяни не мають захисту від проблем одночасності. Ви можете додати механізми для вирішення подібних проблем із Globals, але ви виявите, що чим більше проблем з одночасністю ви вирішите, тим більше ваші глобалісти почнуть імітувати базу даних. Вторинне зло - це не договір про використання.


3
Наприклад, errnoу C.
Девід Хаммен

1
Це точно пояснює, чому глобальні та бази даних неоднакові. Можуть бути й інші відмінності, але ваш конкретний пост повністю руйнує концепцію. Якщо ви навели приклад швидкого коду, тоді я впевнений, що ви отримаєте багато оновлень. наприклад, MyFunc () {x = globalVar * 5; // .... Якась інша обробка; y = globalVar * 34; // Ooops, якийсь інший потік міг змінити globalVar під час деякої іншої обробки, а x і y використовують різні значення для globalVar у своїх розрахунках, що майже напевно не дасть бажаних результатів.
Данк

5

Деякі з інших відповідей намагаються пояснити, чому добре використовувати базу даних. Вони помиляються! База даних є глобальним станом, і як таке є злом, як одиночна або глобальна змінна. Використовувати базу даних - це неправильно, коли ви можете просто просто використовувати локальну карту чи масив!

Глобальні змінні дозволяють отримати глобальний доступ, що несе ризик зловживань. Глобальні змінні також мають переваги. Як правило, глобальні змінні - це те, чого слід уникати, а не те, що ніколи не слід використовувати. Якщо ви можете легко їх уникнути, ви повинні уникати їх. Але якщо переваги переважають недоліки, звичайно, ви повинні їх використовувати! *

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

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


* Виняток тут становлять студенти. Має сенс заборонити студентам використовувати глобальні змінні, щоб вони мали вивчити, що таке альтернативи.

** Деякі відповіді неправильно стверджують, що бази даних якимось чином краще захищені, ніж інші форми глобального стану (питання явно стосується глобального стану , а не лише глобальних змінних). Це болоти. Первинний захист, запропонований у сценаріїв баз даних, здійснюється за умовами, що точно таке саме для будь-якої іншої глобальної держави. Більшість мов також дозволяють отримати додатковий додатковий захист для глобального стану у вигляді constкласів, які просто не дозволяють змінювати свій стан після того, як це встановлено в конструкторі, або getters та setters, які можуть враховувати інформацію про потоки або стан програми.


2

У певному сенсі відмінність між глобальними змінними та базою даних схожа з відмінністю між приватними та публічними членами об'єкта (якщо припустити, що все ще користуються публічними полями). Якщо ви вважаєте всю програму як об'єкт, то глобальні поля - це приватні змінні, а база даних - це публічні поля.

Вони є ключовою відмінністю тут однієї з взятих на себе відповідальності.

Коли ви пишете об'єкт, передбачається, що кожен, хто підтримує методи-члени, забезпечить збереження приватних полів. Але ви вже відмовляєтесь від будь-яких припущень щодо стану громадських полів і ставитесь до них з особливою обережністю.

Це ж припущення застосовується на більш широкому рівні для глобальної бази даних v / s. Крім того, мова програмування / екосистема гарантує обмеження доступу для приватних v / s public так само, як це застосовує їх до (несемінованої пам'яті) глобальної бази даних v / s.

Коли в програму вступає багатопотокова редакція, концепція приватної загальнодоступної загальнодоступної бази даних v / s глобальна V / s - це лише розрізнення по спектру.

static int global; // within process memory space
static int dbvar; // mirrors/caches data outside process memory space

class Cls {
    public: static int class_public; // essentially the same as global
    private: static int class_private; // but public to all methods in class

    private: static void method() {
        static int method_private; // but public to all scopes in method
        // ...
        {
            static int scope1_private; // mutex guarded
            int the_only_truly_private_data;
        }
        // ...
        {
            static int scope2_private; // mutex guarded
        }
    }
}

1

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

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


0

Існує кілька відмінностей:

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

  • Значення бази даних (рядок, стовпець) містить контекст і реляційне відображення в базі даних. Це відношення можна легко витягти та проаналізувати, використовуючи такі інструменти, як Jailer (наприклад). З іншого боку, глобальна змінна дещо відрізняється. Ви можете знайти всі звичаї, але вам було б неможливо розповісти мені всі способи взаємодії змінної з рештою вашого світу.

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

Це єдине, про що я можу зараз подумати, але я впевнений, що є ще. Простіше кажучи, це дві різні речі і їх слід використовувати для різних цілей .


0

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

Наприклад, розглянемо сервер HTTP, який зберігає ім'я сервера.

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

Навпаки, якщо ім'я сервера знаходиться в базі даних, проблем немає. Ви можете просто створити один екземпляр цієї бази даних для кожного примірника сервера HTTP. Оскільки кожен екземпляр сервера має власний екземпляр бази даних, він може мати власне ім’я сервера.

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


0

Я думаю, що це цікаве питання, але на це трохи важко відповісти, оскільки є два основні питання, які пов'язані з терміном "глобальна держава". Перша - концепція "глобальної зв'язку". Доказом цього є те, що альтернатива, що надається для глобального стану, - це введення залежності. Вся справа в тому, що DI не обов'язково усуває глобальний стан. Тобто, вводити залежності від глобального стану абсолютно можливо і спільно. Що робить DI - це усунути з'єднання, що поставляється із глобальними змінними та загальновживаним шаблоном Singleton. Окрім дещо менш очевидного дизайну, є дуже мало недоліків у усуненні такого типу з’єднання, а переваги усунення зчеплення зростають експоненціально із кількістю залежностей від цих глобальних.

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

Тож питання, чим бази даних відрізняються від глобальних, також роздвоєний у цих двох аспектах. Чи вводять вони сполучення? Так, вони можуть, але це дуже залежить від того, як створено програму та як створена база даних. Є занадто багато факторів, щоб мати однозначну відповідь на те, чи бази даних впроваджують глобальне з'єднання без деталей конструкції. Що стосується того, чи запроваджують вони спільний доступ до стану, то це головна точка бази даних. Питання в тому, чи роблять вони це добре. Знову ж таки, я думаю, що це занадто складно, щоб відповісти без багатьох інших відомостей, таких як альтернативи та багато інших компромісів.


0

Я б подумав про це дещо інакше: "глобальна змінна", як поведінка, - це ціна, яку платять адміністратори бази даних (DBA), тому що це необхідно зло, щоб виконувати свою роботу.

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

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

Розробка бази даних є однією з таких голосових ситуацій меншин. Інструменти, необхідні для роботи над DBA, є дуже потужними, і їх теорія не вкорінена в інкапсуляції. Для того, щоб простежити кожну швидку ефективність із своїх баз даних, їм потрібен повний безперешкодний доступ до всього, подібно до глобальних. Візьміть одну зі своїх чудовищних баз даних на 100 мільйонів рядів (або більше!), І ви зрозумієте, чому вони не дозволяють двигуну БД проводити жодних ударів.

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

Не рідкість наявність програмних пакетів на 100 тис. Ліній. Теоретично будь-яка лінія програмного забезпечення може впливати на будь-яку глобальну програму в будь-який момент часу. У DBA, ви ніколи не знайдете 100k різних запитів, які можуть змінювати базу даних. Це було б нерозумно з увагою до деталей, необхідних для захисту вас від себе. Якщо у DBA є щось таке велике, вони навмисно інкапсулюють свою базу даних за допомогою аксесуарів, виконуючи проблеми, пов'язані з "глобальними схожими", і потім робитимуть якнайбільше роботи завдяки цьому "безпечнішому" механізму. Таким чином, коли поштовх приходить, навіть база даних людей уникає глобальних. Вони просто приходять з великою небезпекою, і є альтернативи, які настільки ж сильні, але не такі небезпечні.

Ви б хотіли ходити по розбитому склу або по красиво прометених тротуарах, якби всі інші речі рівні? Так, ви можете ходити по розбитому склу. Так, деякі люди навіть заробляють на життя, роблячи це. Але все ж просто нехай вони підмітають тротуар і рухаються далі!


0

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

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


-2

Глобали не злі; вони просто інструмент. Помилка глобалів є проблематичною, як і неправильне використання будь-якої іншої функції програмування.

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


Чи хтось із прихильників розуму пояснить ваші події? Це здається грубим подзвонити без пояснень.
Байрон Джонс

-2

Шаблон лише для читання, і припускайте, що ваші дані не оновлюються під час їх друку. Черга пише або вирішує конфлікти іншим способом. Ласкаво просимо в пекло чорт, ти використовуєш глобальний db.

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