Uphononongo lwesixhobo sasimahla seSQLIndexManager

Njengoko uyazi, ii-indexes zidlala indima ebalulekileyo kwi-DBMS, ukubonelela ngokukhawuleza ukukhangela kwiirekhodi ezifunekayo. Kungenxa yoko le nto kubaluleke kakhulu ukuba bakhonze ngexesha elifanelekileyo. Ininzi into ebhaliweyo malunga nokuhlalutya kunye nokwenza ngcono, kubandakanywa ne-Intanethi. Umzekelo, esi sihloko sisanda kuphononongwa kwi olu papasho.

Zininzi izisombululo ezihlawulweyo nezisimahla zoku. Umzekelo, kukho esele yenziwe isigqibo, ngokusekwe kwi-adaptive index optimization method.

Okulandelayo, makhe sijonge kusetyenziso lwasimahla SQLIndexManejala, ibhalwe ngu AlanDenton.

Umahluko ophambili wobugcisa phakathi kweSQLIndexManager kunye nenani lezinye ii-analogues zinikwa ngumbhali ngokwakhe apha ΠΈ apha.

Kweli nqaku, siza kujonga ngaphandle kwiprojekthi kunye nobuchule bokusebenza kwesi sisombululo sesoftware.

Ukuxoxa ngolu ncedo apha.
Ngokuhamba kwexesha, uninzi lwezimvo kunye neempazamo zalungiswa.

Ke, ngoku masiqhubele phambili kwi-SQLIndexManager eluncedo ngokwayo.

Isicelo sibhalwe kwi-C # .NET Framework 4.5 kwi-Visual Studio 2017 kwaye isebenzisa i-DevExpress kwiifom:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

kwaye ijongeka ngoluhlobo:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Zonke izicelo zenziwa kwezi fayile zilandelayo:

  1. isalathisi
  2. Umbuzo
  3. QueryEngine
  4. IsevaInfo

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Xa uqhagamshela kwisiseko sedatha kwaye uthumela imibuzo kwi-DBMS, isicelo sisayinwe ngolu hlobo lulandelayo:

ApplicationName=”SQLIndexManager”

Xa uzisa usetyenziso, ifestile yemodal iya kuvula ukongeza umdibaniso:
Uphononongo lwesixhobo sasimahla seSQLIndexManager

Apha, ukulayisha uluhlu olupheleleyo lwazo zonke iimeko ze-MS SQL Server efikelelekayo kuthungelwano lwasekhaya akukasebenzi.

Unokongeza umdibaniso usebenzisa elona qhosha lisekhohlo kwimenyu engundoqo:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Okulandelayo, le mibuzo ilandelayo kwi-DBMS iya kuqaliswa:

  1. Ukufumana ulwazi malunga neDBMS
    SELECT ProductLevel  = SERVERPROPERTY('ProductLevel')
         , Edition       = SERVERPROPERTY('Edition')
         , ServerVersion = SERVERPROPERTY('ProductVersion')
         , IsSysAdmin    = CAST(IS_SRVROLEMEMBER('sysadmin') AS BIT)
    

  2. Ukufumana uluhlu lwedatha ekhoyo eneempawu zabo ezimfutshane
    SELECT DatabaseName = t.[name]
         , d.DataSize
         , DataUsedSize  = CAST(NULL AS BIGINT)
         , d.LogSize
         , LogUsedSize   = CAST(NULL AS BIGINT)
         , RecoveryModel = t.recovery_model_desc
         , LogReuseWait  = t.log_reuse_wait_desc
    FROM sys.databases t WITH(NOLOCK)
    LEFT JOIN (
        SELECT [database_id]
             , DataSize = SUM(CASE WHEN [type] = 0 THEN CAST(size AS BIGINT) END)
             , LogSize  = SUM(CASE WHEN [type] = 1 THEN CAST(size AS BIGINT) END)
        FROM sys.master_files WITH(NOLOCK)
        GROUP BY [database_id]
    ) d ON d.[database_id] = t.[database_id]
    WHERE t.[state] = 0
        AND t.[database_id] != 2
        AND ISNULL(HAS_DBACCESS(t.[name]), 1) = 1
    

