Gdybym podzielić tabelę z ntext
, text
lub image
typy danych i odbudowanie indeksu na jednej partycji z online = off
, czy to zablokować całą tabelę lub tylko danej partycji?
Gdybym podzielić tabelę z ntext
, text
lub image
typy danych i odbudowanie indeksu na jednej partycji z online = off
, czy to zablokować całą tabelę lub tylko danej partycji?
Miałem trochę czasu, aby na to spojrzeć, a ponieważ miałem już napisane niektóre skrypty demonstracyjne, sprawdzenie reszty było dość łatwe. Zróbmy konfigurację, a następnie spójrz na wyniki. Spowoduje to utworzenie niezbędnej partycjonowanej tabeli i indeksu.
CREATE PARTITION FUNCTION YourMom ( INT )
AS RANGE LEFT FOR VALUES ( 1000000, 2000000, 3000000, 4000000, 5000000 );
CREATE PARTITION SCHEME YourDad
AS PARTITION YourMom
ALL TO ( [PRIMARY] );
CREATE TABLE dbo.YourAuntDebbie
(
Id INT,
StopUsingDeprecatedDataTypes NTEXT
) ON YourDad (Id);
DECLARE @counter INT = 1;
WHILE @counter < 6
BEGIN
RAISERROR('Run number: %d', 0, 1, @counter) WITH NOWAIT;
INSERT dbo.YourAuntDebbie WITH ( TABLOCK ) ( Id, StopUsingDeprecatedDataTypes )
SELECT TOP 1000000 x.n + CASE WHEN @counter = 1 THEN 0
WHEN @counter = 2 THEN 1000000
WHEN @counter = 3 THEN 2000000
WHEN @counter = 4 THEN 3000000
WHEN @counter = 5 THEN 4000000
ELSE 0
END,
REPLICATE(N'A', x.n % 10000)
FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY @@ROWCOUNT ) AS n
FROM sys.messages AS m
CROSS JOIN sys.messages AS m2 ) AS x;
SET @counter += 1;
END;
CREATE CLUSTERED INDEX ix_whatever
ON dbo.YourAuntDebbie ( Id ) ON YourDad(Id);
To daje mi 5 partycji z 1 milionem wierszy i jedną pustą partycję.
SELECT OBJECT_NAME(p.object_id) AS table_name, p.partition_number, p.rows
FROM sys.partitions AS p
WHERE p.object_id = OBJECT_ID('dbo.YourAuntDebbie');
Fantazyjny stół:
+----------------+------------------+---------+
| table_name | partition_number | rows |
+----------------+------------------+---------+
| YourAuntDebbie | 1 | 1000000 |
| YourAuntDebbie | 2 | 1000000 |
| YourAuntDebbie | 3 | 1000000 |
| YourAuntDebbie | 4 | 1000000 |
| YourAuntDebbie | 5 | 1000000 |
| YourAuntDebbie | 6 | 0 |
+----------------+------------------+---------+
Oto sesja XE, której używam, aby zobaczyć, które blokady potrzebują przebudowy indeksu:
CREATE EVENT SESSION Locks
ON SERVER
ADD EVENT sqlserver.lock_acquired
( SET collect_resource_description = ( 1 )
ACTION ( sqlserver.sql_text )
WHERE ( sqlserver.equal_i_sql_unicode_string(sqlserver.database_name, N'Crap')
AND package0.equal_uint64(sqlserver.session_id, ( 61 )))),
ADD EVENT sqlserver.lock_released
( SET collect_resource_description = ( 1 )
ACTION ( sqlserver.sql_text )
WHERE ( sqlserver.database_name = N'Crap'
AND sqlserver.session_id = ( 61 )))
ADD TARGET package0.event_file
( SET filename = N'c:\temp\Locks' )
WITH ( MAX_MEMORY = 4096KB,
EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 30 SECONDS,
MAX_EVENT_SIZE = 0KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = ON,
STARTUP_STATE = OFF );
GO
Mając to na miejscu, mogę odbudować partycje, a następnie zagłębić się w XE.
ALTER EVENT SESSION Locks ON SERVER STATE = START;
ALTER INDEX ix_whatever ON dbo.YourAuntDebbie REBUILD PARTITION = 1 WITH (ONLINE = OFF);
GO
ALTER INDEX ix_whatever ON dbo.YourAuntDebbie REBUILD PARTITION = 2 WITH (ONLINE = OFF);
GO
ALTER INDEX ix_whatever ON dbo.YourAuntDebbie REBUILD PARTITION = 3 WITH (ONLINE = OFF);
GO
ALTER INDEX ix_whatever ON dbo.YourAuntDebbie REBUILD PARTITION = 4 WITH (ONLINE = OFF);
GO
ALTER INDEX ix_whatever ON dbo.YourAuntDebbie REBUILD PARTITION = 5 WITH (ONLINE = OFF);
GO
ALTER INDEX ix_whatever ON dbo.YourAuntDebbie REBUILD PARTITION = 6 WITH (ONLINE = OFF);
GO
ALTER EVENT SESSION Locks ON SERVER STATE = STOP;
Teraz na końcu umieszczę rozdrabnianie zdarzeń XE, ponieważ jest to dość brzydkie i nie ma powodu, aby wszyscy siedzieli, aby zobaczyć wyniki. Wykorzystam wyniki pierwszej partycji jako przykład, ale są one prawie identyczne dla wszystkich 6 partycji, nawet pustej.
Ograniczam wyniki aktualizacji tylko do blokad poziomu obiektu. To jedyne, na których nam zależy.
+---------------+-------------------------+----------------+--------------+-------+------------------+--------+
| EventName | EventDate | ObjectName | ResourceType | Mode | PARTITIONREBUILT | Events |
+---------------+-------------------------+----------------+--------------+-------+------------------+--------+
| lock_acquired | 2017-10-03 13:21:14.554 | YourAuntDebbie | OBJECT | SCH_M | PARTITION = 1 | 1 |
| lock_acquired | 2017-10-03 13:21:14.554 | YourAuntDebbie | OBJECT | SCH_S | PARTITION = 1 | 1 |
| lock_released | 2017-10-03 13:21:14.554 | YourAuntDebbie | OBJECT | SCH_S | PARTITION = 1 | 1 |
| lock_acquired | 2017-10-03 13:21:14.603 | YourAuntDebbie | OBJECT | S | PARTITION = 1 | 6 |
| lock_acquired | 2017-10-03 13:21:14.603 | YourAuntDebbie | OBJECT | SCH_S | PARTITION = 1 | 30 |
| lock_released | 2017-10-03 13:21:14.603 | YourAuntDebbie | OBJECT | SCH_S | PARTITION = 1 | 24 |
| lock_released | 2017-10-03 13:21:14.867 | YourAuntDebbie | OBJECT | SCH_M | PARTITION = 1 | 1 |
+---------------+-------------------------+----------------+--------------+-------+------------------+--------+
Z tego, co mogę powiedzieć, dla każdej partycji , A SCH-M
zamek jest wystawiony na początku przebudowy indeksu, a wydany pod koniec na stole .
Na przykład, jeśli uruchomię przebudowę pojedynczej partycji w transakcji:
BEGIN TRAN
ALTER INDEX ix_whatever ON dbo.YourAuntDebbie REBUILD PARTITION = 1 WITH (ONLINE = OFF);
--ROLLBACK
A następnie w innym oknie SSMS:
SELECT *
FROM dbo.YourAuntDebbie AS yad
WHERE yad.Id = 6000000
AND 1 = (SELECT 1)
Wybór jest zablokowany, dopóki nie zabiję odbudowy. Po zakończeniu plan kwerendy pokazuje, że wystąpiła eliminacja partycji , więc tylko przebudowana partycja 1 wydaje się działać na całą tabelę.
Mam nadzieję że to pomoże!
Oto okropny kod niszczenia sesji XE:
CREATE TABLE #Locks
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED,
WaitsXML XML
);
INSERT #Locks
( WaitsXML )
SELECT CONVERT(XML, event_data) AS TargetData
FROM sys.fn_xe_file_target_read_file( 'c:\temp\Locks*.xel', NULL, NULL, NULL);
WITH locks
AS ( SELECT l.WaitsXML.value('(/event/@name)[1]', 'VARCHAR(128)') AS EventName,
l.WaitsXML.value('(/event/@timestamp)[1]', 'DATETIME2(3)') AS EventDate,
l.WaitsXML.value('(event/data[@name="object_id"]/value)[1]', 'NUMERIC') AS ObjectId,
l.WaitsXML.value('(event/data[@name="resource_type"]/text)[1]', 'VARCHAR(128)') AS ResourceType,
l.WaitsXML.value('(event/data[@name="mode"]/text)[1]', 'VARCHAR(128)') AS Mode,
l.WaitsXML.value('(event/action[@name="sql_text"]/value)[1]', 'VARCHAR(128)') AS SQLText,
l.WaitsXML
FROM #Locks AS l )
SELECT locks.EventName,
locks.EventDate,
ISNULL(OBJECT_NAME(locks.ObjectId), 'Unknown') AS ObjectName,
locks.ResourceType,
locks.Mode,
SUBSTRING(
locks.SQLText,
CHARINDEX('PARTITION', locks.SQLText),
CHARINDEX('WITH', locks.SQLText, CHARINDEX('PARTITION', locks.SQLText))
- CHARINDEX('PARTITION', locks.SQLText)) AS PARTITIONREBUILT,
COUNT(*) AS Events
FROM locks
WHERE OBJECT_NAME(locks.ObjectId) = 'YourAuntDebbie'
--OR OBJECT_NAME(locks.ObjectId) IS NULL
GROUP BY ISNULL(OBJECT_NAME(locks.ObjectId), 'Unknown'),
SUBSTRING(
locks.SQLText,
CHARINDEX('PARTITION', locks.SQLText),
CHARINDEX('WITH', locks.SQLText, CHARINDEX('PARTITION', locks.SQLText))
- CHARINDEX('PARTITION', locks.SQLText)),
locks.EventName,
locks.EventDate,
locks.ResourceType,
locks.Mode
ORDER BY locks.EventDate;