Estou fazendo um pequeno teste para verificar alguns detalhes do page split, e resolvi colocar o script aqui. Ele mostra algumas informações de alocação de páginas, page split, lista encadeada do índice clusterizado e informações de fragmentação.
Detalhe: o que eu pretendo verificar vai ficar para a segunda parte deste script, pois como estava ficando muito grande eu resolvi dividir o trem todo.
***************************************
/*
0) Cria o banco de dados Inside
*/
USE MASTER
GO
IF EXISTS (SELECT * FROM SYSDATABASES WHERE [Name] = 'Inside')
BEGIN
DROP DATABASE Inside
END
CREATE DATABASE Inside
ON
(
Name = 'Inside_data',
FileName = 'C:\Program Files\Microsoft SQL Server\MSSQL$INST2000_01\Data\Inside_data.mdf',
Size = 10MB,
FileGrowth = 5MB,
MaxSize = UNLIMITED
)
LOG ON
Name = 'Inside_log',
FileName = 'C:\Program Files\Microsoft SQL Server\MSSQL$INST2000_01\Data\Inside_log.mdf',
Size = 5MB,
FileGrowth = 3MB,
1 - PRIMEIRA ETAPA
=============================================
Demonstrar que o índice clusterizado mantém os registros ordenados de acordo com a sua chave e que dependendo da forma que os registros são inseridos, isto pode causar um alto número de page splits, gerando uma fragmentação desnecessária na tabela.
1.1) Cria nova tabela para verificar page splits
USE Inside
go
IF EXISTS (SELECT [Id] FROM SysObjects WHERE Name = 'PageSplit' and Xtype = 'U')
DROP TABLE PageSplit
CREATE TABLE PageSplit (
Code int NOT NULL,
Column2 char(3950) NOT NULL)
CREATE UNIQUE CLUSTERED INDEX PageSplit_ind on PageSplit(Code)
1.2) Os próximos passos vão mostrar a inserção de 4 registros, onde veremos um page split e analisaremos o padrão de alocação do SQL Server.
INSERT INTO PageSplit VALUES(1, 'SQL Server')
-- 2 páginas, uma é a raiz do índice clusterizado e a outra é a primeira página do nível folha.
DBCC ExtentInfo (Inside, 'PageSplit', 1)
-- Inside database id = 7
SELECT * FROM Master..sysdatabases
DBCC TRACEON (3604)
-- Index page (m_type = 2)
DBCC PAGE (7, 1, 15, 2)
-- Data page (m_type = 1)
DBCC PAGE (7, 1, 28, 3)
INSERT INTO PageSplit VALUES(99, 'SQL Server')
-- 2 registros (1 e 99)
-- Até o momento, nada de fragmentação lógica
DBCC SHOWCONTIG ('PageSplit', 1)
Esta inserção causa um page split, pois a página 28 não possui espaço para conter os 3 registros.
Como o índice clusterizado deve manter os registros no nível folha ordenados de acordo com suas chaves (no nosso caso somente o campo Code), o registro 99 será colocado em uma nova página (29) enquanto os registros 1 e 2 ficarão na página original (28).
INSERT INTO PageSplit VALUES(2, 'SQL Server')
3 páginas, uma é a raiz do índice clusterizado. As outras duas pertencem ao nível folha.
1 página com registros 1 e 2
1 página com registro 99
-- Registros 1 e 2
-- Registro 99
DBCC PAGE (7, 1, 29, 3)
-- Até o momento, nada de fragmentação lógica.
Ao invés de colocar o registro na página 29 (que continha espaço para suportar uma inserção), o SQL Server preferiu colocar o registro em uma nova página.
INSERT INTO PageSplit VALUES(98, 'SQL Server')
1 página com registro 98
Registros 1 e 2
m_prevPage = (0:0) (Está é a primeira página do índice clusterizado, então não existe prevPage)
m_nextPage = (1:30) (refere-se ao registro 98, que está em uma página fisicamente posterior que
a página do registro 99)
Registro 98
m_prevPage = (1:28) (Refere-se a página com os registros 1 e 2)
m_nextPage = (1:29) (refere-se ao registro 99, que fisicamente está em uma página anterior)
DBCC PAGE (7, 1, 30, 3)
Registro 99
m_prevPage = (1:30) (Refere-se a página com o registro 98)
m_nextPage = (0:0) (Esta é a última página do índice clusterizado, pois não existe nextPage)
Como a ordenação lógica das páginas está diferente da ordenação física, temos uma fragmentação lógica.
TRUNCATE TABLE PageSplit
1.3) Coloque no performance monitor o contador Page Splits/sec e execute o script abaixo.
Como são feitas inserções no início e no fim da tabela, vários page splits são necessários para manter o índice clusterizado ordenado.
SET NOCOUNT ON
DECLARE @contador INT
SET @contador = 1
WHILE (@contador < 50)
INSERT INTO PageSplit VALUES(@contador, 'SQL Server')
INSERT INTO PageSplit VALUES(100 - @contador, 'SQL Server')
SET @contador = @contador + 1
SET NOCOUNT OFF
Muitos page splits por segundo podem degradar a performance das operações de inserção e atualização dos registros, portanto deve-se prestar atenção em dois pontos:
- Fillfactor do seus índices.
- Ordenação do índice clusterizado.
Infelizmente não existe uma receita de bolo de como configurar suas tabelas, mas nada como conhecer a forma como o SQL Server trabalha para conseguir um melhor desempenho.
[]s
LUTI