У меня есть таблица в производственной базе данных, которая имеет размер 525 ГБ, из которых 383 ГБ не используется:
Я хотел бы освободить часть этого пространства, но, прежде чем связываться с производственной БД, я тестирую некоторые стратегии на идентичной таблице в тестовой БД с меньшим количеством данных. Эта таблица имеет похожую проблему:
Некоторая информация о столе:
- Коэффициент заполнения установлен на 0
- Есть около 30 столбцов
- Один из столбцов - это большой объект типа изображения, в котором хранятся файлы размером от нескольких КБ до нескольких сотен МБ.
- Таблица не имеет каких-либо гипотетических индексов, связанных с ней
Сервер работает под управлением SQL Server 2017 (RTM-GDR) (KB4505224) - 14.0.2027.2 (X64). База данных использует SIMPLE
модель восстановления.
Некоторые вещи, которые я пробовал:
- Перестройка индексов:
ALTER INDEX ALL ON dbo.MyTable REBUILD
. Это оказало незначительное влияние. - Реорганизация индексов:
ALTER INDEX ALL ON dbo.MyTable REORGANIZE WITH(LOB_COMPACTION = ON)
. Это оказало незначительное влияние. Скопировал столбец большого объекта в другую таблицу, удалил столбец, заново создал столбец и скопировал данные обратно (как описано в этом сообщении: освобождение таблицы SQL Server неиспользуемого пространства ). Это уменьшило неиспользуемое пространство, но, казалось, просто преобразовало его в используемое пространство:
Использовал утилиту bcp для экспорта таблицы, ее обрезания и перезагрузки (как описано в этом посте: Как освободить неиспользуемое пространство для таблицы ). Это также уменьшило неиспользуемое пространство и увеличило используемое пространство в той же степени, что и изображение выше.
- Хотя это и не рекомендуется, я пробовал команды DBCC SHRINKFILE и DBCC SHRINKDATABASE, но они не оказали никакого влияния на неиспользуемое пространство.
- Бег
DBCC CLEANTABLE('myDB', 'dbo.myTable')
не имел значения - Я пробовал все вышеперечисленное как при сохранении типов данных изображения и текста, так и после изменения типов данных на varbinary (max) и varchar (max).
- Я попытался импортировать данные в новую таблицу в новой базе данных, и это также только преобразовало неиспользуемое пространство в используемое пространство. Я изложил подробности этой попытки в этом посте .
Я не хочу делать эти попытки на производственной базе данных, если это ожидаемые результаты, поэтому:
- Почему неиспользованное пространство просто превращается в использованное пространство после некоторых из этих попыток? Я чувствую, что не понимаю, что происходит под капотом.
- Что еще я могу сделать, чтобы уменьшить неиспользуемое пространство, не увеличивая его?
РЕДАКТИРОВАТЬ: Вот отчет использования диска и сценарий для таблицы:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MyTable](
[Column1] [int] NOT NULL,
[Column2] [int] NOT NULL,
[Column3] [int] NOT NULL,
[Column4] [bit] NOT NULL,
[Column5] [tinyint] NOT NULL,
[Column6] [datetime] NULL,
[Column7] [int] NOT NULL,
[Column8] [varchar](100) NULL,
[Column9] [varchar](256) NULL,
[Column10] [int] NULL,
[Column11] [image] NULL,
[Column12] [text] NULL,
[Column13] [varchar](100) NULL,
[Column14] [varchar](6) NULL,
[Column15] [int] NOT NULL,
[Column16] [bit] NOT NULL,
[Column17] [datetime] NULL,
[Column18] [varchar](50) NULL,
[Column19] [varchar](50) NULL,
[Column20] [varchar](60) NULL,
[Column21] [varchar](20) NULL,
[Column22] [varchar](120) NULL,
[Column23] [varchar](4) NULL,
[Column24] [varchar](75) NULL,
[Column25] [char](1) NULL,
[Column26] [varchar](50) NULL,
[Column27] [varchar](128) NULL,
[Column28] [varchar](50) NULL,
[Column29] [int] NULL,
[Column30] [text] NULL,
CONSTRAINT [PK] PRIMARY KEY CLUSTERED
(
[Column1] ASC,
[Column2] ASC,
[Column3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column4] DEFAULT (0) FOR [Column4]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column5] DEFAULT (0) FOR [Column5]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column15] DEFAULT (0) FOR [Column15]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Column16] DEFAULT (0) FOR [Column16]
GO
Вот результаты выполнения команд в ответе Макса Вернона:
╔════════════╦═══════════╦════════════╦═════════════════╦══════════════════════╦════════════════════╗
║ TotalBytes ║ FreeBytes ║ TotalPages ║ TotalEmptyPages ║ PageBytesFreePercent ║ UnusedPagesPercent ║
╠════════════╬═══════════╬════════════╬═════════════════╬══════════════════════╬════════════════════╣
║ 9014280192║ 8653594624║ 1100376║ 997178 ║ 95.998700 ║ 90.621500 ║
╚════════════╩═══════════╩════════════╩═════════════════╩══════════════════════╩════════════════════╝
╔═════════════╦═══════════════════╦════════════════════╗
║ ObjectName ║ ReservedPageCount ║ UsedPageCount ║
╠═════════════╬═══════════════════╬════════════════════╣
║ dbo.MyTable ║ 5109090 ║ 2850245 ║
╚═════════════╩═══════════════════╩════════════════════╝
ОБНОВИТЬ:
Я запустил следующее по предложению Макса Вернона:
DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');
И вот был выход:
DBCC UPDATEUSAGE: Usage counts updated for table 'MyTable' (index 'PK_MyTable', partition 1):
USED pages (LOB Data): changed from (568025) to (1019641) pages.
RSVD pages (LOB Data): changed from (1019761) to (1019763) pages.
Это обновило использование диска для таблицы:
И общее использование диска:
Таким образом, похоже, что проблема заключалась в том, что использование диска, отслеживаемое SQL Server, стало совершенно несоответствующим фактическому использованию диска. Я считаю, что эта проблема решена, но мне было бы интересно узнать, почему это произошло бы в первую очередь!
источник
DBCC UPDATEUSAGE
обновление неиспользуемого пространства и количества неиспользуемых страниц. Похоже, что использование диска и информация о страницах, сообщаемая SQL Server, была чрезвычайно несинхронизирована - я обновил свой пост с подробностями. Мне интересно, как это могло бы произойти в первую очередь, но, по крайней мере, проблема была найдена. Спасибо за вашу помощь, я действительно ценю это!Возможно, вы испытываете внутреннюю фрагментацию.
Какова фрагментация страницы для этой таблицы?
И отличается ли фрагментация для строки в ряду от страниц вне строки?
Вы говорите, что у вас есть файлы размером в несколько килобайт.
SQL Server хранит все в 8060 байтовых страницах. Это означает, что если у вас есть строка (или данные вне строки), которая составляет 4040 байтов, а следующая аналогична, она не может поместиться в одну и ту же страницу, и вы потеряете половину своего пространства. Попробуйте изменить размер строки, сохранив столбцы переменной длины (например, начните с изображения) в другой таблице.
источник
База данных находится в режиме полного восстановления? Если да, то когда вы выполняете сжатие, оно регистрирует все изменения и не будет сокращать его так, как вы ожидаете. В зависимости от количества часов работы вы можете сделать резервную копию, переключиться в режим массового восстановления и затем выполнить сжатие файла данных. После этого вы захотите запустить индексные сценарии для восстановления / восстановления и вернуться к полному восстановлению. Это то, что я бы попробовал в любом случае, но опять же, это зависит от ваших часов работы для всего этого.
источник
Единственный раз, когда мне не удалось сжать БД и освободить пространство, это потому, что вы не можете сжать БД сверх первоначального размера БД, когда она была создана. Например, если ваша БД является копией рабочей БД и вы впервые создали БД на 525 ГБ, сервер sql не позволит вам уменьшить размер ниже 525 ГБ независимо от того, сколько данных вы удаляете из БД. Но если база данных была создана ниже 383 ГБ, а затем выросла до 525 ГБ, у вас не должно возникнуть проблем с восстановлением свободного пространства. Я долго думал, что это глупое и произвольное ограничение со стороны Microsoft.
Сжатие базы данных только до ее первоначального размера, который устанавливается после создания базы данных
источник
Я сталкивался с этой проблемой ранее на производственных блоках, вам нужно перестроить таблицы и индексы для каждой таблицы (в таком порядке).
Вот запрос, который я использую, чтобы контролировать таблицы. Это поможет вам определить, какие таблицы необходимо перестроить, и создать запросы SQL, которые необходимо выполнить. Этот запрос ограничен запросами с неиспользуемым пространством более 1 МБ и соотношением неиспользованных 5%, так что вы перестраиваете только то, на чем действительно нужно сосредоточиться:
источник