我有一个数据库,我试图通过运行此 T-SQL 一次对所有表进行碎片整理:
SELECT
'ALTER INDEX all ON ' + name + ' REORGANIZE;' + CHAR(10) +
'ALTER INDEX all ON ' + name + ' REBUILD;'
FROM sys.tables
Run Code Online (Sandbox Code Playgroud)
然后将输出复制并粘贴到新的查询窗口并运行它。我没有错误,但我仍然有碎片。我也尝试分别运行这两个命令,但仍然存在碎片。注意:REORGANIZE Aaron已经让我知道这是不必要的,并且我知道我可以使用动态 sql 来自动执行此操作。
我运行这个以确定我仍然有碎片:
SELECT * FROM
sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, NULL)
WHERE avg_fragmentation_in_percent > 0
Run Code Online (Sandbox Code Playgroud)
我得到了:
database_id object_id index_id partition_number index_type_desc alloc_unit_type_desc index_depth index_level avg_fragmentation_in_percent fragment_count avg_fragment_size_in_pages page_count avg_page_space_used_in_percent record_count ghost_record_count version_ghost_record_count min_record_size_in_bytes max_record_size_in_bytes avg_record_size_in_bytes forwarded_record_count compressed_page_count
85 171147655 1 1 CLUSTERED INDEX IN_ROW_DATA 2 …Run Code Online (Sandbox Code Playgroud) 我已经使用 ALTER INDEX REBUILD 来删除索引碎片。在某些情况下,REBUILD 似乎并没有消除这种碎片。REBUILD 不去除碎片的原因是什么?似乎这种情况尤其发生在小索引上。
我正在做一个项目,我需要每天在一张表中更改大约 36K 条记录。我想知道什么会表现得更好:
对我来说,删除所有行并插入新行会更容易,但如果这会使表和索引碎片化并影响性能,那么我更愿意在可能的情况下进行更新并仅在必要时删除/插入。
这将是一项夜间服务,我不希望提高流程本身的速度。我更关心对这个表的查询性能,我已经有 8900 万条记录,以及这个每晚的过程将如何影响它。
对于这个夜间流程,我应该删除/插入记录还是应该更新现有记录(在可能的情况下)?
performance sql-server-2005 sql-server application-design fragmentation
我们有几个数据库,其中创建和删除了大量表。据我们所知,SQL Server 不对系统基表进行任何内部维护,这意味着它们会随着时间的推移变得非常碎片化并变得臃肿。这会给缓冲池带来不必要的压力,也会对计算数据库中所有表的大小等操作的性能产生负面影响。
有没有人建议尽量减少这些核心内部表上的碎片?一个明显的解决方案可以避免创建如此多的表(或在 tempdb 中创建所有临时表),但对于这个问题,我们假设应用程序没有这种灵活性。
编辑:进一步的研究显示了这个悬而未决的问题,它看起来密切相关,并表明某种形式的手动维护ALTER INDEX...REORGANIZE可能是一种选择。
初步研究
可以在以下位置查看有关这些表的元数据sys.dm_db_partition_stats:
-- The system base table that contains one row for every column in the system
SELECT row_count,
(reserved_page_count * 8 * 1024.0) / row_count AS bytes_per_row,
reserved_page_count/128. AS space_mb
FROM sys.dm_db_partition_stats
WHERE object_id = OBJECT_ID('sys.syscolpars')
AND index_id = 1
-- row_count: 15,600,859
-- bytes_per_row: 278.08
-- space_mb: 4,136
Run Code Online (Sandbox Code Playgroud)
但是,sys.dm_db_index_physical_stats似乎不支持查看这些表的碎片:
-- No fragmentation data is returned by sys.dm_db_index_physical_stats
SELECT *
FROM …Run Code Online (Sandbox Code Playgroud) 我的问题有两个部分。
我最近从 MSSQL 迁移到 Postgres,我们在 MSSQL 世界中创建数据库时所做的一件事是指定数据库和事务日志的初始大小。这减少了碎片并提高了性能,特别是如果事先知道数据库的“正常”大小。
我的数据库的性能随着大小的增长而下降。例如,我处理的工作负载通常需要 10 分钟。随着数据库的增长,这个时间会增加。执行 VACUUM、VACUUM FULL 和 VACUUM FULL ANALYZE 似乎不能解决问题。解决性能问题的是停止数据库,对驱动器进行碎片整理,然后进行 VACUUM FULL ANALYZE 使我的测试性能恢复到原来的 10 分钟。这让我怀疑是碎片化是导致我痛苦的原因。
我在 Postgres 中找不到任何关于保留表空间/数据库空间的参考。要么我使用了错误的术语,因此一无所获,要么在 Postgres 中有一种不同的方法来减轻文件系统碎片。
任何指针?
解决方案
提供的答案有助于确认我开始怀疑的内容。PostgreSQL 将数据库存储在多个文件中,这使得数据库可以增长而不必担心碎片化。默认行为是将这些文件与表数据一起打包,这对很少更改的表有好处,但对经常更新的表不利。
PostgreSQL 利用MVCC提供对表数据的并发访问。在此方案下,每次更新都会创建已更新行的新版本(这可能是通过时间戳或版本号,谁知道?)。旧数据不会立即删除,而是标记为删除。执行 VACUUM 操作时会发生实际删除。
这与填充因子有什么关系?表默认填充因子 100 完全填充表页,这反过来意味着表页内没有空间来保存更新的行,即更新的行将放置在与原始行不同的表页中。正如我的经验所示,这对性能不利。由于我的汇总表更新非常频繁(高达 1500 行/秒),我选择将填充因子设置为 20,即表的 20% 用于插入行数据,80% 用于更新数据。虽然这可能看起来过多,但为更新行保留的大量空间意味着更新行与原始行保持在同一页内,并且在 autovacuum 守护程序运行以删除过时行时表页未满。
为了“修复”我的数据库,我执行了以下操作。
ALTER TABLE "my_summary_table" SET (fillfactor = 20);重新运行我的测试,即使数据库达到我需要的数百万行,我也没有发现性能下降。
TL;DR …
我对如何在 MySQL 中维护索引以防止碎片化并以某种方式优化某些查询的执行进行了大量研究。
我熟悉计算表可用的最大空间与数据和索引使用的空间之间的比率的公式。
但是,我的主要问题仍未得到解答。也许这是因为我熟悉SQL Server中的索引维护,并且我倾向于认为在MySQL中它应该在某种程度上相似。
在 SQL Server 中,您可以有多个索引,并且每个索引都可以具有不同级别的碎片。然后,您可以选择一个并在该特定索引中执行“重组”或“重建”操作,而不会影响其余部分。
据我所知,没有这样的“表碎片”,并且 SQL Server 不提供任何工具来修复“表碎片”。它提供的是检查索引碎片的工具(理解为索引使用的页面数量与该页面的完整度和连续性之间的比率),以及内部和外部碎片。
所有这些都很容易理解,至少对我来说是这样。
现在,轮到在 MySQL 中维护索引时,只存在“表碎片”的概念,如上所述。
MySQL 中的一个表可以有多个索引,但是当我用那个著名的公式检查“碎片率”时,我没有看到每个索引的碎片,而是整个表。
当我想优化 MySQL 中的索引时,我不会选择要操作的特定索引(如在 SQL Server 中)。相反,我在整个表中执行“优化”操作,这可能会影响所有索引。
当在 MySQL 中优化表时,数据 + 索引使用的空间与整体空间之间的比率减少,这表明硬盘驱动器中进行了某种物理重组,这转化为物理空间的减少。但是,索引碎片不仅与物理空间有关,还与由于插入和更新而随时间发生变化的树结构有关。
最后,我在 InnoDB/MySQL 中得到了一张表。该表有 300 万条记录、105 列和 55 个索引。它是 1.5GB,不包括索引,即 2.1GB。
该表每天都会被访问数千次以进行更新、插入(我们实际上并没有删除记录)。
该表已创建多年,我确信没有人在维护任何索引。
我期待在那里找到一个巨大的碎片,但是当我按照规定执行碎片计算时
free_space / (data_length + index_length)
Run Code Online (Sandbox Code Playgroud)
事实证明,我只有 0.2% 的碎片。恕我直言,这是非常不现实的。
所以最大的问题是:
我的雇主正在考虑部署 NTFS 碎片整理软件,该软件使用 Windows MoveFile API 对打开的文件进行碎片整理。这将被部署到数千个运行 SQL 版本 2005-2012 和 Windows 版本 2003 和 2008 R2 的 SQL Server 服务器。如果重要的话,我正在谈论的产品是 PerfectDisk,但我相信有类似的程序以相同的方式工作。
到目前为止,除了偶尔出现的 I/O 性能问题之外,测试还没有发现很多问题,这并不奇怪,可以通过重新安排和调整碎片整理来解决。但是,我更关心数据损坏的风险。
这里有没有人有在数据库服务器上在生产中运行这种类型的软件的经验?您是否遇到过任何数据损坏?
尽管无法找到任何确凿的证据表明它存在问题,但我对此感到相当不安。
感谢您的任何答复。
编辑补充:幸好这个可怕的想法被遗忘了,部分原因可能是我给出了一些警告。
我有一个可容纳 100.000 名玩家的高分表,每天插入 2 次,每个玩家一条记录。在一天结束时,该表中索引的索引碎片为 99%。有没有办法通过调整设置来防止这种情况?
CREATE TABLE HighScore(
[id] [int] IDENTITY(1,1) NOT NULL,
[user] [int] NULL,
[player] [int] NULL,
[round] [tinyint] NULL,
[group] [int] NULL,
[rank] [int] NULL,
[delta] [int] NULL,
[roundpoints] [int] NULL,
[totalpoints] [int] NULL,
PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [HighScore_RoundGroup_Nidx] ON .[HighScore]
(
[round] ASC,
[group] ASC
)WITH …Run Code Online (Sandbox Code Playgroud) 我最近发现一个堆表有超过 70% 的碎片。所以我决定做一个
ALTER TABLE dbo.myTable REBUILD
Run Code Online (Sandbox Code Playgroud)
有趣的是,之后我有 20% 的碎片。从那以后,那张桌子上再也没有写过。所以我决定再做一次重建。
第2次后桌帽50%碎片化就更厉害了! 我真的不明白这怎么会发生......
我在任何地方都找不到关于此的明确资源,所以希望大师可以在这里给我一个答案。
我有一个非常大的表,我们必须向其中添加一列。聚集索引非常碎片化,我想做一个ALTER INDEX REBUILD清理它。
我通常也做一个 ALTER TABLE REBUILD在更改列时,因为这会清除该操作中的任何指针或拆分。
由于我们谈论的是聚集索引(本质上是表),所以我是否需要同时执行这两项操作?
我的怀疑是ALTER INDEX REBUILD在集群上不会更新所有的东西ALTER TABLE,但我也担心ALTER TABLE不会清理索引碎片。
fragmentation ×10
sql-server ×7
index ×3
heap ×1
mysql ×1
optimization ×1
performance ×1
postgresql ×1