pedant
Goto Top

WSUS auf Server 2016 - Datenbank bereinigen?

Hallo,

vor einiger Zeit hatte ich den WSUS neu installiert und dieses mal auf einen Server 2016.

Ich hatte zuvor einmal wöchentlich eine automatische Bereinigung laufen lassen.
So oder ähnlich: https://wsus.de/de/serverbereinigung1
(Die genaue Quelle für das Skript weiß ich nicht mehr.)

Seit der Neuinstallation habe ich das nicht gemacht.
Ich hatte die automatische Bereinigung (per PS-Skript) nicht wieder eingerichtet, da ich mir unsicher war, ob das auch für die aktuellen Versionen von WSUS/MSSQL/PS funktionieren oder mir alles kaputt machen würde.

Jetzt ist, nicht überraschenderweise, die Datenbank recht groß (vermutlich zu groß) geworden.
- Die letzten Synchronisationen schlugen fehl.
- Der Server-Cleanup-Wizard der WSUS-Management-Console schlägt nach einiger Zeit fehl.
(Beides vermutlich wegen der Datenbankgröße)

Meine Frage
Ist dieses Skript auch für mein aktuelles 2016er-System noch funktionabel oder sollte ich ein anderes verwenden und wenn welches?
https://wsus.de/de/serverbereinigung1
Mir geht es primär darum, die Datenbankgröße im Zaum zu halten und nicht so sehr darum ein paar GB Plattenplatz bei den Updates einzusparen.


Mein System

OS
Windows Server 2016 Standard 64-bit (en-US) 1607 [10.0.14393.1480]

PowerShell
PSVersion: 5.1.14393.1480
CLRVersion: 4.0.30319.42000
Quelle: PS C:\> $PSVersionTable

.Net-Framework
v2.0: 2.0.50727.4927 SP2
v3.0: 3.0.30729.4926 SP2
v3.5: 3.5.30729.4926 SP1
v4.0: Client 4.0.0.0
v4.x: Client 4.7.02053, Full 4.7.02053

WSUS
Version: 10.0.14393.0
Quelle: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Update Services\Server\Setup], VersionString=

SQL
Microsoft SQL Server 2016 Express Edition (64-bit)
(Versionseinschränkung: 10GB max. Datenbankgröße)
Version: 13.0.4422.0
Quelle: Microsoft SQL Server Management Studio

SUS-DB
SUSDB.mdf: 10.678.697.984 Bytes (~9,95 GB)
SUSDB_log.ldf: 612.368.384 Bytes (~0,57 GB)
Quelle: C:\MSSQL\Data\> dir s*.*

Vielen Dank im Voraus
Gruß Frank

Content-Key: 343905

Url: https://administrator.de/contentid/343905

Printed on: April 24, 2024 at 15:04 o'clock

Member: BassFishFox
Solution BassFishFox Jul 19, 2017 at 19:03:14 (UTC)
Goto Top
Hallo,

Wieviele Clients hast Du denn? Von der Anzahl der Clients und den dazu gehoerigen Verwaltungsinformationen ist die Groesse der DB abhaengig.

Ist dieses Skript auch für mein aktuelles 2016er-System noch funktionabel

Probiere es doch aus. face-wink

Den Trick mit dem "shrink" einer SUSDB per SQL Server Management Studio kennt Du?

BFF
Member: Pedant
Pedant Jul 20, 2017 at 08:17:51 (UTC)
Goto Top
Hallo BassFishFox,

zur Zeit sind es 38 Clients.

Zitat von @BassFishFox:
Probiere es doch aus

Ich habe mir die WSUS-Serverbereinigung.ps1 mal genauer angesehen.
Sie scheint nichts anderes zu tun, als beim Cleanup-Manager ein paar Einstellungen vorzunehmen und ihn dann zu starten.
Den Teil könnte ich durchaus ausprobieren.

Gestartet wird das PS-Skript von einer WSUS-Serverbereinigung.cmd und die nutzt auch eine WsusDBMaintenance.sql und da bin ich mir unsicher, ob dieses SQL-Skript zur aktuellen Datenbankstruktur passt.

