6.1.26

Заблуждения относительно журнала и резервных копий журнала: как самому проверить

Автор: Paul Randal, Misconceptions around the log and log backups: how to convince yourself

До сих пор широко распространено заблуждение, что при корректной работе в моделях восстановления FULL или BULK_LOGGED полные или дифференциальные резервные копии могут усекать журнал. Нет. Это НИКОГДА не происходит. Это одна из причин, по которой я посвящаю целый доклад этой теме на конференции PASS в этом году — поведение журнала транзакций, по моему скромному мнению, является одной из самых неправильно понимаемых частей SQL Server.

5.1.26

Заблуждения относительно восстановления базы данных

Автор: Paul Randal, Misconceptions around database repair

На этой неделе на форумах и в Twitter было оживлённо: люди сталкиваются с множеством интересных проблем. Я заметил, что существует много заблуждений относительно запуска восстановления (repair), поэтому, чтобы завершить пятницу, я пройдусь по их списку для вас. Вот эти заблуждения, по поводу некоторых из которых мне приходилось спорить с людьми несколько раз и в конце концов прибегать к фразе «Слушайте, я писал код восстановления, мне жаль, но вы не правы», что я ненавижу делать:

4.1.26

Заблуждения относительно добавления столбцов в таблицу

Автор: Paul Randal, Misconceptions around adding columns to a table

Это короткое продолжение моей статьи «Заблуждения вокруг размера null bitmap».

Битовая карта NULL всегда присутствует в записи данных (т.е. в записях в куче или на конечном уровне кластерного индекса), за исключением случаев, когда в SQL Server 2008 и новее все столбцы определены как SPARSE, но она необязательна в записях индексов, если все столбцы в записях индексов не допускают значения NULL. Заблуждение связано с тем, что происходит при добавлении нового столбца в таблицу. Распространённое заблуждение заключается в том, что если у вас есть 8 столбцов в таблице (и, следовательно, 8 битов в битовой карте NULL), и вы добавляете девятый столбец, то SQL Server должен обновить каждую запись, чтобы все битовые карты NULL содержали 9 битов. (Такое же заблуждение применяется к добавлению 17-го, 25-го, 33-го и т.д. столбцов).

3.1.26

Заблуждения относительно хранилища FILESTREAM

Автор: Paul Randal, Misconceptions around FILESTREAM storage

Эта короткая статья вызвана вопросом, поступившим через Twitter, (перефразированный) вопрос звучит так: «Могут ли данные FILESTREAM храниться удалённо?». Это сбивало с толку многих, и ни документация BOL по FILESTREAM, ни мой технический документ по FILESTREAM (см. здесь) прямо не отвечают на этот вопрос.

2.1.26

Заблуждения относительно повреждений: могут ли они исчезать?

Автор: Paul Randal, Misconceptions around corruptions: can they disappear?

Этот вопрос возникал уже несколько раз, совсем недавно — сегодня утром в письме: последующие запуски DBCC CHECKDB показывают разное количество повреждений, а иногда и вовсе их не находят — что происходит? Ещё более странно: задание обслуживания запускает DBCC CHECKDB, который показывает ошибки, но затем утром — никаких ошибок согласованности. Как?

29.12.25

Заблуждения о перестроении индексов (распределение, режим BULK_LOGGED, блокировки)

Автор: Paul Randal, Search Engine Q&A #19: Misconceptions around index rebuilds (allocation, BULK_LOGGED mode, locking)

Я обнаружил (и помог исправить) довольно много мифов и заблуждений об операциях перестроения индексов. Их достаточно, чтобы стоило написать об этом статью в блоге (да и здесь, в Орландо, слишком жарко, чтобы идти сидеть у бассейна, так что мы оба сидим здесь и пишем в блогах)…

26.12.25

Любопытный случай… немедленной записи и минимально логируемых операций

Автор: Paul Randal, The Curious Case of… eager writing and minimally-logged operations

У меня состоялось обсуждение по электронной почте с одним специалистом, который размышлял о резервных копиях журнала и минимально логируемых операциях. В частности, он был озадачен тем, как работает резервное копирование журнала, если после завершения операции контрольная точка не происходила. Ведь грязные страницы данных всё ещё остаются в памяти, верно?

Оказывается, это не так.

25.12.25

Любопытный случай… блокировки BULK_OPERATION при просмотре кучи (heap) с NOLOCK

Автор: Paul Randal, The Curious Case of… the BULK_OPERATION lock during a heap NOLOCK scan

Мне пришёл вопрос по электронной почте: один специалист заметил, что во время выполнения просмотра кучи с использованием NOLOCK на всю продолжительность операции на куче удерживалась блокировка BULK_OPERATION. Вопрос заключался в том, зачем нужна эта блокировка, ведь просмотр с NOLOCK, казалось бы, не должно читать проблемные страницы?

Ответ заключается в том, что эта дополнительная блокировка нужна именно потому, что сканирование с NOLOCK может прочитать проблемную страницу, если в тот же момент над кучей выполняется операция массовой загрузки (bulk operation).

24.12.25

Небольшая ошибка в столбце sample_ms в sys.dm_io_virtual_file_stats