Emva kokuphumeza ezi scripts zingentla, iwindow iya kuvela iqulathe ulwazi olufutshane malunga nogcino lwedatha lomzekelo okhethiweyo we-MS SQL Server:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Kuyafaneleka ukuba uqaphele ukuba ulwazi olwandisiweyo luboniswa ngokusekelwe kumalungelo. Ukuba kukho sysadmin, emva koko unokukhetha idatha kwimboniselo sys.master_iifayile. Ukuba akukho malungelo anjalo, ngoko ke idatha encinci ibuyiselwa ngokulula ukuze ungacothi isicelo.

Apha kufuneka ukhethe i-database yomdla kwaye ucofe kwiqhosha elithi "Kulungile".

Okulandelayo, okushicilelweyo kulandelayo kuya kusetyenziswa kwisiseko sedatha ngasinye esikhethiweyo ukuhlalutya imeko yezalathisi:

Uhlalutyo lwesimo sesalathisi

declare @Fragmentation float=15;
declare @MinIndexSize bigint=768;
declare @MaxIndexSize bigint=1048576;
declare @PreDescribeSize bigint=32768;
SET NOCOUNT ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
IF OBJECT_ID('tempdb.dbo.#AllocationUnits') IS NOT NULL
DROP TABLE #AllocationUnits
CREATE TABLE #AllocationUnits (
ContainerID   BIGINT PRIMARY KEY
, ReservedPages BIGINT NOT NULL
, UsedPages     BIGINT NOT NULL
)
INSERT INTO #AllocationUnits (ContainerID, ReservedPages, UsedPages)
SELECT [container_id]
, SUM([total_pages])
, SUM([used_pages])
FROM sys.allocation_units WITH(NOLOCK)
GROUP BY [container_id]
HAVING SUM([total_pages]) BETWEEN @MinIndexSize AND @MaxIndexSize
IF OBJECT_ID('tempdb.dbo.#ExcludeList') IS NOT NULL
DROP TABLE #ExcludeList
CREATE TABLE #ExcludeList (ID INT PRIMARY KEY)
INSERT INTO #ExcludeList
SELECT [object_id]
FROM sys.objects WITH(NOLOCK)
WHERE [type] IN ('V', 'U')
AND ( [is_ms_shipped] = 1 )
IF OBJECT_ID('tempdb.dbo.#Partitions') IS NOT NULL
DROP TABLE #Partitions
SELECT [object_id]
, [index_id]
, [partition_id]
, [partition_number]
, [rows]
, [data_compression]
INTO #Partitions
FROM sys.partitions WITH(NOLOCK)
WHERE [object_id] > 255
AND [rows] > 0
AND [object_id] NOT IN (SELECT * FROM #ExcludeList)
IF OBJECT_ID('tempdb.dbo.#Indexes') IS NOT NULL
DROP TABLE #Indexes
CREATE TABLE #Indexes (
ObjectID         INT NOT NULL
, IndexID          INT NOT NULL
, IndexName        SYSNAME NULL
, PagesCount       BIGINT NOT NULL
, UnusedPagesCount BIGINT NOT NULL
, PartitionNumber  INT NOT NULL
, RowsCount        BIGINT NOT NULL
, IndexType        TINYINT NOT NULL
, IsAllowPageLocks BIT NOT NULL
, DataSpaceID      INT NOT NULL
, DataCompression  TINYINT NOT NULL
, IsUnique         BIT NOT NULL
, IsPK             BIT NOT NULL
, FillFactorValue  INT NOT NULL
, IsFiltered       BIT NOT NULL
, PRIMARY KEY (ObjectID, IndexID, PartitionNumber)
)
INSERT INTO #Indexes
SELECT ObjectID         = i.[object_id]
, IndexID          = i.index_id
, IndexName        = i.[name]
, PagesCount       = a.ReservedPages
, UnusedPagesCount = CASE WHEN ABS(a.ReservedPages - a.UsedPages) > 32 THEN a.ReservedPages - a.UsedPages ELSE 0 END
, PartitionNumber  = p.[partition_number]
, RowsCount        = ISNULL(p.[rows], 0)
, IndexType        = i.[type]
, IsAllowPageLocks = i.[allow_page_locks]
, DataSpaceID      = i.[data_space_id]
, DataCompression  = p.[data_compression]
, IsUnique         = i.[is_unique]
, IsPK             = i.[is_primary_key]
, FillFactorValue  = i.[fill_factor]
, IsFiltered       = i.[has_filter]
FROM #AllocationUnits a
JOIN #Partitions p ON a.ContainerID = p.[partition_id]
JOIN sys.indexes i WITH(NOLOCK) ON i.[object_id] = p.[object_id] AND p.[index_id] = i.[index_id] 
WHERE i.[type] IN (0, 1, 2, 5, 6)
AND i.[object_id] > 255
DECLARE @files TABLE (ID INT PRIMARY KEY)
INSERT INTO @files
SELECT DISTINCT [data_space_id]
FROM sys.database_files WITH(NOLOCK)
WHERE [state] != 0
AND [type] = 0
IF @@ROWCOUNT > 0 BEGIN
DELETE FROM i
FROM #Indexes i
LEFT JOIN sys.destination_data_spaces dds WITH(NOLOCK) ON i.DataSpaceID = dds.[partition_scheme_id] AND i.PartitionNumber = dds.[destination_id]
WHERE ISNULL(dds.[data_space_id], i.DataSpaceID) IN (SELECT * FROM @files)
END
DECLARE @DBID   INT
, @DBNAME SYSNAME
SET @DBNAME = DB_NAME()
SELECT @DBID = [database_id]
FROM sys.databases WITH(NOLOCK)
WHERE [name] = @DBNAME
IF OBJECT_ID('tempdb.dbo.#Fragmentation') IS NOT NULL
DROP TABLE #Fragmentation
CREATE TABLE #Fragmentation (
ObjectID         INT NOT NULL
, IndexID          INT NOT NULL
, PartitionNumber  INT NOT NULL
, Fragmentation    FLOAT NOT NULL
, PRIMARY KEY (ObjectID, IndexID, PartitionNumber)
)
INSERT INTO #Fragmentation (ObjectID, IndexID, PartitionNumber, Fragmentation)
SELECT i.ObjectID
, i.IndexID
, i.PartitionNumber
, r.[avg_fragmentation_in_percent]
FROM #Indexes i
CROSS APPLY sys.dm_db_index_physical_stats(@DBID, i.ObjectID, i.IndexID, i.PartitionNumber, 'LIMITED') r
WHERE i.PagesCount <= @PreDescribeSize
AND r.[index_level] = 0
AND r.[alloc_unit_type_desc] = 'IN_ROW_DATA'
AND i.IndexType IN (0, 1, 2)
IF OBJECT_ID('tempdb.dbo.#Columns') IS NOT NULL
DROP TABLE #Columns
CREATE TABLE #Columns (
ObjectID     INT NOT NULL
, ColumnID     INT NOT NULL
, ColumnName   SYSNAME NULL
, SystemTypeID TINYINT NULL
, IsSparse     BIT
, IsColumnSet  BIT
, MaxLen       INT
, PRIMARY KEY (ObjectID, ColumnID)
)
INSERT INTO #Columns
SELECT ObjectID     = [object_id]
, ColumnID     = [column_id]
, ColumnName   = [name]
, SystemTypeID = [system_type_id]
, IsSparse     = [is_sparse]
, IsColumnSet  = [is_column_set]
, MaxLen       = [max_length]
FROM sys.columns WITH(NOLOCK)
WHERE [object_id] IN (SELECT DISTINCT i.ObjectID FROM #Indexes i)
IF OBJECT_ID('tempdb.dbo.#IndexColumns') IS NOT NULL
DROP TABLE #IndexColumns
CREATE TABLE #IndexColumns (
ObjectID   INT NOT NULL
, IndexID    INT NOT NULL
, OrderID    INT NOT NULL
, ColumnID   INT NOT NULL
, IsIncluded BIT NOT NULL
, PRIMARY KEY (ObjectID, IndexID, ColumnID)
)
INSERT INTO #IndexColumns
SELECT ObjectID   = [object_id]
, IndexID    = [index_id]
, OrderID    = CASE WHEN [is_included_column] = 0 THEN [key_ordinal] ELSE [index_column_id] END
, ColumnID   = [column_id]
, IsIncluded = ISNULL([is_included_column], 0)
FROM sys.index_columns ic WITH(NOLOCK)
WHERE EXISTS(
SELECT *
FROM #Indexes i
WHERE i.ObjectID = ic.[object_id]
AND i.IndexID = ic.[index_id]
AND i.IndexType IN (1, 2)
)
IF OBJECT_ID('tempdb.dbo.#Lob') IS NOT NULL
DROP TABLE #Lob
CREATE TABLE #Lob (
ObjectID    INT NOT NULL
, IndexID     INT NOT NULL
, IsLobLegacy BIT
, IsLob       BIT
, PRIMARY KEY (ObjectID, IndexID)
)
INSERT INTO #Lob (ObjectID, IndexID, IsLobLegacy, IsLob)
SELECT c.ObjectID
, IndexID     = ISNULL(i.IndexID, 1)
, IsLobLegacy = MAX(CASE WHEN c.SystemTypeID IN (34, 35, 99) THEN 1 END)
, IsLob       = 0
FROM #Columns c
LEFT JOIN #IndexColumns i ON c.ObjectID = i.ObjectID AND c.ColumnID = i.ColumnID
WHERE c.SystemTypeID IN (34, 35, 99)
GROUP BY c.ObjectID
, i.IndexID
IF OBJECT_ID('tempdb.dbo.#Sparse') IS NOT NULL
DROP TABLE #Sparse
CREATE TABLE #Sparse (ObjectID INT PRIMARY KEY)
INSERT INTO #Sparse
SELECT DISTINCT ObjectID
FROM #Columns
WHERE IsSparse = 1
OR IsColumnSet = 1
IF OBJECT_ID('tempdb.dbo.#AggColumns') IS NOT NULL
DROP TABLE #AggColumns
CREATE TABLE #AggColumns (
ObjectID        INT NOT NULL
, IndexID         INT NOT NULL
, IndexColumns    NVARCHAR(MAX)
, IncludedColumns NVARCHAR(MAX)
, PRIMARY KEY (ObjectID, IndexID)
)
INSERT INTO #AggColumns
SELECT t.ObjectID
, t.IndexID
, IndexColumns = STUFF((
SELECT ', [' + c.ColumnName + ']'
FROM #IndexColumns i
JOIN #Columns c ON i.ObjectID = c.ObjectID AND i.ColumnID = c.ColumnID
WHERE i.ObjectID = t.ObjectID
AND i.IndexID = t.IndexID
AND i.IsIncluded = 0
ORDER BY i.OrderID
FOR XML PATH(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 2, '')
, IncludedColumns = STUFF((
SELECT ', [' + c.ColumnName + ']'
FROM #IndexColumns i
JOIN #Columns c ON i.ObjectID = c.ObjectID AND i.ColumnID = c.ColumnID
WHERE i.ObjectID = t.ObjectID
AND i.IndexID = t.IndexID
AND i.IsIncluded = 1
ORDER BY i.OrderID
FOR XML PATH(''), TYPE).value('(./text())[1]', 'NVARCHAR(MAX)'), 1, 2, '')
FROM (
SELECT DISTINCT ObjectID, IndexID
FROM #Indexes
WHERE IndexType IN (1, 2)
) t
SELECT i.ObjectID
, i.IndexID
, i.IndexName
, ObjectName       = o.[name]
, SchemaName       = s.[name]
, i.PagesCount
, i.UnusedPagesCount
, i.PartitionNumber
, i.RowsCount
, i.IndexType
, i.IsAllowPageLocks
, u.TotalWrites
, u.TotalReads
, u.TotalSeeks
, u.TotalScans
, u.TotalLookups
, u.LastUsage
, i.DataCompression
, f.Fragmentation
, IndexStats       = STATS_DATE(i.ObjectID, i.IndexID)
, IsLobLegacy      = ISNULL(lob.IsLobLegacy, 0)
, IsLob            = ISNULL(lob.IsLob, 0)
, IsSparse         = CAST(CASE WHEN p.ObjectID IS NULL THEN 0 ELSE 1 END AS BIT)
, IsPartitioned    = CAST(CASE WHEN dds.[data_space_id] IS NOT NULL THEN 1 ELSE 0 END AS BIT)
, FileGroupName    = fg.[name]
, i.IsUnique
, i.IsPK
, i.FillFactorValue
, i.IsFiltered
, a.IndexColumns
, a.IncludedColumns
FROM #Indexes i
JOIN sys.objects o WITH(NOLOCK) ON o.[object_id] = i.ObjectID
JOIN sys.schemas s WITH(NOLOCK) ON s.[schema_id] = o.[schema_id]
LEFT JOIN #AggColumns a ON a.ObjectID = i.ObjectID AND a.IndexID = i.IndexID
LEFT JOIN #Sparse p ON p.ObjectID = i.ObjectID
LEFT JOIN #Fragmentation f ON f.ObjectID = i.ObjectID AND f.IndexID = i.IndexID AND f.PartitionNumber = i.PartitionNumber
LEFT JOIN (
SELECT ObjectID      = [object_id]
, IndexID       = [index_id]
, TotalWrites   = NULLIF([user_updates], 0)
, TotalReads    = NULLIF([user_seeks] + [user_scans] + [user_lookups], 0)
, TotalSeeks    = NULLIF([user_seeks], 0)
, TotalScans    = NULLIF([user_scans], 0)
, TotalLookups  = NULLIF([user_lookups], 0)
, LastUsage     = (
SELECT MAX(dt)
FROM (
VALUES ([last_user_seek])
, ([last_user_scan])
, ([last_user_lookup])
, ([last_user_update])
) t(dt)
)
FROM sys.dm_db_index_usage_stats WITH(NOLOCK)
WHERE [database_id] = @DBID
) u ON i.ObjectID = u.ObjectID AND i.IndexID = u.IndexID
LEFT JOIN #Lob lob ON lob.ObjectID = i.ObjectID AND lob.IndexID = i.IndexID
LEFT JOIN sys.destination_data_spaces dds WITH(NOLOCK) ON i.DataSpaceID = dds.[partition_scheme_id] AND i.PartitionNumber = dds.[destination_id]
JOIN sys.filegroups fg WITH(NOLOCK) ON ISNULL(dds.[data_space_id], i.DataSpaceID) = fg.[data_space_id] 
WHERE o.[type] IN ('V', 'U')
AND (
f.Fragmentation >= @Fragmentation
OR
i.PagesCount > @PreDescribeSize
OR
i.IndexType IN (5, 6)
)

Njengoko kunokubonwa kwimibuzo ngokwayo, iitafile zexeshana zisetyenziswa rhoqo. Oku kwenziwa ukwenzela ukuba kungabikho ukubuyiswa kwakhona, kwaye kwimeko yeskimu esikhulu, isicwangciso sinokuveliswa ngokufanayo xa ufaka idatha, ekubeni ukufaka iinguqu zetafile kunokwenzeka kuphela kwintambo enye.

Emva kokuphumeza okubhalwe ngasentla, iwindow enetafile yesalathisi iya kuvela:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Ungabonisa nolunye ulwazi oluneenkcukacha apha, olunje:

  1. idatabase
  2. inani lamacandelo
  3. umhla kunye nexesha lomnxeba wokugqibela
  4. ucinezelo
  5. iqela lefayile

njalo njalo.
Izithethi ngokwazo zinokulungiswa:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Kwiiseli zekholamu yokuLungisa, unokukhetha ukuba yeyiphi intshukumo eya kwenziwa ngexesha lokuphucula. Kwakhona, xa ukuskena kugqityiwe, intshukumo engagqibekanga iyakhethwa ngokusekelwe kwizicwangciso ezikhethiweyo:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Kufuneka ukhethe izalathisi ezifunwayo zokuqhubekekiswa.

Usebenzisa imenyu engundoqo, ungagcina okushicilelweyo (iqhosha elifanayo liqala inkqubo yesalathisi ngokwaso):

Uphononongo lwesixhobo sasimahla seSQLIndexManager

kwaye ugcine itafile kwiifomati ezahlukeneyo (iqhosha elifanayo likuvumela ukuba uvule izicwangciso ezineenkcukacha zokuhlalutya kunye nokwandisa izalathisi):

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Unokuhlaziya ulwazi ngokunqakraza kwiqhosha lesithathu ekhohlo kwimenyu enkulu ecaleni kweglasi yokukhulisa.

Iqhosha elineglasi yokukhulisa likuvumela ukuba ukhethe ugcino lwedatha olufunwayo ukuze luqwalaselwe.

Okwangoku akukho nkqubo yoncedo ebanzi. Ke ngoko, ucofa iqhosha elithi "?" izakubangela ukuba kuvele ifestile yemodal equlathe ulwazi olusisiseko malunga nemveliso yesoftware:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Ukongeza kuyo yonke into echazwe ngasentla, imenyu ephambili inebar yokukhangela:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Xa uqala inkqubo yokwandisa isalathisi:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Unako kwakhona ukujonga ilog yeentshukumo ezenziweyo emazantsi efestile:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Kwifestile yezicwangciso ezineenkcukacha zohlalutyo lwesalathiso kunye nokwenza ngcono, ungaqwalasela ezinye iinketho ezifihlakeleyo:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Izicelo zesicelo:

  1. yenza ukuba kwenzeke ukuhlaziya izibalo ngokukhethayo hayi kwizalathisi kuphela kwaye nangeendlela ezahlukeneyo (ukuhlaziya ngokupheleleyo okanye ngokuyinxenye)
  2. yenza kube nokwenzeka hayi ukukhetha kuphela isiseko sedatha, kodwa nabancedisi abohlukeneyo (oku kulula kakhulu xa kukho imizekelo emininzi ye-MS SQL Server)
  3. Ukuguquguquka okukhulu ekusebenziseni, kuyacetyiswa ukuba usonge imiyalelo kwiilayibrari kwaye uyikhuphe kwimiyalelo yePowerShell, njengoko kusenziwa, umzekelo, apha:
  4. dbatools.io/commands
  5. yenza kube nokwenzeka ukugcina nokutshintsha izicwangciso zobuqu zombini kwisicelo siphela kwaye, ukuba kuyimfuneko, kumzekelo ngamnye we-MS SQL Server kunye nedatabase nganye.
  6. Ukusuka kumanqaku 2 kunye no-4, kulandela ukuba ufuna ukwenza amaqela ngoovimba beenkcukacha kunye namaqela ngemizekelo ye-MS SQL Server, apho izicwangciso ziyafana.
  7. Khangela izalathisi eziphindiweyo (ezipheleleyo nezingagqibekanga, ezinokwahluka kancinane okanye zahluke kuphela kwimiqolo ebandakanyiweyo)
  8. Ekubeni iSQLIndexManager isetyenziswa kuphela kwi-MS SQL Server DBMS, kuyimfuneko ukubonisa oku egameni, umzekelo, ngolu hlobo lulandelayo: SQLIndexManager ye-MS SQL Server.
  9. Hambisa zonke iindawo ezingezizo ze-GUI zesicelo kwiimodyuli ezahlukileyo kwaye uzibhale kwakhona kwiNET Core 2.1

Ngexesha lokubhalwa, into yesi-6 yeminqweno iphuhliswa ngokusebenzayo kwaye sele kukho inkxaso ngendlela yokukhangela iimpinda ezipheleleyo nezifanayo:

Uphononongo lwesixhobo sasimahla seSQLIndexManager

Imithombo

umthombo: www.habr.com

Yongeza izimvo