Виявлення змін у таблиці SQL Server


13

У моєму додатку, з БД, що працює на SQL Server 2012, я отримав завдання (заплановане завдання), яке періодично виконує дорогий запит і записує результати в таблицю, яку згодом можна буде запитувати додатком.

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

У мене є такі ідеї:

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

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

Відповіді:


14

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

Тож вам доведеться обробляти це за допомогою спеціального коду. Це означає тригери, оскільки альтернатива (виявлення з журналів записів) є прерогативою, зарезервованою лише для транзакційної реплікації (або це CDC alter-ego). Майте на увазі, що якщо ви спробуєте відстежити це через стовпчик "Останнє оновлення у", тоді ви зіткнетеся саме з проблемою серіалізації, згаданою вище. Якщо одночасність оновлення важлива, вам доведеться використовувати механізм черги (тригер використовує INSERT, а потім процес агрегує вставлені значення, щоб сформулювати "останній оновлений о"). Не намагайтеся обдурити якесь «розумне» рішення, наприклад, прокрадання поточної особи або пошук sys.dm_db_index_usage_stats . А також стовпець "updated_at" на запис, як, наприклад, часові позначки Rails,

Чи є якась «легка» альтернатива? Насправді така є, але важко сказати, чи буде вона працювати для вас і важко її правильно виправити: сповіщення про запити . Повідомлення про запити робить саме це, воно налаштує сповіщення, якщо в будь-яких даних є зміни, і вам потрібно оновити запит. Хоча більшість розробників знайомі лише з його втіленням .Net як SqlDependency, сповіщення про запити може використовуватися як довгоживучий стійкий механізм виявлення зміни даних. У порівнянні з істинним відстеженням змін, він буде дійсно легким, а його семантика наближається до ваших потреб (щось, що- небудь , змінилося, тому вам потрібно повторно запитувати).

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


То чому б Microsoft заважав створювати sys.dm_db_index_usage_stats, якщо на інформацію, яку він надає, не можна покластися?
Крейг Ефрейн

Це не DMV, призначений для відстеження змін . Є дуже надійним за цільовим призначенням, а саме налаштуванням продуктивності.
Рем Русану

8

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

Є два механізми SQL Server, які можуть вам допомогти. Вашим кінцевим рішенням може бути гібрид двох.

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

Rowversion / мітка часу . Це 8-байтовий тип варбінарного стовпця (який можна ввести в BigInt), який збільшується, по всій базі даних, кожного разу, коли рядок, що містить його, вставляється або оновлюється (це не допомагає при видаленні). Якщо ви проіндексували ці стовпці, ви можете легко визначити, чи змінилися дані рядків, порівнявши MAX (часову позначку) з його значенням з останнього разу, коли він був оцінений. Оскільки значення монотонно зростає, це дасть вам надійну вказівку на те, що дані змінилися, якщо нове значення більше, ніж було в останній раз, коли ви його перевіряли.


7

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

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

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

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

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


3

SqlTableDependency

SqlTableDependency - компонент реалізації на високому рівні для доступу до сповіщень, що містять значення записів таблиці в базі даних SQL Server.

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

Чим відрізняється .NET SqlDepenency?

В основному, основна відмінність полягає в тому, що SqlTableDependency надсилає події, що містять значення для запису, вставленого, зміненого або видаленого, а також операції DML (вставлення / видалення / оновлення), виконаної в таблиці: SqlDepenency не вказує, які дані були змінені таблицю бази даних, вони говорять лише про те, що щось змінилося.

Погляньте на проект GITHUB .


1

Якщо очікувані оновлення впливають на індекс (і лише якщо), ви можете використовувати системну таблицю sys.dm_db_index_usage_statsдля виявлення останнього оновлення до індексу у відповідній таблиці. Ви б використовували last_user_updateполе.

Наприклад, щоб отримати останні оновлені таблиці:

select
    object_name(object_id) as OBJ_NAME, *
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
order by
    dm_db_index_usage_stats.last_user_update desc

Або перевірити, чи була змінена конкретна таблиця після конкретної дати:

select
    case when count(distinct object_id) > 0 then 1 else 0 end as IS_CHANGED
from
    sys.dm_db_index_usage_stats
where
    database_id = db_id(db_name())
    and object_id = object_id('MY_TABLE_NAME')
    and last_user_update > '2016-02-18'

Як ви ставитесь до коментаря Ремуса вище? "Не намагайтеся обдурити таке" розумне "рішення, як, наприклад, прокрадання поточної особи або пошук sys.dm_db_index_usage_stats." (Дивіться також його коментар нижче його відповіді.)
Фабіан Шмід

1
@FabianSchmied Цікаво - я не бачив, що коли я додав свою відповідь, я не міг знайти нічого авторитетного, окрім інших відповідей Ремуса, що вказувало б, що це неправдоподібно для цього випадку використання; на сторінці MS dm_db_index_operational_statsвідображаються проблеми (очищені, коли очищається кеш метаданих), але не для dm_db_index_usage_stats. Єдине питання, яке я знайшов, було відновлення індексів, перезавантаження сервера та від'єднання бази даних, очищаючи статистику використання, і це, здається, не застосовується тут. Буде зацікавлено переглянути обґрунтовану інформацію про це.
Джефф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.