Nichts von dem was ich zum Thema WSUS-Bereinigen im Netz bisher fand erwähnt die Existenz von WSUS 10 oder MSSQL 2016.
Ich meine, wir haben 2017, irgend jemand außer mir, wird doch auch den Kram vom letzten Jahr schon nutzen und ich hatte gehofft, dass der mir sagt:
"Nimm dieses oder jenes Sktript oder behalt dein altes und alles ist gut."

Wenn ich tatsächlich der Erste und Einzige bin, werde ich nicht umhin kommen ein Datenbank-Backup zu erstellen, das Skript auszuprobieren und zu hoffen, dass es keine kleinen, fiesen Fehler produziert, die erst auffallen, wenn man das Skript als mögliche Ursache schon nicht mehr auf dem Schirm hat.

Zitat von @BassFishFox:
Den Trick mit dem "shrink" einer SUSDB per SQL Server Management Studio kennt Du?
Per Management Studio nicht, aber vielleicht ist der Trick in der erwähnten SQL-Datei enthalten.

Ich zeig mal was ich bei der vorhergehenden Installation regelmäßig nutzte:

WSUS-Serverbereinigung.cmd
@echo off 

set TOOLPATH=D:\WSUS-Tools\WSUS-CleanUp
set WSUS-SERVER=MeinServer
set PORT-NUMBER=8530
set DATABASECONNECT=np:\\.\pipe\MSSQL$SQLEXPRESS\sql\query
set SQLCMD="C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\SQLCMD.EXE"  

set LOG=%TOOLPATH%\WSUS-CleanUp.log
set CLEANUPSCRIPT=%TOOLPATH%\WSUS-Serverbereinigung.ps1
set SQLSCRIPT=%TOOLPATH%\WsusDBMaintenance.sql

echo.> "%LOG%"  
echo Start der Datenbankbereinigung: %DATE% um %TIME% Uhr>> "%LOG%"  
echo =============================================================>> "%LOG%"  
echo.>> "%LOG%"  
echo Cleanup Report für [%WSUS-SERVER%] (Port: %PORT-NUMBER%)>> "%LOG%"  
echo ------------------------------------------------------------->> "%LOG%"  
REM "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" Set-ExecutionPolicy Remotesigned  
"%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" "%CLEANUPSCRIPT%" %WSUS-SERVER% %PORT-NUMBER%>> "%LOG%"  
echo ------------------------------------------------------------->> "%LOG%"  
echo Ende Dateibereinigung: %DATE% um %TIME% Uhr>> "%LOG%"  
echo.>> "%LOG%"  
echo SQL Server Wartung (Connect %DATABASECONNECT%)>> "%LOG%"  
echo ------------------------------------------------------------->> "%LOG%"  
%SQLCMD% -I -S %DATABASECONNECT% -i %SQLSCRIPT%>> "%LOG%"  
%SQLCMD% -I -S %DATABASECONNECT% -dSUSDB -Q "DBCC SHRINKDATABASE(N'SUSDB' )">> "%LOG%"  
%SQLCMD% -I -S %DATABASECONNECT% -dSUSDB -Q "DBCC SHRINKFILE (N'SUSDB' , 0, TRUNCATEONLY)">> "%LOG%"  
%SQLCMD% -I -S %DATABASECONNECT% -dSUSDB -Q "DBCC SHRINKFILE (N'SUSDB_log' , 0, TRUNCATEONLY)">> "%LOG%"  
echo ------------------------------------------------------------->> "%LOG%"  
echo Ende der Datenbankbereinigung: %DATE% um %TIME% Uhr>> "%LOG%"  
echo =============================================================>> "%LOG%"  
exit

WSUS-Serverbereinigung.ps1
#Region VARIABLES   

# WSUS Connection Parameters: 
[String]$updateServer = $args 
[Boolean]$useSecureConnection = $False 
[Int32]$portNumber = $args[1]   

# Cleanup Parameters: 
# Decline updates that have not been approved for 30 days or more, 
# are not currently needed by any clients, and are superseded by an aproved update. 

