Я создал следующую таблицу:
CREATE TABLE dbo.TestStructure
(
id INT NOT NULL,
filler1 CHAR(36) NOT NULL,
filler2 CHAR(216) NOT NULL
);
а затем создал кластерный индекс:
CREATE CLUSTERED INDEX idx_cl_id
ON dbo.TestStructure(id);
Затем я заполнил его 30 строками, каждый размер которых составляет 256 байт (на основе объявления таблицы):
DECLARE @i AS int = 0;
WHILE @i < 30
BEGIN
SET @i = @i + 1;
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (@i, 'a', 'b');
END;
Теперь, основываясь на информации, которую я прочитал в книге «Учебный комплект (экзамен 70-461): запросы к Microsoft SQL Server 2012 (Ицик Бен-Ган)»:
SQL Server внутренне организует данные в файле данных на страницах. Страница размером 8 Кбайт принадлежит одному объекту; например, к таблице или индексу. Страница - это самая маленькая единица чтения и записи. Страницы далее организованы в экстенты. Экстент состоит из восьми последовательных страниц. Страницы из экстента могут принадлежать одному объекту или нескольким объектам. Если страницы принадлежат нескольким объектам, тогда экстент называется смешанным экстентом; если страницы принадлежат одному объекту, то экстент называется равномерным экстентом. SQL Server хранит первые восемь страниц объекта в смешанных экстентах. Когда объект превышает восемь страниц, SQL Server выделяет дополнительные единообразные экстенты для этого объекта. Благодаря этой организации мелкие объекты тратят меньше места, а крупные объекты становятся менее фрагментированными.
Итак, здесь у меня есть первая смешанная страница экстента размером 8 КБ, заполненная 7680 байтами (я вставил 30 строк размера 256 байт, так что 30 * 256 = 7680), чтобы проверить размер, который я запустил, проверка размера proc - он возвращает следующий результат
index_type_desc: CLUSTERED INDEX
index_depth: 1
index_level: 0
page_count: 1
record_count: 30
avg_page_space_used_in_percent: 98.1961947121324
name : TestStructure
rows : 30
reserved : 16 KB
data : 8 KB
index_size : 8 KB
unused : 0 KB
Таким образом, 16 КБ зарезервировано для таблицы, первая страница 8 КБ предназначена для страницы корневого IAM, вторая страница предназначена для листовой страницы хранения данных, которая занимает 8 КБ и занимает ~ 7,5 КБ, теперь, когда я вставляю новую строку с 256 байт:
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (1, 'a', 'b');
он не хранится на той же странице, хотя он имеет пространство 256 байт (7680 b + 256 = 7936, что по-прежнему меньше 8 КБ), создается новая страница данных, но эта новая строка может поместиться на той же старой странице Почему SQL Server создает новую страницу, когда он может сэкономить место и время поиска, вставляя ее в существующую страницу?
Примечание: то же самое происходит с индексом кучи.
источник
Хотя верно, что SQL Server использует страницы данных 8 КБ (8192 байта) для хранения 1 или более строк, каждая страница данных имеет некоторые издержки (96 байтов), а каждая строка имеет некоторые издержки (не менее 9 байтов). 8192 байта не являются чисто данными.
Для более подробного изучения того, как это работает, смотрите мой ответ на следующий вопрос DBA.SE:
СУММА ДАННЫХ, не соответствующих размеру таблицы из sys.allocation_units
Используя информацию в этом связанном ответе, мы можем получить более четкое представление о реальном размере строки:
Использование
DBCC PAGE
подтверждает мои расчеты, показывая:Record Size 263
(дляtempdb
) иRecord Size 277
(для базы данных, которая установленаALLOW_SNAPSHOT_ISOLATION ON
).Теперь, с 30 рядами, то есть:
БЕЗ информации о версии
30 * 263 даст нам 7890 байт. Затем добавьте 96 байтов заголовка страницы для 7986 использованных байтов. Наконец, добавьте 60 байтов (по 2 на строку) массива слотов, чтобы на странице было всего 8046 байтов, а оставшихся 146. Использование
DBCC PAGE
подтверждает мой расчет, показывая:m_slotCnt 30
(т.е. количество строк)m_freeCnt 146
(т.е. количество байтов, оставленных на странице)m_freeData 7986
(т.е. данные + заголовок страницы - 7890 + 96 - массив слотов не учитывается при вычислении «используемых» байтов)С информацией о версии
30 * 277 байт, всего 8310 байт. Но 8310 превышает 8192, и это даже не учитывает ни 96-байтовый заголовок страницы, ни 2-байтовый массив слотов строки (30 * 2 = 60 байт), что должно дать нам только 8036 используемых байтов для строк.
НО, а как насчет 29 строк? Это даст нам 8033 байта данных (29 * 277) + 96 байтов для заголовка страницы + 58 байтов для массива слотов (29 * 2), равного 8187 байтов. И это оставило бы страницу с 5 оставшимися байтами (8192 - 8187; непригодно для использования, конечно). Использование
DBCC PAGE
подтверждает мой расчет, показывая:m_slotCnt 29
(т.е. количество строк)m_freeCnt 5
(т.е. количество байтов, оставленных на странице)m_freeData 8129
(т.е. данные + заголовок страницы - 8033 + 96 - массив слотов не учитывается при вычислении «используемых» байтов)Что касается кучи
Кучи заполняют страницы данных немного по-другому. Они поддерживают очень приблизительную оценку количества места, оставшегося на странице. При взгляде на выходе DBCC, посмотрите на строку для:
PAGE HEADER: Allocation Status PFS (1:1)
. Вы увидите что-VALUE
то похожее0x60 MIXED_EXT ALLOCATED 0_PCT_FULL
(когда я смотрел на кластерную таблицу) или0x64 MIXED_EXT ALLOCATED 100_PCT_FULL
когда смотрел на таблицу кучи. Это оценивается для каждой транзакции, поэтому выполнение отдельных вставок, таких как выполняемый здесь тест, может показывать разные результаты для таблиц Clustered и Heap. Однако выполнение одной операции DML для всех 30 строк приведет к заполнению кучи, как и ожидалось.Однако ни одна из этих подробностей, касающихся кучи, не влияет напрямую на этот конкретный тест, поскольку обе версии таблицы умещаются в 30 строк, а остается только 146 байт. Этого места недостаточно для другой строки, независимо от Clustered или Heap.
Пожалуйста, имейте в виду, что этот тест довольно прост. Расчет фактического размера строки может быть очень сложным в зависимости от различных факторов, таких как:
SPARSE
сжатие данных, данные больших объектов и т. Д.Чтобы увидеть детали страницы данных, используйте следующий запрос:
** Значение 14-байт «Информация о версии» будет присутствовать , если база данных установлена либо
ALLOW_SNAPSHOT_ISOLATION ON
илиREAD_COMMITTED_SNAPSHOT ON
.источник
tempdb
? Мне удалось воспроизвести точные цифры, предоставленные ОП.ALLOW_SNAPSHOT_ISOLATION
. Я также только что попробовалtempdb
и увидел, что «информация о версии» не существует, следовательно, 30 строк подходят. Я обновлю, чтобы добавить новую информацию.Фактическая структура страницы данных довольно сложна. Хотя обычно указывается, что для пользовательских данных доступно 8060 байт на страницу, есть некоторые дополнительные издержки, которые нигде не учитываются, что приводит к такому поведению.
Однако вы могли заметить, что SQL Server на самом деле дает вам подсказку, что 31-я строка не помещается на странице. Чтобы следующий ряд поместился на той же странице,
avg_page_space_used_in_percent
значение должно быть ниже 100% - (100/31) = 96,774194, и в вашем случае оно намного выше этого.PS Я полагаю, что видел подробное, вплоть до байтового объяснения структуры страницы данных в одной из книг «Внутренние компоненты SQL Server» Калена Делани, но это было почти 10 лет назад, поэтому, пожалуйста, извините, что не помню больше подробностей. Кроме того, структура страницы имеет тенденцию меняться от версии к версии.
источник