У мене є таблиця у виробничій базі даних, що має розмір 525 ГБ, з яких 383 ГБ не використовується:
Я хотів би відновити частину цього простору, але, перш ніж поплутатися з виробничим БД, я тестую деякі стратегії на ідентичній таблиці в тестовій БД з меншою кількістю даних. Ця таблиця має подібну проблему:
Деякі відомості про таблицю:
- Коефіцієнт заповнення встановлюється на 0
- Є близько 30 стовпців
- Один із стовпців - це зображення типу LOB, і він зберігає файли розміром від кількох КБ до декількох сотень МБ
- У таблиці немає гіпотетичних індексів, пов'язаних з нею
Сервер працює під керуванням 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)
. Це мало незначний вплив. Скопіював стовпець LOB в іншу таблицю, скинув стовпчик, знову створив стовпець і скопіював дані назад (як зазначено в цій публікації: Вивільнення невикористаної таблиці простору SQL Server ). Це зменшило невикористаний простір, але, здавалося, просто перетворило його у використаний простір:
Використовував утиліту bcp, щоб експортувати таблицю, усікати її та перезавантажувати її (як зазначено в цій публікації: Як звільнити невикористаний простір для таблиці ). Це також зменшило невикористаний простір і збільшило використаний простір приблизно в такому ж розмірі, як наведене вище зображення.
- Хоча це не рекомендується, я спробував команди DBCC SHRINKFILE та DBCC SHRINKDATABASE, але вони не вплинули на невикористаний простір.
- Біг
DBCC CLEANTABLE('myDB', 'dbo.myTable')
не змінив значення - Я спробував усе вищезазначене як при збереженні типів зображень і тексту, так і після зміни типів даних на varbinary (max) та varchar (max).
- Я намагався імпортувати дані в нову таблицю в новій базі даних, і це також перетворило невикористаний простір у використаний простір. Деталі цієї спроби я окреслив у цій публікації .
Я не хочу робити цих спроб у виробничій БД, якщо ці результати я можу очікувати, тому:
- Чому після деяких із цих спроб невикористаний простір просто перетворюється на використаний простір? Я відчуваю, що я не дуже добре розумію, що відбувається під капотом.
- Чи можу я щось зробити, щоб зменшити невикористаний простір, не збільшуючи використаний простір?
EDIT: Ось звіт про використання диска та сценарій для таблиці:
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, були надзвичайно синхронізовані - я оновив свою публікацію з деталями. Мені цікаво, як це сталося б в першу чергу, але принаймні проблема була знайдена. Дякую за всю вашу допомогу, я дуже це вдячний!