As instâncias do SQL Server hospedam bancos de dados que contêm os dados da pilha de back-end de um modelo de negócios ou dados de configuração para aplicativos específicos. Independentemente do caso de uso, uma instância possui um conjunto de valores/configurações que devem ser ajustados para seguir as práticas recomendadas.
O objetivo do Stored Procedure que estou apresentando neste artigo é apresentar ao DBA um conjunto de configurações/valores importantes que não devem ser negligenciados. Além disso, compartilharei um recurso interessante que ajuda os DBAs a manter o controle de qualquer configuração/valor específico que foi alterado/modificado recentemente.
Considerações iniciais
Certifique-se de que a conta que você usará para executar este procedimento armazenado tenha privilégios suficientes. Eu sei que pedir por um usuário com privilégio sysadmin parece demais, mas é a maneira mais fácil de fazer isso funcionar corretamente, já que o SP usa xp_cmdshell e outros procedimentos especiais armazenados no sistema para realizar o trabalho. Ou você pode ajustar os direitos do usuário para aderir ao princípio de privilégio mínimo.
Como usar o procedimento armazenado SQL?
- Copie e cole o código SP TSQL fornecido neste artigo.
- O SP espera apenas 1 parâmetro:@ storeValuesInTable
S é se o DBA deseja salvar a saída em uma tabela de destino e N é se o DBA quiser apenas ver a saída diretamente.
Campos Apresentados e seus Significados
- versão_sql – a versão atual do SQL Server da instância.
- sql_edition – a edição atual do SQL Server da instância.
- número_construção – o número de compilação atual da instância.
- min_server_memory – o valor atual (em MB) atribuído à memória mínima do servidor.
- max_server_memory – o valor atual (em MB) atribuído à memória máxima do servidor.
- server_memory – o valor atual (em MB) que o servidor que hospeda a instância do SQL Server tem disponível.
- server_cores – a quantidade de núcleos de vCPU que o servidor que hospeda a instância do SQL Server possui.
- sql_cores – a quantidade de núcleos de vCPU que a instância do SQL Server atribuiu para seu uso.
- cost_threshold_for_parallelism – o valor atual atribuído para a configuração Limite de custo para paralelismo.
- max_degree_of_parallelism – o valor atual atribuído para a configuração Grau máximo de paralelismo.
- lpim_enabled – 0 se Bloquear páginas na memória configuração está desativada e 1 se ativada.
- ifi_enabled – 0 se Inicialização de arquivo instantâneo está desativado e 1 se ativado.
- data_instalada – a data e hora em que a instância do SQL Server foi instalada.
- sql_service_account – a conta de serviço que executará o serviço DB Engine.
- sql_agent_service_account – a conta de serviço que executará o serviço do Agente.
- startup_time – valor de data e hora em que a instância do SQL Server foi iniciada recentemente.
- data_collection_timestamp – visível apenas se S é passado para o SP. Ele é usado para definir quando o SP foi executado e salvou com sucesso as informações no InstanceValues tabela.
Testes de execução do procedimento armazenado em SQL
Vou demonstrar algumas execuções do Stored Procedure para que você tenha uma ideia do que esperar dele.
EXEC DBA_InstanceValues @storeValuesInTable = 'N'
EXEC DBA_InstanceValues @storeValuesInTable = 'Y'
Para esta execução específica, a saída será salva em uma tabela chamada InstanceValues . Ele será criado no banco de dados de destino se não existir.
A tabela tem quase a mesma estrutura da captura de tela acima, com uma pequena diferença:ela inclui um campo chamado data_collection_timestamp bem na ponta da mesa.
O data_collection_timestamp campo é útil para vários propósitos:
- para dizer quando o SP foi executado para coletar os dados salvos (bastante óbvio).
- Para buscar quaisquer diferenças dentro de um determinado intervalo de tempo para qualquer campo de configuração específico.
Para provar que é útil, deixe-me apresentar um exemplo rápido.
Executei o SP uma vez, passando o Y parâmetro. O respectivo registro foi inserido em InstanceValues tabela. Então eu mudo cost_threshold_for_parallelism valor dentro da minha instância para 50 e execute o script novamente.
Como você pode ver, a modificação foi registrada com sucesso em InstanceValues tabela. Agora, como isso pode ser útil?
Se você criar um trabalho de agente que execute esse procedimento armazenado diariamente, poderá criar um mecanismo de auditoria interna para acompanhar quando um valor de configuração específico é modificado, assim como demonstrei. Assim, você pode manter o controle total sobre sua instância do SQL Server. Se você me perguntar, é algo extremamente útil.
O Código Completo do Procedimento Armazenado
No início do script, você verá o valor padrão. O Stored Procedure assume-o se nenhum valor for passado para o parâmetro.
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
Conclusão
O procedimento armazenado personalizado apresentado neste artigo permite que você crie um mecanismo de alerta para notificar sobre alterações de quaisquer valores de um determinado campo ao longo do tempo.
Você pode implantar esse SP em todas as instâncias do SQL Server sob seu suporte e implementar o mecanismo de auditoria em toda a pilha de instâncias com suporte.
A principal importância das informações apresentadas seria verificar se a instância do SQL Server possui valores que seguem as melhores práticas recomendadas. Também ajuda a verificar se alguma atividade/alteração recente ocorreu para atualizar alguma das configurações (intencionalmente ou por engano).