Saya memiliki tabel di database produksi yang memiliki ukuran 525 GB, di mana 383 GB tidak digunakan:
Saya ingin mendapatkan kembali sebagian ruang ini, tetapi, sebelum mengacaukan DB produksi, saya menguji beberapa strategi pada tabel yang sama dalam tes DB dengan lebih sedikit data. Tabel ini memiliki masalah serupa:
Beberapa informasi tentang tabel:
- Faktor pengisian diatur ke 0
- Ada sekitar 30 kolom
- Salah satu kolom adalah LOB jenis gambar, dan itu menyimpan file dengan ukuran mulai dari beberapa KB hingga beberapa ratus MB
- Tabel tidak memiliki indeks hipotetis yang terkait dengannya
Server menjalankan SQL Server 2017 (RTM-GDR) (KB4505224) - 14.0.2027.2 (X64). Basis data menggunakan SIMPLE
model pemulihan.
Beberapa hal yang saya coba:
- Membangun kembali indeks:
ALTER INDEX ALL ON dbo.MyTable REBUILD
. Ini memiliki dampak yang dapat diabaikan. - Reorganisasi indeks:
ALTER INDEX ALL ON dbo.MyTable REORGANIZE WITH(LOB_COMPACTION = ON)
. Ini memiliki dampak yang dapat diabaikan. Menyalin kolom LOB ke tabel lain, menjatuhkan kolom, menciptakan kembali kolom, dan menyalin data kembali (seperti yang diuraikan dalam posting ini: Membebaskan Ruang SQL Server Tabel yang Tidak Terpakai ). Ini mengurangi ruang yang tidak digunakan, tetapi sepertinya hanya mengubahnya menjadi ruang yang digunakan:
Menggunakan utilitas bcp untuk mengekspor tabel, memotongnya, dan memuatnya kembali (seperti diuraikan dalam posting ini: Cara membebaskan ruang yang tidak digunakan untuk sebuah tabel ). Ini juga mengurangi ruang yang tidak digunakan dan meningkatkan ruang yang digunakan ke tingkat yang sama seperti gambar di atas.
- Meskipun tidak disarankan, saya mencoba perintah DBCC SHRINKFILE dan DBCC SHRINKDATABASE, tetapi tidak berdampak pada ruang yang tidak digunakan.
- Berlari
DBCC CLEANTABLE('myDB', 'dbo.myTable')
tidak membuat perbedaan - Saya sudah mencoba semua hal di atas, sekaligus mempertahankan gambar dan tipe data teks dan setelah mengubah tipe data menjadi varbinary (max) dan varchar (max).
- Saya mencoba mengimpor data ke tabel baru dalam database baru, dan ini juga hanya mengubah ruang yang tidak digunakan menjadi ruang yang digunakan. Saya menguraikan detail dari upaya ini di pos ini .
Saya tidak ingin melakukan upaya ini pada DB produksi jika ini adalah hasil yang dapat saya harapkan, jadi:
- Mengapa ruang yang tidak digunakan baru saja dikonversi ke ruang yang digunakan setelah beberapa upaya ini? Saya merasa tidak memiliki pemahaman yang baik tentang apa yang terjadi di bawah tenda.
- Apakah ada hal lain yang bisa saya lakukan untuk mengurangi ruang yang tidak digunakan tanpa menambah ruang yang digunakan?
EDIT: Inilah laporan Penggunaan Disk dan skrip untuk tabel:
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
Berikut adalah hasil dari mengeksekusi perintah dalam jawaban Max Vernon:
╔════════════╦═══════════╦════════════╦═════════════════╦══════════════════════╦════════════════════╗
║ TotalBytes ║ FreeBytes ║ TotalPages ║ TotalEmptyPages ║ PageBytesFreePercent ║ UnusedPagesPercent ║
╠════════════╬═══════════╬════════════╬═════════════════╬══════════════════════╬════════════════════╣
║ 9014280192║ 8653594624║ 1100376║ 997178 ║ 95.998700 ║ 90.621500 ║
╚════════════╩═══════════╩════════════╩═════════════════╩══════════════════════╩════════════════════╝
╔═════════════╦═══════════════════╦════════════════════╗
║ ObjectName ║ ReservedPageCount ║ UsedPageCount ║
╠═════════════╬═══════════════════╬════════════════════╣
║ dbo.MyTable ║ 5109090 ║ 2850245 ║
╚═════════════╩═══════════════════╩════════════════════╝
MEMPERBARUI:
Saya menjalankan yang berikut seperti yang disarankan oleh Max Vernon:
DBCC UPDATEUSAGE (N'<database_name>', N'<table_name>');
Dan inilah hasilnya:
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.
Ini memperbarui penggunaan disk untuk tabel:
Dan keseluruhan penggunaan disk:
Jadi, sepertinya masalahnya adalah bahwa penggunaan disk yang dilacak oleh SQL Server menjadi sangat tidak sinkron dengan penggunaan disk yang sebenarnya. Saya akan mempertimbangkan masalah ini terselesaikan, tetapi saya akan tertarik untuk mengetahui mengapa hal ini terjadi sejak awal!
DBCC UPDATEUSAGE
memperbarui ruang yang tidak digunakan dan jumlah halaman yang tidak digunakan. Sepertinya penggunaan disk dan informasi halaman yang dilaporkan oleh SQL Server sangat tidak sinkron - Saya memperbarui posting saya dengan detailnya. Saya ingin tahu tentang bagaimana ini akan terjadi di tempat pertama, tetapi setidaknya masalah ditemukan. Terima kasih atas semua bantuan Anda, saya sangat menghargainya!Anda bisa mengalami fragmentasi internal.
Apa fragmentasi halaman untuk tabel ini?
Dan apakah fragmentasi untuk baris berbeda dari halaman baris kosong?
Anda mengatakan Anda memiliki file yang beberapa KB.
SQL Server menyimpan semuanya di halaman 8060 Byte. Artinya, jika Anda memiliki baris (atau data tidak-baris) yaitu 4040 byte dan yang berikutnya serupa, itu tidak dapat masuk dalam halaman yang sama dan Anda akan menghabiskan setengah ruang Anda. Coba ubah ukuran baris Anda dengan menyimpan kolom panjang variabel (mulai dengan gambar misalnya) di tabel yang berbeda.
sumber
Apakah database dalam mode pemulihan penuh? Jika demikian, saat Anda melakukan psikiater, itu mencatat semua perubahan dan tidak akan menyusutkan seperti yang Anda harapkan. Tergantung pada jam operasi Anda, Anda bisa mengambil cadangan, beralih ke mode pemulihan pengiriman massal dan kemudian menjalankan menyusut pada file data. Setelah itu, Anda ingin menjalankan skrip indeks untuk memperbaiki / membangun kembali dan kembali ke pemulihan penuh. Itulah yang akan saya coba, tetapi sekali lagi, itu tergantung pada jam operasi Anda untuk semua ini.
sumber
Satu-satunya saat saya tidak dapat mengecilkan DB dan mendapatkan kembali ruang karena Anda tidak dapat mengecilkan DB di luar ukuran awal DB saat dibuat. Jadi misalnya, jika DB Anda adalah salinan dari DB produksi dan Anda pertama kali membuat DB pada 525GB, sql server tidak akan memungkinkan Anda untuk mengecilkan ukuran di bawah 525GB tidak peduli berapa banyak data yang Anda hapus dari DB. Tetapi jika DB dibuat di bawah 383GB dan kemudian tumbuh menjadi 525GB Anda seharusnya tidak mengalami kesulitan untuk mendapatkan kembali ruang tersebut. Saya sudah lama berpikir ini adalah pembatasan bodoh dan sewenang-wenang oleh Microsoft.
Kecilkan basis data hanya hingga ukuran awalnya yang ditetapkan setelah membuat basis data
sumber
Saya pernah mengalami masalah ini sebelumnya pada kotak produksi, yang perlu Anda lakukan adalah membangun kembali tabel dan indeks untuk setiap tabel (dalam urutan itu).
Inilah kueri yang saya gunakan untuk menjaga tabel di cek. Ini akan membantu Anda menentukan tabel mana yang perlu dibangun kembali, dan membuat query SQL yang perlu Anda jalankan. Kueri ini terbatas pada yang memiliki ruang lebih dari 1MB yang tidak digunakan dan rasio 5% yang tidak digunakan, sehingga Anda hanya membangun kembali apa yang benar-benar Anda butuhkan untuk fokus:
sumber