Instancje SQL Server zawierają bazy danych zawierające dane dla stosu zaplecza modelu biznesowego lub dane konfiguracyjne dla poszczególnych aplikacji. Niezależnie od przypadku użycia, instancja ma zestaw wartości/ustawień, które należy dostosować zgodnie z najlepszymi praktykami.
Celem procedury składowanej, którą prezentuję w tym artykule, jest przedstawienie DBA zestawu ważnych ustawień/wartości, których nie należy przeoczyć. Co więcej, podzielę się fajną funkcją, która pomoże administratorom baz danych zachować kontrolę nad każdym konkretnym ustawieniem/wartością, które zostały ostatnio zmienione/zmodyfikowane.
Wstępne rozważania
Upewnij się, że konto, którego będziesz używać do wykonania tej procedury przechowywanej, ma wystarczające uprawnienia. Wiem, że prośba o użytkownika z uprawnieniami sysadmin brzmi zbyt dużo, ale jest to najłatwiejszy sposób na prawidłowe działanie, ponieważ SP używa xp_cmdshell i inne specjalne procedury przechowywane w systemie, aby wykonać zadanie. Możesz też dostroić prawa użytkownika, aby przestrzegać zasady najmniejszych uprawnień.
Jak korzystać z procedury składowanej SQL?
- Skopiuj i wklej kod SP TSQL podany w tym artykule.
- SP oczekuje tylko 1 parametru:@ storeValuesInTable
T oznacza, że administrator danych chce zapisać dane wyjściowe w tabeli docelowej, a N jest, jeśli administrator chce tylko bezpośrednio widzieć dane wyjściowe.
Przedstawione pola i ich znaczenie
- wersja_sql – aktualna wersja instancji SQL Server.
- sql_edition – bieżąca edycja instancji SQL Server.
- numer_kompilacji – aktualny numer kompilacji instancji.
- min_server_memory – aktualna wartość (w MB) przypisana do minimalnej pamięci serwera.
- max_server_memory – aktualna wartość (w MB) przypisana do maksymalnej pamięci serwera.
- pamięć_serwera – aktualna wartość (w MB), którą ma dostępny serwer hostujący instancję SQL Server.
- rdzenie_serwera – liczba rdzeni vCPU, które ma serwer hostujący instancję SQL Server.
- sql_cores – liczba rdzeni vCPU, które instancja SQL Server przypisała do swojego użycia.
- cost_threshold_for_parallelism – bieżąca wartość przypisana do ustawienia progu kosztu dla równoległości.
- max_degree_of_parallelism – aktualna wartość przypisana do ustawienia maksymalnego stopnia równoległości.
- lpim_enabled – 0, jeśli Zablokuj strony w pamięci ustawienie jest wyłączone i 1, jeśli jest włączone.
- ifi_enabled – 0, jeśli Inicjalizacja pliku błyskawicznego jest wyłączony i 1, jeśli jest włączony.
- data_zainstalowania – data i godzina zainstalowania instancji SQL Server.
- sql_service_account – konto usługi, które będzie uruchamiać usługę DB Engine.
- sql_agent_service_account – konto usługi, które będzie uruchamiać usługę agenta.
- czas_uruchomienia – wartość daty i godziny, kiedy instancja SQL Server została niedawno uruchomiona.
- data_collection_timestamp – widoczne tylko wtedy, gdy T jest przekazywana do SP. Służy do określenia, kiedy SP został wykonany i pomyślnie zapisał informacje w InstanceValues stół.
Testy wykonania procedury składowanej w SQL
Zademonstruję kilka wykonań procedury składowanej, aby zorientować się, czego się po niej spodziewać.
EXEC DBA_InstanceValues @storeValuesInTable = 'N'
EXEC DBA_InstanceValues @storeValuesInTable = 'Y'
W przypadku tego konkretnego wykonania dane wyjściowe zostaną zapisane w tabeli o nazwie InstanceValues . Zostanie utworzony w docelowej bazie danych, jeśli nie istnieje.
Tabela ma prawie taką samą strukturę jak na powyższym zrzucie ekranu, z niewielką różnicą:zawiera pole o nazwie data_collection_timestamp na samym końcu stołu.
data_collection_timestamp pole jest przydatne z kilku powodów:
- aby powiedzieć, kiedy SP został wykonany w celu zebrania zapisanych danych (dość oczywiste).
- Poszukiwanie jakichkolwiek różnic w określonym zakresie czasu dla dowolnego konkretnego pola ustawień.
Aby udowodnić, że jest to przydatne, przedstawię krótki przykład.
Wykonałem SP raz, przekazując Y parametr. Odpowiedni rekord został wstawiony do InstanceValues stół. Następnie zmieniam cost_threshold_for_parallelism wartość w mojej instancji do 50 , a następnie ponownie uruchom skrypt.
Jak widać, modyfikacja została pomyślnie zarejestrowana w InstanceValues stół. Jak to może być przydatne?
Jeśli utworzysz zadanie agenta, które codziennie wykonuje tę procedurę składowaną, możesz utworzyć mechanizm audytu wewnętrznego aby śledzić, kiedy określona wartość ustawienia jest modyfikowana, tak jak zademonstrowałem. Dzięki temu możesz zachować pełną kontrolę nad instancją SQL Server. Jeśli mnie pytasz, jest to coś niezwykle przydatnego.
Kompletny kod procedury przechowywanej
Na samym początku skryptu zobaczysz wartość domyślną. Procedura składowana zakłada to, jeśli do parametru nie zostanie przekazana żadna wartość.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author : Alejandro Cobar
-- Create date: 2021-05-15
-- Description: SP to retrieve important instance settings/values
-- =============================================
CREATE PROCEDURE [dbo].[DBA_InstanceValues]
@storeValuesInTable CHAR(1) = 'N'
AS
BEGIN
SET NOCOUNT ON;
DECLARE @sqlCommand VARCHAR(4096)
SET @sqlCommand = ''
IF(@storeValuesInTable = 'Y')
BEGIN
IF NOT EXISTS (SELECT * FROM dbo.sysobjects where id = object_id(N'[InstanceValues]') and OBJECTPROPERTY(id, N'IsTable') = 1)
BEGIN
CREATE TABLE InstanceValues(
[sql_version] [VARCHAR](32) NOT NULL,
[sql_edition] [VARCHAR](64) NOT NULL,
[build_number] [VARCHAR](32) NOT NULL,
[min_server_memory] [DECIMAL](15,2) NOT NULL,
[max_server_memory] [DECIMAL](15,2) NOT NULL,
[server_memory] [DECIMAL](15,2) NOT NULL,
[server_cores] [SMALLINT] NOT NULL,
[sql_cores] [SMALLINT] NOT NULL,
[cost_threshold_for_parallelism][SMALLINT] NOT NULL,
[max_degree_of_parallelism] [SMALLINT] NOT NULL,
[lpim_enabled] [TINYINT] NOT NULL,
[ifi_enabled] [TINYINT] NOT NULL,
[installed_date] [DATETIME] NOT NULL,
[sql_service_account] [VARCHAR](64) NOT NULL,
[sql_agent_service_account] [VARCHAR](64) NOT NULL,
[startup_time] [DATETIME] NOT NULL,
[data_collection_timestamp] [DATETIME] NOT NULL
) ON [PRIMARY]
END
END
CREATE TABLE #CPUValues(
[index] SMALLINT,
[description] VARCHAR(128),
[server_cores] SMALLINT,
[value] VARCHAR(5)
)
CREATE TABLE #MemoryValues(
[index] SMALLINT,
[description] VARCHAR(128),
[server_memory] DECIMAL(10,2),
[value] VARCHAR(64)
)
INSERT INTO #CPUValues
EXEC xp_msver 'ProcessorCount'
INSERT INTO #MemoryValues
EXEC xp_msver 'PhysicalMemory'
CREATE TABLE #IFI_Value(DataOut VarChar(2000))
DECLARE @show_advanced_options INT
DECLARE @xp_cmdshell_enabled INT
DECLARE @xp_regread_enabled INT
SELECT @show_advanced_options = CONVERT(INT, ISNULL(value, value_in_use))
FROM master.sys.configurations
WHERE name = 'show advanced options'
IF @show_advanced_options = 0
BEGIN
EXEC sp_configure 'show advanced options', 1
RECONFIGURE WITH OVERRIDE
END
SELECT @xp_cmdshell_enabled = CONVERT(INT, ISNULL(value, value_in_use))
FROM master.sys.configurations
WHERE name = 'xp_cmdshell'
IF @xp_cmdshell_enabled = 0
BEGIN
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE WITH OVERRIDE
END
INSERT INTO #IFI_Value
EXEC xp_cmdshell 'whoami /priv | findstr `"SeManageVolumePrivilege`"'
IF @xp_cmdshell_enabled = 0
BEGIN
EXEC sp_configure 'xp_cmdshell', 0
RECONFIGURE WITH OVERRIDE
END
IF @show_advanced_options = 0
BEGIN
EXEC sp_configure 'show advanced options', 0
RECONFIGURE WITH OVERRIDE
END
IF (SELECT CONVERT(INT, (REPLACE(SUBSTRING(CONVERT(NVARCHAR, SERVERPROPERTY('ProductVersion')), 1, 2), '.', '')))) > 10
BEGIN
IF(@storeValuesInTable = 'Y')
BEGIN
SET @sqlCommand = '
INSERT INTO InstanceValues
'
END
SET @sqlCommand += '
SELECT
v.sql_version,
(SELECT SUBSTRING(CONVERT(VARCHAR(255),SERVERPROPERTY(''EDITION'')),0,CHARINDEX(''Edition'',CONVERT(VARCHAR(255),SERVERPROPERTY(''EDITION'')))) + ''Edition'') AS sql_edition,
CONVERT(VARCHAR,SERVERPROPERTY(''ProductVersion'')) AS build_number,
(SELECT CONVERT(DECIMAL(10,2),[value]) FROM sys.configurations WHERE name LIKE ''%min server memory%'') min_server_memory,
(SELECT CONVERT(DECIMAL(10,2),[value]) FROM sys.configurations WHERE name LIKE ''%max server memory%'') max_server_memory,
(SELECT ROUND(CONVERT(DECIMAL(10,2),server_memory/1024.0),1) FROM #MemoryValues) AS server_memory,
server_cores,
(SELECT COUNT(*) AS ''sql_cores'' FROM sys.dm_os_schedulers WHERE status = ''VISIBLE ONLINE'') AS sql_cores,
(SELECT CONVERT(SMALLINT,[value]) FROM sys.configurations WHERE name LIKE ''%cost threshold for parallelism%'') AS cost_threshold_for_parallelism,
(SELECT CONVERT(SMALLINT,[value]) FROM sys.configurations WHERE name LIKE ''%max degree of parallelism%'') AS max_degree_of_parallelism,
(SELECT CASE locked_page_allocations_kb WHEN 0 THEN 0 ELSE 1 END FROM sys.dm_os_process_memory) AS lpim_enabled,
(SELECT COUNT(1) FROM #IFI_Value WHERE DataOut LIKE ''%SeManageVolumePrivilege%Enabled%'') AS ifi_enabled,
(SELECT create_date FROM sys.server_principals WHERE sid = 0x010100000000000512000000) AS installed_date,
(SELECT service_account FROM sys.dm_server_services WHERE servicename = {fn CONCAT({fn CONCAT(''SQL Server ('',CONVERT(VARCHAR(32),ISNULL(SERVERPROPERTY(''INSTANCENAME''),''MSSQLSERVER'')))},'')'')}) AS sql_service_account,
(SELECT service_account FROM sys.dm_server_services WHERE servicename = {fn CONCAT({fn CONCAT(''SQL Server Agent ('',CONVERT(VARCHAR(32),ISNULL(SERVERPROPERTY(''INSTANCENAME''),''MSSQLSERVER'')))},'')'')}) AS sql_agent_service_account,
(SELECT login_time FROM sys.sysprocesses WHERE spid = 1) AS startup_time'
IF(@storeValuesInTable = 'Y')
BEGIN
SET @sqlCommand += '
,GETDATE() AS data_collection_timestamp
'
END
SET @sqlCommand += '
FROM #CPUValues
LEFT JOIN (
SELECT
CASE
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''8%'' THEN ''SQL Server 2000''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''9%'' THEN ''SQL Server 2005''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''10.0%'' THEN ''SQL Server 2008''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''10.5%'' THEN ''SQL Server 2008 R2''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''11%'' THEN ''SQL Server 2012''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''12%'' THEN ''SQL Server 2014''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''13%'' THEN ''SQL Server 2016''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''14%'' THEN ''SQL Server 2017''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''15%'' THEN ''SQL Server 2019''
ELSE ''UNKNOWN''
END AS sql_version
) AS v ON 1 = 1
'
EXECUTE(@sqlCommand)
END
ELSE
BEGIN
DECLARE @instanceName VARCHAR(100)
SET @instanceName = CONVERT(VARCHAR,SERVERPROPERTY ('InstanceName'))
IF (@instanceName) IS NULL
BEGIN
DECLARE @agentAccount NVARCHAR(128);
EXEC master.dbo.xp_regread
'HKEY_LOCAL_MACHINE',
'SYSTEM\CurrentControlSet\services\SQLSERVERAGENT',
'ObjectName',
@agentAccount OUTPUT;
DECLARE @engineAccount NVARCHAR(128);
EXEC master.dbo.xp_regread
'HKEY_LOCAL_MACHINE',
'SYSTEM\CurrentControlSet\services\MSSQLSERVER',
'ObjectName',
@engineAccount OUTPUT;
END
ELSE
BEGIN
DECLARE @SQL NVARCHAR (500)
SET @SQL = 'EXEC master.dbo.xp_regread ''HKEY_LOCAL_MACHINE'', ''SYSTEM\CurrentControlSet\services\SQLAgent$'[email protected]+''',''ObjectName'', @serviceAccount OUTPUT;'
EXECUTE sp_executesql @SQL,N'@serviceAccount NVARCHAR(128) OUTPUT',@[email protected] OUTPUT
SET @SQL = 'EXEC master.dbo.xp_regread ''HKEY_LOCAL_MACHINE'', ''SYSTEM\CurrentControlSet\services\MSSQL$'[email protected]+''',''ObjectName'', @serviceAccount OUTPUT;'
EXECUTE sp_executesql @SQL,N'@serviceAccount NVARCHAR(128) OUTPUT',@[email protected] OUTPUT
END
IF(@storeValuesInTable = 'Y')
BEGIN
SET @sqlCommand = '
INSERT INTO InstanceValues
'
END
SET @sqlCommand += '
SELECT
v.sql_version,
(SELECT SUBSTRING(CONVERT(VARCHAR(255),SERVERPROPERTY(''EDITION'')),0,CHARINDEX(''Edition'',CONVERT(VARCHAR(255),SERVERPROPERTY(''EDITION'')))) + ''Edition'') AS sql_edition,
CONVERT(VARCHAR,SERVERPROPERTY(''ProductVersion'')) AS build_number,
(SELECT CONVERT(DECIMAL(10,2),[value]) FROM sys.configurations WHERE name LIKE ''%min server memory%'') min_server_memory,
(SELECT CONVERT(DECIMAL(10,2),[value]) FROM sys.configurations WHERE name LIKE ''%max server memory%'') max_server_memory,
(SELECT ROUND(CONVERT(DECIMAL(10,2),server_memory/1024.0),1) FROM #MemoryValues) AS server_memory,
server_cores,
(SELECT COUNT(*) AS sql_cores FROM sys.dm_os_schedulers WHERE status = ''VISIBLE ONLINE'') AS sql_cores,
(SELECT CONVERT(SMALLINT,[value]) FROM sys.configurations WHERE name LIKE ''%cost threshold for parallelism%'') AS cost_threshold_for_parallelism,
(SELECT CONVERT(SMALLINT,[value]) FROM sys.configurations WHERE name LIKE ''%max degree of parallelism%'') AS max_degree_of_parallelism,
(SELECT CASE locked_page_allocations_kb WHEN 0 THEN 0 ELSE 1 END FROM sys.dm_os_process_memory) AS lpim_enabled,
(SELECT COUNT(1) FROM #IFI_Value WHERE DataOut LIKE ''%SeManageVolumePrivilege%Enabled%'') AS ifi_enabled,
(SELECT create_date FROM sys.server_principals WHERE sid = 0x010100000000000512000000) AS installed_date,
(SELECT '+CHAR(39)[email protected]+CHAR(39)+' AS sql_service_account) AS sql_service_account,
(SELECT '+CHAR(39)[email protected]+CHAR(39)+' AS sql_agent_service_account) AS sql_agent_service_account,
(SELECT login_time FROM sys.sysprocesses WHERE spid = 1) AS startup_time'
IF(@storeValuesInTable = 'Y')
BEGIN
SET @sqlCommand += '
,GETDATE() AS data_collection_timestamp
'
END
SET @sqlCommand += '
FROM #CPUValues
LEFT JOIN (
SELECT
CASE
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''8%'' THEN ''SQL Server 2000''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''9%'' THEN ''SQL Server 2005''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''10.0%'' THEN ''SQL Server 2008''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''10.5%'' THEN ''SQL Server 2008 R2''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''11%'' THEN ''SQL Server 2012''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''12%'' THEN ''SQL Server 2014''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''13%'' THEN ''SQL Server 2016''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''14%'' THEN ''SQL Server 2017''
WHEN CONVERT(VARCHAR(128), SERVERPROPERTY (''PRODUCTVERSION'')) LIKE ''15%'' THEN ''SQL Server 2019''
ELSE ''UNKNOWN''
END AS sql_version
) AS v ON 1 = 1
'
EXECUTE(@sqlCommand)
--SELECT @sqlCommand
END
DROP TABLE #CPUValues
DROP TABLE #MemoryValues
DROP TABLE #IFI_Value
END
Wniosek
Niestandardowa procedura składowana przedstawiona w tym artykule umożliwia zbudowanie mechanizmu ostrzegania w celu powiadamiania o zmianach dowolnych wartości określonego pola w czasie.
Możesz wdrożyć ten SP w każdej obsługiwanej przez Ciebie instancji SQL Server i wdrożyć mechanizm audytu w całym stosie obsługiwanych instancji.
Podstawowym znaczeniem prezentowanych informacji byłoby sprawdzenie, czy instancja SQL Server posiada wartości zgodne z zalecanymi najlepszymi praktykami. Pomaga również sprawdzić, czy miała miejsce jakakolwiek ostatnia aktywność/zmiana, aby zaktualizować którekolwiek z ustawień (celowo lub przez pomyłkę).