2016-02-12 49 views
0

我的公司承諾在我們的Azure SQL數據庫表上使用GUID作爲主鍵的罪名(它實際上比這更糟糕:我們使用VARCHAR(36)而不是UNIQUEIDENTIFIER)。因此,我們最終得到分散的索引。他們看起來像這樣:爲什麼我的Azure SQL數據庫索引仍然是碎片?

CREATE TABLE OldTable (
    Id VARCHAR(36) PRIMARY KEY CLUSTERED NOT NULL DEFAULT NEWID(), 
    CreateTime DATETIME2 NOT NULL, 
    ... 
) 

我通過創建新表「修復」了問題。這一次,我爲CLUSTERED INDEX使用了不可變的,不斷增長的DATETIME2(例如CreateTime)列,並將VARCHAR(36)保留爲PRIMARY KEY,但這次是NONCLUSTERED。就像這樣:

CREATE TABLE NewTable (
    Id VARCHAR(36) PRIMARY KEY NONCLUSTERED NOT NULL DEFAULT NEWID(), 
    CreateTime DATETIME2 NOT NULL INDEX IX_NewTable_CreateTime CLUSTERED, 
) 

然後我 「抄襲」 行從舊錶使用INSERT INTO newtable的SELECT * FROM OldTable新表。最後,我重新命名了表格並刪除了舊錶格。生活似乎很好。

令我驚訝的是,幾周後,我發現NewTable有許多分段索引,平均分片高達80%!即使IX_NewTable_CreateTime報告了18%的碎片。

INSERT INTO是否支離破碎的索引? REBUILD索引能解決問題嗎?

+0

弗拉gmentation是不可避免的,隨着時間的推移,索引將會碎片化。你必須重建索引,因爲SQL azure不會爲你做這件事 – TheGameiswar

回答

8

碎片將取決於索引字段上的插入/更新頻率和索引頁的大小。

出於維護目的,您可以使用Azure自動化並創建一個循環腳本來檢查碎片索引並對其進行優化。

有一個運行手冊的畫廊只有爲:

enter image description here

關於這一點的最好的事情是,自動化是免費的,只要你不超過每月500運行的上場時間,時間您執行好,你將不必支付:)

我做了一個自定義的改進的相冊程序,隨意使用它太:

<# 
.SYNOPSIS 
    Indexes tables in a database if they have a high fragmentation 

.DESCRIPTION 
    This runbook indexes all of the tables in a given database if the fragmentation is 
    above a certain percentage. 
    It highlights how to break up calls into smaller chunks, 
    in this case each table in a database, and use checkpoints. 
    This allows the runbook job to resume for the next chunk of work even if the 
    fairshare feature of Azure Automation puts the job back into the queue every 30 minutes 

.PARAMETER SqlServer 
    Name of the SqlServer 

.PARAMETER Database 
    Name of the database 

.PARAMETER SQLCredentialName 
    Name of the Automation PowerShell credential setting from the Automation asset store. 
    This setting stores the username and password for the SQL Azure server 

.PARAMETER FragPercentage 
    Optional parameter for specifying over what percentage fragmentation to index database 
    Default is 20 percent 

.PARAMETER RebuildOffline 
    Optional parameter to rebuild indexes offline if online fails 
    Default is false 

.PARAMETER Table 
    Optional parameter for specifying a specific table to index 
    Default is all tables 

.PARAMETER SqlServerPort 
    Optional parameter for specifying the SQL port 
    Default is 1433 

.EXAMPLE 
    Update-SQLIndexRunbook -SqlServer "server.database.windows.net" -Database "Finance" -SQLCredentialName "FinanceCredentials" 

.EXAMPLE 
    Update-SQLIndexRunbook -SqlServer "server.database.windows.net" -Database "Finance" -SQLCredentialName "FinanceCredentials" -FragPercentage 30 

.EXAMPLE 
    Update-SQLIndexRunbook -SqlServer "server.database.windows.net" -Database "Finance" -SQLCredentialName "FinanceCredentials" -Table "Customers" -RebuildOffline $True 