[Boolean]$supersededUpdates = $True 
# Decline updates that aren't approved and have been expired my Microsoft.  
[Boolean]$expiredUpdates = $True 
# Delete updates that are expired and have not been approved for 30 days or more. 
[Boolean]$obsoleteUpdates = $True 
# Delete older update revisions that have not been approved for 30 days or more. 
[Boolean]$compressUpdates = $True 
# Delete computers that have not contacted the server in 30 days or more. 
[Boolean]$obsoleteComputers = $True 
# Delete update files that aren't needed by updates or downstream servers.  
[Boolean]$unneededContentFiles = $True   

#EndRegion VARIABLES  

#Region SCRIPT   

# Load .NET assembly 
[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")     

# Connect to WSUS Server 
$Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($updateServer,$useSecureConnection,$portNumber)  

# Perform Cleanup 
$CleanupManager = $Wsus.GetCleanupManager() 
$CleanupScope = New-Object Microsoft.UpdateServices.Administration.CleanupScope($supersededUpdates,$expiredUpdates,$obsoleteUpdates,$compressUpdates,$obsoleteComputers,$unneededContentFiles) 
$CleanupManager.PerformCleanup($CleanupScope)
   
#EndRegion SCRIPT

WsusDBMaintenance.sql
/******************************************************************************
This sample T-SQL script performs basic maintenance tasks on SUSDB
1. Identifies indexes that are fragmented and defragments them. For certain
   tables, a fill-factor is set in order to improve insert performance.
   Based on MSDN sample at http://msdn2.microsoft.com/en-us/library/ms188917.aspx
   and tailored for SUSDB requirements
2. Updates potentially out-of-date table statistics.
******************************************************************************/

USE SUSDB;
GO
SET NOCOUNT ON;

-- Rebuild or reorganize indexes based on their fragmentation levels
DECLARE @work_to_do TABLE (
    objectid int
    , indexid int
    , pagedensity float
    , fragmentation float
    , numrows int
)

DECLARE @objectid int;
DECLARE @indexid int;
DECLARE @schemaname nvarchar(130); 
DECLARE @objectname nvarchar(130); 
DECLARE @indexname nvarchar(130); 
DECLARE @numrows int
DECLARE @density float;
DECLARE @fragmentation float;
DECLARE @command nvarchar(4000); 
DECLARE @fillfactorset bit
DECLARE @numpages int

-- Select indexes that need to be defragmented based on the following
-- * Page density is low
-- * External fragmentation is high in relation to index size
PRINT 'Estimating fragmentation: Begin. ' + convert(nvarchar, getdate(), 121)   
INSERT @work_to_do
SELECT
    f.object_id
    , index_id
    , avg_page_space_used_in_percent
    , avg_fragmentation_in_percent
    , record_count
FROM 
    sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'SAMPLED') AS f  
WHERE
    (f.avg_page_space_used_in_percent < 85.0 and f.avg_page_space_used_in_percent/100.0 * page_count < page_count - 1)
    or (f.page_count > 50 and f.avg_fragmentation_in_percent > 15.0)
    or (f.page_count > 10 and f.avg_fragmentation_in_percent > 80.0)

PRINT 'Number of indexes to rebuild: ' + cast(@@ROWCOUNT as nvarchar(20))  

PRINT 'Estimating fragmentation: End. ' + convert(nvarchar, getdate(), 121)  

SELECT @numpages = sum(ps.used_page_count)
FROM
    @work_to_do AS fi
    INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id
    INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id

-- Declare the cursor for the list of indexes to be processed.
DECLARE curIndexes CURSOR FOR SELECT * FROM @work_to_do

-- Open the cursor.
OPEN curIndexes