Автор: Paul Randal, Small bug with sample_ms in sys.dm_io_virtual_file_stats

Мне написал бывший студент по поводу странного поведения столбца sample_ms в представлении sys.dm_io_virtual_file_stats. Предполагается, что он должен содержать количество миллисекунд, прошедших с момента запуска экземпляра SQL Server. У него есть экземпляр SQL Server 2016, работающий с августа 2019 года, и там отображается следующее:

  • ms_ticks из sys.dm_os_sys_info: 51915112684 (что соответствует 28 августа 2019 года)
  • sample_ms из sys.dm_io_virtual_file_stats: 375504432 (что составляет около 4,5 дней)

Очевидно, что внутренняя переменная в SQL Server несколько раз переполнилась или сбросилась, вероятно, при достижении 2^31 или 2^32, хотя начиная с SQL Server 2016 тип данных sample_ms должен быть bigint.

23.12.25

Любопытный случай… неубиваемого потока

Автор: Paul Randal, The Curious Case of… the un-killable thread

На прошлой неделе, преподавая курс IEPTO2, я объяснял, почему иногда поток невозможно завершить с помощью команды KILL, и подумал, что это отличная тема для статьи.

22.12.25

Любопытный случай… с object ID 99

Автор: Paul Randal, The Curious Case of… object ID 99

Сегодня я отвечал на вопрос под тегом #sqlhelp в Twitter и упомянул об использовании значения object ID 99, потому что SQL Server никогда не присвоит таблице идентификатор объекта 99. И я подумал, что это станет хорошей темой для небольшой записи в блоге.

21.12.25

Любопытный случай… журнал, генерируемый при выполнении DROP TABLE

Автор: Paul Randal, The Curious Case of… log generated during a DROP TABLE

На прошлой неделе я несколько раз обсуждал объём журнала транзакций, который генерируется при выполнении DROP TABLE для очень большой таблицы. В обоих случаях люди беспокоились об объёме журнала, и о том, не замедлит ли это работу.

Надеюсь, все вы знаете, что мифом является утверждение, будто операции DROP TABLE и TRUNCATE TABLE не журналируются. Если вы этого не знали, прочитайте мою запись в блоге на sqlperformance.com, где объясняется механизм отложенного удаления (deferred drop). Обе операции полностью журналируются и будут генерировать довольно много записей журнала транзакций.

Основная часть генерируемого журнала возникает из-за необходимости фиксировать в журнале освобождение экстентов и страниц внутри них. Для каждого экстента должен быть сброшен бит на соответствующей странице GAM и странице IAM, а все 8 страниц в экстенте должны быть помечены как освобождённые на соответствующей странице PFS (путем выключения бита 0x40 в каждом байте PFS). Таким образом, получается три записи журнала на каждый распределённый экстент.

Я быстро подготовил тестовый пример, чтобы показать вам записи журнала:

SELECT
[Current LSN],
[Operation],
[Context],
[Log Record Length],
[Description]
FROM fn_dblog (null, null);
Current LSN             Operation       Context  Length  Description
----------------------  --------------  -------- ------- ------------------------------------------------------
.
.
000001eb:00000010:016a  LOP_SET_BITS    LCX_IAM  72
000001eb:00000010:016b  LOP_MODIFY_ROW  LCX_PFS  88      Deallocated 0001:000026f0;Deallocated 0001:000026f1;Deallocated 0001:000026f2;Deallocated 0001:000026f3;Deallocated 0001:000026f4;Deallocated 0001:000026f5;Deallocated 0001:000026f6;Deallocated 0001:000026f7
000001eb:00000010:016c  LOP_SET_BITS    LCX_GAM  72      Deallocated 1 extent(s) starting at page 0001:000026f0
.
.

И этот набор из трёх записей журнала повторяется для каждого освобождаемого экстента.

Одно из обсуждений касалось удаления таблицы размером 20 ТБ. 

20 ТБ = 20 * 1024 * 1024 *1024 * 1024 = 21 990 232 555 520 байт. 

Один экстент — это 65 536 байт, поэтому 20 ТБ — это 21 990 232 555 520 / 65 536 = 335 544 320 экстентов. При 72 + 88 + 72 = 232 записываемых байта на экстент это означает, что удаление таблицы размером 20 ТБ приведёт к генерации как минимум 335 544 320 * 232 = 77 846 282 240 байт = 72,5 ГБ журнала транзакций, или примерно 0,35% от размера таблицы. Периодически генерируется небольшое количество других записей журнала, поэтому для надёжности я бы сказал, что это 0,35–0,4%.

Всё это не генерируется как одна транзакция (весь смысл отложенного удаления заключается в выполнении операции в фоновом режиме множеством небольших транзакций) и не генерируется мгновенно (поскольку фоновая задача отложенного удаления выполняется в одном потоке), поэтому эти записи журнала будут перемешаны со всем остальным, что происходит в базе данных, и отправлены на синхронную реплику AG так же, как и другие транзакции. Так что, хотя и генерируется дополнительный объём журнала, я не ожидаю, что это вызовет серьёзные проблемы.

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

Суть в том: выполнение DROP или TRUNCATE для таблицы требует генерации в журнале примерно 0,35–0,4% от размера таблицы.