.NOTES 
    AUTHOR: Matias Quaranta 
    LASTEDIT: Jan 10th, 2015 
#> 
workflow MyRunBook 
{ 
    param(
     [parameter(Mandatory=$True)] 
     [string] $SqlServer, 

     [parameter(Mandatory=$True)] 
     [string] $Database, 

     [parameter(Mandatory=$True)] 
     [string] $SQLCredentialName, 

     [parameter(Mandatory=$False)] 
     [int] $FragPercentage = 20, 

     [parameter(Mandatory=$False)] 
     [int] $SqlServerPort = 1433, 

     [parameter(Mandatory=$False)] 
     [boolean] $RebuildOffline = $False, 

     [parameter(Mandatory=$False)] 
     [string] $Table 

    ) 

    # Get the stored username and password from the Automation credential 
    $SqlCredential = Get-AutomationPSCredential -Name $SQLCredentialName 
    if ($SqlCredential -eq $null) 
    { 
     throw "Could not retrieve '$SQLCredentialName' credential asset. Check that you created this first in the Automation service." 
    } 

    $SqlUsername = $SqlCredential.UserName 
    $SqlPass = $SqlCredential.GetNetworkCredential().Password 

    InlineScript{ 

     # Define the connection to the SQL Database 
     $Conn = New-Object System.Data.SqlClient.SqlConnection("Server=tcp:$using:SqlServer,$using:SqlServerPort;Database=$using:Database;User ID=$using:SqlUsername;Password=$using:SqlPass;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;") 

     # Open the SQL connection 
     $Conn.Open() 

     # SQL command to find tables and their average fragmentation 
     $SQLCommandString = @" 
     SELECT a.object_id, b.name, (select name from sys.tables t where t.object_id = b.object_id) as tablename, avg_fragmentation_in_percent 
     FROM sys.dm_db_index_physical_stats (
       DB_ID(N'$Database') 
      , OBJECT_ID(0) 
      , NULL 
      , NULL 
      , NULL) AS a 
     JOIN sys.indexes AS b 
     ON a.object_id = b.object_id AND a.index_id = b.index_id; 
"@ 
     # Return the tables with their corresponding average fragmentation 
     $Cmd=new-object system.Data.SqlClient.SqlCommand($SQLCommandString, $Conn) 
     $Cmd.CommandTimeout=120 

     # Execute the SQL command 
     $FragmentedTable=New-Object system.Data.DataSet 
     $Da=New-Object system.Data.SqlClient.SqlDataAdapter($Cmd) 
     [void]$Da.fill($FragmentedTable) 

# Return the table names that have high fragmentation 
     ForEach ($FragTable in $FragmentedTable.Tables[0]) 
     { 

      If ($FragTable.avg_fragmentation_in_percent -ge $Using:FragPercentage) 
      { 
       Write-Verbose ("Index found : " + $FragTable.name + " on table:" + $FragTable.tablename) 

       $SQLCommandString = "EXEC('ALTER INDEX "+$FragTable.name+" ON "+$FragTable.tablename+" REBUILD')" 

       $Cmd2=new-object system.Data.SqlClient.SqlCommand($SQLCommandString, $Conn) 
       # Set the Timeout to be less than 30 minutes since the job will get queued if > 30 
       # Setting to 25 minutes to be safe. 
       $Cmd2.CommandTimeout=1500 

       Try 
       { 
        $Ds=New-Object system.Data.DataSet 
        $Da=New-Object system.Data.SqlClient.SqlDataAdapter($Cmd2) 
        [void]$Da.fill($Ds) 
       } 
       Catch 
       { 
        Write-Verbose ($FragTable.name +" on table "+$FragTable.tablename+" could NOT be indexed.") 
       } 
      } 
     } 

     $Conn.Close() 
    } 
    Write-Verbose "Finished Indexing" 
} 
+0

感謝Matias,如果你把它上傳到PowerShell Gallery,你可以得到你的腳本版本。 https://www.powershellgallery.com/ – anirudhgarg