-- Loop through the indexes
WHILE (1=1)
BEGIN
    FETCH NEXT FROM curIndexes
    INTO @objectid, @indexid, @density, @fragmentation, @numrows;
    IF @@FETCH_STATUS < 0 BREAK;

    SELECT 
        @objectname = QUOTENAME(o.name)
        , @schemaname = QUOTENAME(s.name)
    FROM 
        sys.objects AS o
        INNER JOIN sys.schemas as s ON s.schema_id = o.schema_id
    WHERE 
        o.object_id = @objectid;

    SELECT 
        @indexname = QUOTENAME(name)
        , @fillfactorset = CASE fill_factor WHEN 0 THEN 0 ELSE 1 END
    FROM 
        sys.indexes
    WHERE
        object_id = @objectid AND index_id = @indexid;

    IF ((@density BETWEEN 75.0 AND 85.0) AND @fillfactorset = 1) OR (@fragmentation < 30.0)
        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REORGANIZE';  
    ELSE IF @numrows >= 5000 AND @fillfactorset = 0
        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD WITH (FILLFACTOR = 90)';  
    ELSE
        SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD';  
    PRINT convert(nvarchar, getdate(), 121) + N' Executing: ' + @command;  
    EXEC (@command);
    PRINT convert(nvarchar, getdate(), 121) + N' Done.';  
END

-- Close and deallocate the cursor.
CLOSE curIndexes;
DEALLOCATE curIndexes;


IF EXISTS (SELECT * FROM @work_to_do)
BEGIN
    PRINT 'Estimated number of pages in fragmented indexes: ' + cast(@numpages as nvarchar(20))  
    SELECT @numpages = @numpages - sum(ps.used_page_count)
    FROM
        @work_to_do AS fi
        INNER JOIN sys.indexes AS i ON fi.objectid = i.object_id and fi.indexid = i.index_id
        INNER JOIN sys.dm_db_partition_stats AS ps on i.object_id = ps.object_id and i.index_id = ps.index_id

    PRINT 'Estimated number of pages freed: ' + cast(@numpages as nvarchar(20))  
END
GO


--Update all statistics
PRINT 'Updating all statistics.' + convert(nvarchar, getdate(), 121)   
EXEC sp_updatestats
PRINT 'Done updating statistics.' + convert(nvarchar, getdate(), 121)   
GO

--Delete sync history
PRINT 'Delete sync history.' + convert(nvarchar, getdate(), 121)  
DELETE FROM tbEventInstance WHERE EventNamespaceID = '2' AND EVENTID IN ('381', '382', '384', '386', '387', '389')  
GO

Die aufgezeigten Skripte hatte ich im Netz gefunden und weitgehend unverändert belassen.
(Pfade und Namen habe ich in der cmd natürlich angepasst.)

Gruß Frank
Member: Pedant
Pedant Jul 27, 2017 at 09:23:09 (UTC)
Goto Top
Hallo WSUS-2008/2012-Admins,

da ich anscheinend der Erste und momentan noch der Einzige bin, der WSUS auf 2016 (Server und MSSQL) betreibt, habe ich die Datenbank gesichert und das Skript, wie BassFishFox es eingangs vorschlug, einfach ausprobiert.

Aus der WSUS-Serverbereinigung.cmd habe ich noch das REM aus Zeile 19 entfernt:
<code type=plain">REM "%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" Set-ExecutionPolicy Remotesigned
<code type=plain">"%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe" Set-ExecutionPolicy Remotesigned

Bevor ich das Skript insgesamt habe laufen lassen, habe ich zuerst nur den SQL-Teil benutzt.
Das reduzierte die Datenbankgröße von ~10 GB auf etwas über 6 GB und zeigte keine Auffälligkeiten im Log.
Danach habe ich zwei oder dreimal, den "Server Cleanup Wizard" mit allen fünf Haken über die GUI gestartet.
Das brach jeweils nach einiger Zeit ab und die Serverknoten-zurücksetzen-Aufforderung erschien.
Ich hatte allerdings das Gefühl, dass ein Teil der Arbeit erledigt wurde und so war es wohl auch.
Das vollständige Skript gestartet, lief dann ohne Fehler durch und hat nur noch 11 ObsoleteUpdates und 6 ObsoleteComputers gelöscht.
Danach habe ich noch einmal den "Server Cleanup Wizard" manuell gestartet und der lief dann flott und ohne Fehler durch und hatte auch nichts mehr zu tun.

Das Skript ist jetzt wieder in der Aufgabenplanung für sonntags eingetragen und wie es scheint ist alles gut.

Gruß Frank