Você usa o Redshift como DW ou DL para seus relatório e cargas de dados;
Vê uma possibilidade de facilitar sua vida e dar liberdade para o próprio cliente acessar esses dados e gerar relatórios da forma que ele achar mais legal com a ferramenta que ele quiser, etc.;
Mas lembra que a AWS cobra pela saída de dados;
Procura no portal da AWS e descobre que eles não tem uma monitoração específica de quem está saindo com dados, mas eles acertam a cobrança… incrível…
Mas você não quer abandonar a ideia e quer ganhar alguma grana com isso..
O que vou mostrar não é a solução perfeita, ela carece de algumas melhorias mas já é um norte para ajudar nessa ideia…
O Redshift é um PostgreSQL modificado, então muita query em tabelas de sistema do PG funciona direitinho no Redshift…
Para esse cenário, você pode criar um pacote de integration services e rodar a query abaixo contra o Redshift:
select
TRIM(q.DATABASE) AS DB,
TRIM(u.usename) as usename,
sum(bytes)/1024 as kbytes,
sum(packets) as packets,
date(b.starttime) as data
from
stl_bcast b
join stl_query q ON
b.query = q.query
AND b.userid = q.userid
join pg_user u
on u.usesysid = q.userid
where
packets>0
and b.starttime >= dateadd(day, -7, current_Date)
and datediff(seconds, b.starttime, b.endtime)>0
--and u.usename like 'usr%'
--and querytxt not like 'fetch%'
group by TRIM(q.DATABASE)
,u.usename
,date(b.starttime)
Essa query vai trazer a informação do volume em kb trafegado pela query executada.
Com isso, você consegue montar um report incremental e ratear o custo da saída de dados da AWS.
É 100%?, não,,, mas pelo menos já é alguma coisa já que a AWS não provê dados granularizados de quem consome a saída de dados.
novos códigos serão criados também em outro repositório:
O Redshift tem umas vantagens bem interessantes, baixo custo, RDS, baixa necessidade de manutenção.
No fundo ele é um PostgreSQL modificado para prover volume de dados e não ficar trabalhando como OLTP, ele é ótimo como estrutura para DW.
Imagine o seguinte cenário, você vende uma solução mas precisa prover um acesso do seu cliente para que ele consiga acessar uma parte dos dados diretamente na sua estrutura de banco, para ele “ter a liberdade” de cruzar esses dados, montar estruturas de relatórios, etc. da forma que ele achar mais interessante, ou até mesmo exportar esses dados para uma estrutura dele e usar da forma que achar melhor.
Certo, temos várias formas de fazer isso, todas tem seus prós e contras, mas nesse caso vou usar como exemplo justamente o título do post, vamos colocar os dados no Redshift.
Então, você tem alguma forma de extração de dados incrementais (SSIS, Pentaho, Informática, estagiário, etc.) que leva os dados do seu OLTP para o Redshift e isso funciona bem.
Agora você precisa criar a estrutura de permissões para liberar o acesso do seu cliente para essa estrutura de dados.
Uma coisa muito importante : Todos os usuários do Redshift são exclusivos do banco de dados e não da instância, Então caso o cliente tenha mais de um banco ou você queira dar permissão para mais de um banco, siga o processo quantas vezes for necessário.
— Normalmente quando os objetos são criados no Redshift ele ficam armazenados no schema public. — Isso não é um problema, o problema começa quando é criado um schema para armazenar um outro conjunto de objetos — para um setor da empresa, ou um outro departamento… — Quando isso acontece, o usuário owner da carga dos objetos tem acesso a essa estrutura de dados sem problema, mas novos usuários, — ou usuários permissonalizados não tem a permissão para os objetos ou para novos objetos nesse schema. — O script abaixo tente a sanar um cenário em que você quer liberar o acesso de select para os objetos e novo objetos em um schema pulic — ou personalizado sem ter que ficar dando grant toda a vez que novos objetos são criados. — Outra opção de uso é caso você tenha um Redshift na sua empresa e venda como serviço ele como datalake para algum cliente. — dessa forma você consegue liberar um usuário para que o cliente acesse a estrutura de dados e consiga baixar os dados.
-- Normalmente quando os objetos são criados no Redshift ele ficam armazenados no schema public.
-- Isso não é um problema, o problema começa quando é criado um schema para armazenar um outro conjunto de objetos
-- para um setor da empresa, ou um outro departamento...
-- Quando isso acontece, o usuário owner da carga dos objetos tem acesso a essa estrutura de dados sem problema, mas novos usuários,
-- ou usuários permissonalizados não tem a permissão para os objetos ou para novos objetos nesse schema.
-- O script abaixo tente a sanar um cenário em que você quer liberar o acesso de select para os objetos e novo objetos em um schema pulic
-- ou personalizado sem ter que ficar dando grant toda a vez que novos objetos são criados.
-- Outra opção de uso é caso você tenha um Redshift na sua empresa e venda como serviço ele como datalake para algum cliente.
-- dessa forma você consegue liberar um usuário para que o cliente acesse a estrutura de dados e consiga baixar os dados.
-- criar um usuário
create user <username> with password ‘<password>’;
-- cria um grupo para receber as permissões
create group data_viewers;
-- adiciona o usuário ao grupo
alter group data_viewers add user <username>;
-- nesse caso remove a opção de criar objetos para os usuários do grupo
revoke create on schema public from group data_viewers;
-- atribui acesso no schema public ao grupo
grant usage on schema public to group data_viewers;
-- atribui select em todas as tabelas do schema public para o grupo
grant select on all tables in schema public to group data_viewers;
-- atribui acesso a futuros objetos do schema public para o grupo
alter default privileges in schema public grant select on tables to group data_viewers
novos códigos serão criados também em outro repositório:
Como DBA´s estamos sujeitos a jobs, alertas, sistemas, coisas do além, gerentes, desenvolvedores, intervenções místicas, etc. executando ações no banco de dados.
Não da pra ficar olhando de perto o ambiente a todo o momento analisando cada ação, monitorando tudo o que ocorre, se preparando para o pior.
Sendo um bom DBA, você deve ter um monte de Jobs, Alertas, Operadores, Sistemas de Monitoração, aquele estagiário sendo escalpelado, entre outras formas de monitorar seus bancos, a ideia aqui é trazer mais uma opção para atazanar sua vida monótona.
Vamos a receita do desastre,,, Para isso vamos precisar:
Que você tenha uma conta no Telegram
Instale o aplicativo do Telegram no seu Windows
Crie um Bot
Configure o Bot
Crie um canal e adicione esse Bot como administrador a um canal
Mande uma mensagem para esse canal para criar um ID
Acesse uma URL do Telegram com a chave do BOT para pegar o ID do canal
Crie uma função no Powershell
Declare a variável com o nome do Bot e a chave
Mande uma mensagem
Vamos começar,,,
Conta do Telegram
PQ está lendo essa parte? vai para a próxima,,, se não sabe fazer isso nem adianta continuar…
Instale o aplicativo do Telegram no seu Windows
Esse ponto é interessante, acesse o cliente web do Telegram e baixe a versão para seu SO, as configurações do Bot serão feitas através do cliente e não pelo celular ou Web
Crie um Bot
Com sua contra criada e app instalado, acesse essa URL https://t.me/BotFather . Ele é o Bot que cria os Bot´s
Para criar digite /start
Depois digite /newbot
De um nome para seu Bot
Agora crie um username para seu bot, ele tem que terminar com bot
Você vai receber uma mensagem de resposta com um textão e a parte que interessa que é o TOKEN. ANOTA ISSO!
Aqui vale uma dica, o Bot vai aparecer em pesquisa para qualquer pessoa que use o Telegram, mas só quem tem o token vai poder realmente usar ele.
Configure o Bot
Adicione uma descrição para ele, uma hora você ou alguém vai fazer manutenção e vai ter que lembrar para que ele serve
Usando /setdescription adicione uma descrição
Crie um canal e adicione esse Bot como administrador a um canal
Agora usando o cliente do SO, Web ou o app do celular, crie um canal no Telegram e deixe como privado.
Adicione um novo membro para esse canal, nesse caso o seu Bot, vai aparecer um alerta de que o Bot precisa ser administrador do canal e bla bla bla
Mande uma mensagem para esse canal para criar um ID
Só o fato de criar um canal não efetiva cria o canal, até esse momento ele não tem um ID
Mande uma mensagem qualquer para o canal para ele criar a estrutura com o ID
Acesse uma URL do Telegram com a chave do BOT para pegar o ID do canal
Após mandar a mensagem acesse a URL com o token do seu Bot
a URL é alguma coisa assim: https://api.telegram.org/bot<aquele token do seu Bot>/getUpdates
deixa escrito o nome bot, remove o < e o > e cola o token
deve abrir uma página com alguma coisa parecida com:
O que interessa para nós é um desses últimos “id” que estão com um símbolo de menos na frente “id”:-XXXX
Crie uma função no Powershell
Agora a parte mágica, vamos usar um powershell para criar a função que vai conectar na API do Telegram e mandar a mensagem…
<#
.Synopsis
Sends Telegram text message via Bot API
.DESCRIPTION
Uses Telegram Bot API to send text message to specified Telegram chat. Several options can be specified to adjust message parameters.
.EXAMPLE
$bot = "#########:xxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx"
$chat = "-#########"
Send-TelegramTextMessage -BotToken $bot -ChatID $chat -Message "Hello"
.EXAMPLE
$bot = "#########:xxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx"
$chat = "-#########"
Send-TelegramTextMessage `
-BotToken $bot `
-ChatID $chat `
-Message "Hello *chat* _channel_, check out this link: [TechThoughts](http://techthoughts.info/)" `
-ParseMode Markdown `
-Preview $false `
-Notification $false `
-Verbose
.PARAMETER BotToken
Use this token to access the HTTP API
.PARAMETER ChatID
Unique identifier for the target chat
.PARAMETER Message
Text of the message to be sent
.PARAMETER ParseMode
Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. Default is Markdown.
.PARAMETER Preview
Disables link previews for links in this message. Default is $false
.PARAMETER Notification
Sends the message silently. Users will receive a notification with no sound. Default is $false
.OUTPUTS
System.Boolean
.NOTES
Author: Jake Morrison - @jakemorrison - http://techthoughts.info/
This works with PowerShell Versions 5.1, 6.0, 6.1
For a description of the Bot API, see this page: https://core.telegram.org/bots/api
How do I get my channel ID? Use the getidsbot https://telegram.me/getidsbot
How do I set up a bot and get a token? Use the BotFather https://t.me/BotFather
.COMPONENT
PoshGram - https://github.com/techthoughts2/PoshGram
.FUNCTIONALITY
https://core.telegram.org/bots/api#sendmessage
Parameters Type Required Description
chat_id Integer or String Yes Unique identifier for the target chat or username of the target channel (in the format @channelusername)
text String Yes Text of the message to be sent
parse_mode String Optional Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message.
disable_web_page_preview Boolean Optional Disables link previews for links in this message
disable_notification Boolean Optional Sends the message silently. Users will receive a notification with no sound.
reply_to_message_id Integer Optional If the message is a reply, ID of the original message
#>
function Send-TelegramTextMessage {
[CmdletBinding()]
Param
(
[Parameter(Mandatory = $true,
HelpMessage = '#########:xxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx')]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[string]$BotToken, #you could set a token right here if you wanted
[Parameter(Mandatory = $true,
HelpMessage = '-#########')]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[string]$ChatID, #you could set a Chat ID right here if you wanted
[Parameter(Mandatory = $true,
HelpMessage = 'Text of the message to be sent')]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[string]$Message,
[Parameter(Mandatory = $false,
HelpMessage = 'HTML vs Markdown for message formatting')]
[ValidateSet("Markdown", "HTML")]
[string]$ParseMode = "Markdown", #set to Markdown by default
[Parameter(Mandatory = $false,
HelpMessage = 'Disables link previews')]
[bool]$Preview = $false, #set to false by default
[Parameter(Mandatory = $false,
HelpMessage = 'Sends the message silently')]
[bool]$Notification = $false #set to false by default
)
#------------------------------------------------------------------------
$results = $true #assume the best
#------------------------------------------------------------------------
$payload = @{
"chat_id" = $ChatID;
"text" = $Message
"parse_mode" = $ParseMode;
"disable_web_page_preview" = $Preview;
"disable_notification" = $Notification
}#payload
#------------------------------------------------------------------------
try {
Write-Verbose -Message "Sending message..."
$eval = Invoke-RestMethod `
-Uri ("https://api.telegram.org/bot{0}/sendMessage" -f $BotToken) `
-Method Post `
-ContentType "application/json" `
-Body (ConvertTo-Json -Compress -InputObject $payload) `
-ErrorAction Stop
if (!($eval.ok -eq "True")) {
Write-Warning -Message "Message did not send successfully"
$results = $false
}#if_StatusDescription
}#try_messageSend
catch {
Write-Warning "An error was encountered sending the Telegram message:"
Write-Error $_
$results = $false
}#catch_messageSend
return $results
#------------------------------------------------------------------------
}#function_Send-TelegramTextMessage
Declare a variável com o nome do Bot e a chave
Sendo simplista você vai precisar agora criar duas variáveis a com o nome do Token e o ID do chat
$bot = "token"
$chat = "-ID_do_chat"
Mande uma mensagem
Agora é só chamar a função e mandar a mensagem…
AAHH a máquina precisa ter acesso a internet.
Qualquer mensagem de erro a função vai tentar trazer a mensagem para te ajudar no troubleshooting.
Com isso agora temos um powershell que acessa uma API do Telegram e manda mensagem, junta isso em um JOB que é acionado por um alerta e vc tem o SQL ou outro sistema de monitoração mandando mensagens para seu celular.
Todos sabemos que o SQL é um consumidor de memória frenético, quanto mais memória disponível mais memória ele vai reservar para ele.
O que é um desenho “by default”, ele sempre fará isso afinal de contas ele precisa alocar as páginas de dados do seu banco em algum lugar.
Para resolver todos os seus problemas, existe uma forma de liberar toda a memória disponível de uma só vez do seu servidor e não é parando o serviço do SQL.
Para isso, você vai precisar o Visual Studio instalado, vamos criar um novo projeto dele…
Importante! Abra o Visual Studio como administrador !
Novo projeto de linha de comando
Escreva o nome que quiser para o app
Copie e cole o código abaixo no projeto:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
public class CriticalProcess
{
[DllImport("ntdll.dll", SetLastError = true)]
private static extern int NtSetInformationProcess(IntPtr hProcess, int processInformationClass, ref int processInformation, int processInformationLength);
static void Main(string[] args)
{
int isCritical = 1; // queremos que ele seja um processo crítico
int BreakOnTermination = 0x1D; // valor para BreakOnTermination (flag)
Process.EnterDebugMode(); //acquire Debug Privileges
// configurando o BreakOnTermination = 1 para o processo ativo
NtSetInformationProcess(Process.GetCurrentProcess().Handle, BreakOnTermination, ref isCritical, sizeof(int));
}
}
Se tudo ocorrer como esperado, dependendo da quantidade de memória do seu servidor isso pode demorar de alguns segundos a algumas horas.
Por mais que tenhamos criado uma aplicação de linha de comando a primeira parte do processo é bem gráfica e todos já tiveram o grande prazer de conhecer:
Ele vai gerar um DUMP de toda a memória para o arquivo de paginação e depois que a maquina reiniciar ele vai copiar esse arquivo de paginação para um arquivo chamado memory.dump
É só isso,,, execução e queda,,,
Agora falando sério: NUNCA !!!! JAMAIS !!!!! Simplesmente pegue o código de qualquer coisa que você encontra na internet e saia executando sem antes entender o que ele faz.
Esse exemplo é bem ridículo, mas imagina um script que você leu o por alto achando que vai resolver todos os seus problemas de backup, ou de fragmentação de índice e descobre que no meio tem um sp_msforeach_table com um sp_msforeach_db que trunca as tabelas, ou pior, alguém cria uma chave de criptografia e habilita TDE nas suas bases e depois força a remoção da chave,,,, a culpa é tão e somente sua! Você é o DBA é sua responsabilidade preservar os dados.
Tenha discernimento com o que você copia da internet e de onde copia essas informações.
SEMPRE LEIA e NUNCA EXECUTE DIRETAMENTE EM PRODUÇÃO !!!
As vezes temos operações de disco que chegam a gerar mensagens como a seguinte:
SQL Server has encountered 1 occurrence(s) of I/O requests taking longer than 15 seconds to complete on file [X:\Banco\Disk99\DataFiles\Arquivo_do_banco_X_FG_Y_Arquivo_75.ndf] in database [Banco_X] (254). The OS file handle is 0x0000000000009A18. The offset of the latest long I/O is: 0x00000030038000
Muitas vezes isso indica que o sistema de discos não está operando de forma satisfatória e está impactando alguma operação.
A query abaixo tenta ajudar a identificar qual operação DDL ou DML que estava acessando o arquivo naquele momento:
SELECT DB_NAME(mf.database_id) AS [Database]
,mf.physical_name
,r.io_pending
,r.io_pending_ms_ticks
,r.io_type
,fs.num_of_reads
,fs.num_of_writes
,ER.session_id
,ST.TEXT
FROM sys.dm_io_pending_io_requests AS r
INNER JOIN sys.dm_io_virtual_file_stats(NULL, NULL) AS fs ON r.io_handle = fs.file_handle
INNER JOIN sys.master_files AS mf ON fs.database_id = mf.database_id
AND fs.file_id = mf.file_id
INNER JOIN sys.dm_os_schedulers os ON r.scheduler_address = os.scheduler_address
INNER JOIN sys.dm_exec_requests AS ER ON os.scheduler_id = ER.Scheduler_id
CROSS APPLY sys.dm_exec_sql_text(ER.sql_handle) AS ST
ORDER BY r.io_pending_ms_ticks DESC;
go
Mais um para a minha lista de programas inúteis,,,
Esse programa é um command line que vai converter o arquivo de saída do SSMS quando você escolhe ter o resultado em arquivo (.RPT) para arquivo separado por vírgula (.CSV)
Ele tem uma limitação que é pegar apenas uma saída do arquivo de resultado. O que isso significa? Se você executar mais de uma query no SSMS e isso gerar mais de um resultado dentro do arquivo RPT ele só vai converter o primeiro resultado.
Essa limitação não é do programa em si, mas da limitação do CSV, já que ele vai usar a primeira linha do arquivo como coluna e remover os traços da segunda linha você acaba tendo apenas um resultado dentro de um arquivo CSV pois não dá para ter mais de uma linha com nome de coluna.
Não entendeu nada? imaginei…
A execução dele é bem simples:
Para um arquivo: rpt2csv nome_do_arquivo.RPT
Para mais arquivos: rpt2csv nome_do_arquivo.RPT nome_do_arquivo2.RPT nome_do_arquivo3.RPT …
Percebeu que NÃO tem o nome_do_arquivo.CSV ? Ele vai gerar no mesmo local onde está o RPT o arquivo CSV com o mesmo nome.
Se você digitar apenas rpt2csv ele vai mostrar um pequeno help, já se você adicionar o /? ele vai mostrar um exemplo…
O download do projeto está aqui, caso queira apenas o executável baixe o projeto, descompacte e vá até o diretório “rpt2csv\rpt2csv\bin\Debug” lá você vai encontrar o rpt2csv.
Se isso for útil comente e responta a enquete,,, se não foi útil responda a enquete,,,
[polldaddy poll=8777809]
Se você fizer uma análise mais a fundo, vai perceber que ele converte qualquer coisa que se pareça com o RPT e não apenas RPT para o CSV.
Com um pouco de modificação o mesmo código pode ser utilizado para muitas outras coisas,,, por exemplo: limitar acesso a uma instância bloqueando a conexão do usuário…
Aí você pensa: “posso fazer isso por trigger, por que fazer diferente?”
Eu respondo,,, se você trabalha em ambientes com alto volume de conexões, ou ambientes que as vezes sofrem com falta de recursos como cpu ou memória sabe que a trigger pode ser uma pedra no sapato,,, ao invés de respeitar o filtro e impedir apenas algumas conexões ela bloqueia tudo e todos…
Já com o Event Notification é o contrário,,, no pior cenário ele só para de funcionar…
Os dois pontos que vejo como mais complicados são:
Não é possível personalizar uma mensagem para o usuário que sofreu o kill
Dependendo do que você fizer, ele pode começar a encher o errorlog com mensagens de kill
com o código abaixo consegui chegar a 20.000 tentativas de conexão simultâneas e nenhuma conseguiu conectar,,,
--apenas para constatar quais tipos de eventos podemos tratar... o que queremos eh o audit_login
select * from sys.event_notification_event_types with (nolock)
where type_name like '%login%'
use master
go
alter database BASE_DE_ADMINISTRACAO_DO_DBA set enable_broker with rollback immediate
go
alter database BASE_DE_ADMINISTRACAO_DO_DBA set TRUSTWORTHY on
go
use BASE_DE_ADMINISTRACAO_DO_DBA
go
CREATE QUEUE [Login_Killer_Queue]
GO
CREATE SERVICE [Login_Killer_Service]
AUTHORIZATION [dbo]
ON QUEUE [dbo].[Login_Killer_QUEUE]
([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])
GO
CREATE ROUTE Login_Killer_Route
WITH SERVICE_NAME = 'Login_Killer_Service',
ADDRESS = 'LOCAL';
GO
DECLARE @AuditServiceBrokerGuid [uniqueidentifier]
,@SQL [varchar](max);
-- Pega o service broker guid da base de dados
SELECT @AuditServiceBrokerGuid = [service_broker_guid]
FROM [master].[sys].[databases]
WHERE [name] = 'ADM_BDADOS'
-- Cria e executa o SQL dinamico para criar o objeto do evento de notificacao
SET @SQL = 'IF EXISTS (SELECT * FROM sys.server_event_notifications
WHERE name = ''Login_Killer_Notification'')
DROP EVENT NOTIFICATION Login_Killer_EventNotification ON SERVER
CREATE EVENT NOTIFICATION Login_Killer_EventNotification
ON SERVER
WITH fan_in
FOR AUDIT_LOGIN
TO SERVICE ''Login_Killer_Service'', '''
+ CAST(@AuditServiceBrokerGuid AS [varchar](50)) + ''';'
EXEC (@SQL)
GO
SELECT * FROM [sys].[server_event_notifications]
select * from sys.server_event_session_actions with (nolock)
CREATE TABLE [dbo].[DBA_FailedConnectionTracker](
[host_name] [varchar](128) NOT NULL,
[login_name] [varchar](128) NOT NULL,
[spidu] int,
[FailedLoginData] XML
) ;
CREATE PROCEDURE dbo.spc_DBA_FailedConnectionTracker
AS
BEGIN
SET NOCOUNT ON;
-- looping infinito
WHILE (1 = 1)
BEGIN
DECLARE @messageBody VARBINARY(MAX);
DECLARE @messageTypeName NVARCHAR(256);
WAITFOR (
RECEIVE TOP(1)
@messageTypeName = message_type_name,
@messageBody = message_body
FROM [Login_Killer_Queue]
), TIMEOUT 500
-- se nao houver mensagens saia
IF @@ROWCOUNT = 0
BEGIN
BREAK ;
END ;
-- se o tipo da mensagem for um EventNotification para a fila atual
IF (@messageTypeName = 'http://schemas.microsoft.com/SQL/Notifications/EventNotification')
BEGIN
DECLARE @XML XML,
@host_name varchar(128) ,
@login_name varchar(128) ,
@SPID varchar(5);
SELECT @XML=CONVERT(XML,@messageBody)
,@host_name = ''
,@login_name = ''
,@SPID ='';
-- Pega o SPID e as informacoes de login
SELECT @SPID = @XML.value('(/EVENT_INSTANCE/SPID)[1]', 'varchar(5)')
, @host_name = @XML.value('(/EVENT_INSTANCE/HostName)[1]', 'NVARCHAR(128)')
, @login_name = @XML.value('(/EVENT_INSTANCE/LoginName)[1]', 'NVARCHAR(128)')
;
DECLARE @kill varchar(8000) = '';
--Caso o hostname e o login entrem no criterio abaixo
if ((@host_name like 'SPO%') and (@login_name like '%_user'))
SELECT @kill = @kill + 'kill ' + @SPID + ';'
EXEC(@kill);
--/* esta parte pode ser comentada para não gerar "log" do kill
if ((@host_name like 'SPO%') and (@login_name like '%_user'))
INSERT INTO [dbo].[DBA_FailedConnectionTracker]
([host_name], [login_name], FailedLoginData,spidu)
values ( @host_name, @login_name,@XML,@SPID);
--*/
END;
END;
END;
--inicia a matanca
ALTER QUEUE [dbo].[Login_Killer_Queue]
WITH STATUS = on
,ACTIVATION (PROCEDURE_NAME = [spc_DBA_FailedConnectionTracker]
,STATUS = ON
,MAX_QUEUE_READERS = 1
,EXECUTE AS OWNER)
GO
Esse código mostra o tamanho ocupado por cada pacote de SSIS carregado dentro do MSDB.
Algumas vezes o MSDB começa a ganhar proporções ocupadas que apenas a limpeza de rotina não dá conta e o pessoal esquece que os pacotes também ocupam espaço…
with ChildFolders
as
(
select PARENT.parentfolderid, PARENT.folderid, PARENT.foldername,
cast('' as sysname) as RootFolder,
cast(PARENT.foldername as varchar(max)) as FullPath,
0 as Lvl
from msdb.dbo.sysssispackagefolders PARENT
where PARENT.parentfolderid is null
UNION ALL
select CHILD.parentfolderid, CHILD.folderid, CHILD.foldername,
case ChildFolders.Lvl
when 0 then CHILD.foldername
else ChildFolders.RootFolder
end as RootFolder,
cast(ChildFolders.FullPath + '/' + CHILD.foldername as varchar(max))
as FullPath,
ChildFolders.Lvl + 1 as Lvl
from msdb.dbo.sysssispackagefolders CHILD
inner join ChildFolders on ChildFolders.folderid = CHILD.parentfolderid
)
select F.RootFolder, F.FullPath, P.name as PackageName,
P.description as PackageDescription, P.packageformat, P.packagetype,
P.vermajor, P.verminor, P.verbuild, P.vercomments,
cast(cast(P.packagedata as varbinary(max)) as xml) as PackageData,datalength(P.packagedata)/1024 AS SpaeceusedKB
from ChildFolders F
inner join msdb.dbo.sysssispackages P on P.folderid = F.folderid
order by F.FullPath asc, P.name asc;
A algumas semanas tivemos a necessidade de substituir a forma que usávamos para monitorar Dead Lock por alguma coisa mais usual…
Antes o Dead Lock era gravado no errorlog e depois encaminhado por email para o time,,, isso não é usual já que fica colocando muita informação desnecessária no errorlog e depois de alguns restarts perde-se algum histórico…
Com isso, cheguei a conclusão,,, vamos usar o event notification….
A vantagem é ser muito menos intrusivo que uma trigger e bem mais fácil de trabalhar que o System_Health…
Qual a ideia:
O Event Notification vai enviar um email para cada vez que o evento de DeadLock acontecer, neste email vai conter informação da base que sofreu o DeadLock, horário e o DeadLock Graphic.
Vai gravar em uma tabela na base de administração do DBA informações como: data e hora, base, XML com o evento.
use master
go
/*você já deve ter uma base de administração, caso não tenha, crie uma e troque o nome*/
alter database BASE_DE_ADMINISTRACAO_DO_DBA set enable_broker with rollback immediate
go
alter database BASE_DE_ADMINISTRACAO_DO_DBA set TRUSTWORTHY on
go
use BASE_DE_ADMINISTRACAO_DO_DBA
go
/**criando a tabela de controle/
create TABLE [dbo].[SQLDeadlockEvents](
[EventRowID] [bigint] primary key IDENTITY(1,1) NOT NULL,
[EventType] [nvarchar](128) NOT NULL,
[AlertTime] [datetime] NULL,
[DeadlockGraph] NULL,
[DatabaseName] [sysname] null,
[AuditDate] [smalldatetime]
) ON [FG_Data_01]
with (data_compression = PAGE) --neste caso tenho EE se você usar SE comente esta linha
GO
/**Criando a fila do Broker/
CREATE QUEUE [DeadlockGraphQueue]
GO
/*Criando o serviço para a fila do Broker*/
CREATE SERVICE [DeadlockEventsService]
AUTHORIZATION [dbo]
ON QUEUE [dbo].[DeadLockGraphQueue]
([http://schemas.microsoft.com/SQL/Notifications/PostEventNotification])
GO
/*criando rota*/
CREATE ROUTE DeadlockGraphRoute
WITH SERVICE_NAME = 'DeadlockEventsService',
ADDRESS = 'LOCAL';
GO
/* Para ficar mais fácil ele vai criar o event notification com os dados dinamicamente, se não, eu teria que explicar que o guid altera, onde tem que pegar essa informação,,, era mais SQL para uma coisa que pode ser feita de forma dinâmica */
DECLARE @AuditServiceBrokerGuid [uniqueidentifier]
,@SQL [varchar](max);
-- broker guid do CaptureDeadlockGraph
SELECT @AuditServiceBrokerGuid = [service_broker_guid]
FROM [master].[sys].[databases]
WHERE [name] = 'BASE_DE_ADMINISTRACAO_DO_DBA'
SET @SQL = 'IF EXISTS (SELECT * FROM sys.server_event_notifications
WHERE name = ''DeadlockGraphEventNotification'')
DROP EVENT NOTIFICATION DeadlockGraphEventNotification ON SERVER
CREATE EVENT NOTIFICATION DeadlockGraphEventNotification
ON SERVER --aqui é o detalhe legal... servidor e não database
WITH fan_in
FOR DEADLOCK_GRAPH
TO SERVICE ''DeadlockEventsService'', '''
+ CAST(@AuditServiceBrokerGuid AS [varchar](50)) + ''';'
EXEC (@SQL)
GO
/*Para confirmar que foi criado,,, lembra que é nível servidor,,, por isso a consulta no [sys].[server_event_notifications] */
SELECT * FROM [sys].[server_event_notifications]
WHERE [name] = 'DeadlockGraphEventNotification';
GO
/* onde a mágica acontece.... */
create PROC [dbo].[usp_sCaptureDeadlockGraph]
AS BEGIN
SET NOCOUNT ON
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
DECLARE @SQLProcedureName [varchar](64)
SET @SQLProcedureName = 'usp_sCaptureDeadlockGraph'
BEGIN TRY
DECLARE @message_body
, @EventTime [datetime]
, @EventType [varchar](128)
, @message_type_name [nvarchar](256)
, @dialog [uniqueidentifier]
-- Endless loop
WHILE (1 = 1)
BEGIN
BEGIN TRANSACTION ;
-- Receive the next available message
WAITFOR (RECEIVE TOP(1)
@message_type_name = [message_type_name],
@message_body = [message_body],
@dialog = [conversation_handle]
FROM [dbo].[DeadlockGraphQueue]), TIMEOUT 2000
-- Rollback and exit if no messages were found
IF (@@ROWCOUNT = 0)
BEGIN
ROLLBACK TRANSACTION;
BREAK;
END;
-- End conversation of end dialog message
IF (@message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
BEGIN
PRINT 'End Dialog received for dialog # ' + CAST(@dialog as [nvarchar](40));
END CONVERSATION @dialog;
END;
ELSE
BEGIN
SET @EventTime = CAST(CAST(@message_body.query('/EVENT_INSTANCE/PostTime/text()') AS [nvarchar](max)) AS [datetime])
SET @EventType = CAST(@message_body.query('/EVENT_INSTANCE/EventType/text()') AS [nvarchar](128))
INSERT INTO [dbo].[SQLDeadlockEvents] ([EventType], [AlertTime], [DeadlockGraph])
VALUES (@EventType, @EventTime, @message_body)
END
COMMIT TRANSACTION
DECLARE @MailSubject [nvarchar](256)
,@MailBody [nvarchar](max)
,@SQLQuery [nvarchar](max)
,@AttachedFileName [nvarchar](256)
,@EventDateTime [datetime]
,@EventID [int]
,@Counter [int]
,@dbname [sysname]
IF EXISTS (SELECT * FROM [tempdb].[dbo].[sysobjects]
WHERE name = '##ListDeadlocks' AND type in (N'U'))
DROP TABLE ##ListDeadlocks
CREATE TABLE ##ListDeadlocks
([EventRowID] [int]
,[AlertTime] [datetime]
,[DeadlockGraph]
,[DeadlockXml] )
INSERT INTO ##ListDeadlocks
([EventRowID]
,[AlertTime]
,[DeadlockGraph]
,[DeadlockXml])
SELECT [EventRowID]
,[AlertTime]
,[DeadlockGraph].query('./EVENT_INSTANCE/TextData/deadlock-list')
,DeadlockGraph FROM [dbo].[SQLDeadlockEvents]
WHERE [EventRowID] = @@IDENTITY
select @dbname = db_name(DeadlockGraph.value( '(/deadlock-list//@dbid)[1]', 'int' ))
--db_name(DeadlockGraph.value( '(/EVENT_INSTANCE/TextData/deadlock-list//@dbid)[1]', 'int' ) )
FROM ##ListDeadlocks
--where [EventRowID] = SCOPE_IDENTITY()
update [dbo].[SQLDeadlockEvents]
set [DatabaseName] = @dbname
where [EventRowID] = (select [EventRowID] FROM ##ListDeadlocks )
SELECT @Counter = MIN(EventRowID)
FROM ##ListDeadlocks
WHILE @Counter IS NOT NULL
BEGIN
SELECT @EventDateTime = [AlertTime]
,@EventID = [EventRowID]
FROM ##ListDeadlocks
WHERE [EventRowID] = @Counter
SELECT @MailSubject = 'Um deadlock ocorreu em ' + cast(@dbname as [varchar](256)) --+' servidor: '
--+ CONVERT([varchar](128), SERVERPROPERTY('ServerName'))
,@MailBody = 'Deadlock EventRowID: ' + CAST(@EventID AS [varchar](12))
+ char(13) + char(13) + 'O deadlock ocorreu as '
+ CONVERT([varchar](50), @EventDateTime, 120)
+ ' no Servidor: ' + CONVERT([varchar](128), SERVERPROPERTY('ServerName'))+'.'+char(13) + char(13) + 'Verifique o anexo xdl-file para mais detalhes.'
+ char(13) + char(13) +'Para mais informações ou eventos anteriores consulte: SELECT * FROM BASE_DE_ADMINISTRACAO_DO_DBA.dbo.SQLDeadlockEvents WITH (NOLOCK)'
,@SQLQuery = 'SET NOCOUNT ON; SELECT [DeadlockGraph] FROM [##ListDeadlocks] WITH (READUNCOMMITTED) WHERE EventRowID = '
+ CAST(@EventID AS [varchar](10))
,@AttachedFileName = CONVERT([varchar](128), SERVERPROPERTY('ServerName')) + '_' + CAST(@EventID AS [varchar](12)) + '.xdl'
EXEC msdb.dbo.sp_send_dbmail
@recipients = 'email@dominio.com.br' -- troque o email aqui
,@subject = @MailSubject
,@body = @MailBody
,@query = @SQLQuery
,@attach_query_result_as_file = 1 ,@query_attachment_filename = @AttachedFileName -- http://support.microsoft.com/kb/924345
,@query_result_header = 0
,@query_result_width = 32767
,@query_no_truncate = 1
DELETE FROM ##ListDeadlocks
WHERE EventRowID = @Counter
SELECT @Counter = MIN(EventRowID)
FROM ##ListDeadlocks
end
END --End of loop
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER()
,ERROR_SEVERITY()
,ERROR_STATE()
,ISNULL(ERROR_PROCEDURE(), @SQLProcedureName)
,ERROR_LINE()
,ERROR_MESSAGE()
END CATCH
END
GO
/*inicia a fila para começar a capturar os DeadLocks*/
ALTER QUEUE [dbo].[DeadlockGraphQueue]
WITH STATUS = ON
,ACTIVATION (PROCEDURE_NAME = [usp_sCaptureDeadlockGraph]
,STATUS = ON
,MAX_QUEUE_READERS = 1
,EXECUTE AS OWNER)
GO
O ciclo de vida da maioria dos bancos envolve desenvolvedores e DBA´s compartilhando scripts para atualização de objetos e atividades de manutenção.
Existem algumas formas de fazer isso:
gerar scripts de objetos
Proporcionar acesso as partes interessadas (piada… eu sei…) para gerar os scripts
DACPAC´s
Backup/Restore
etc..
Um pouco de contexto antes,,,
Um DAC (aplicativo da camada de dados) é uma entidade lógica de gerenciamento de banco de dados que define todos os objetos do SQL Server, como tabelas, exibições e objetos de instância, incluindo logons, associados a um banco de dados de usuário.
Todos tem seus prós e contras… não é a intenção deste post tratar isso…
Para quem gosta de gerar scripts dos objetos lembra que tem um outro post que faz isso…
A vantagem do DACPAC é que você pode usa-lo para comparação entre um projeto do TFS e esse pacote extraído da produção ou, caso você seja o DBA, você pode comparar esse pacote contra a sua base de produção ou uma base em homologação. Gerar os scripts para igualar os ambientes ou apenas conseguir ter ideia das diferenças.
Quando clicar no “…” para mapear o caminho, se não aparecer uma janela pop-up, movimenta a janela do app um pouco para o lado, ela ficou atrás da janela inicial,,, isso é um bug que acontece de vez em quando,,,
se vc clicar diversas vezes no “…” ele vai ficar abrindo diversas vezes…
ainda não estou tratando outros tipos de entrada no campo texto,,,, você deve colocar um banco abaixo do outro,,,
e sim,,, ele fecha a janela quando você clica em OK,,,
Ele ficou até que bem simpático…
Com os campos preenchidos
E tem até barra de status… olha que chique…
Manage Cookie Consent
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
Functional
Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.