上节回顾:http://www.javashuo.com/article/p-kivzmehu-k.htmlhtml
脚本示意:https://github.com/lotapp/BaseCode/tree/master/database/SQL/SQLServernode
PS:这些脚本都是我之前用SQLServer手写的,参考便可(如今用MySQL,下次也整理一下)git
以前写SQLServer监控系列文章由于换环境断篇了,只是简单演示了下基础功能,如今准备写MySQL
监控相关内容了,因而补了下:github
SQLServer性能优化之---数据库级日记监控:http://www.javashuo.com/article/p-nnywaxsf-da.htmlsql
在说监控前你能够先看下数据库发邮件:http://www.javashuo.com/article/p-btuevyov-gn.html数据库
应用:通常就是设置个定时任务,把耗时SQL信息或者错误信息经过邮件的方式及时预警性能优化
好处就太多了,eg:客户出错若是是数据库层面,那瞬间就能够场景重放(PS:等客户找会下降业绩)服务器
以往都是程序的try
+catch
来捕获错误,但数据库定时任务之类的出错程序是捕获不到的,因此就须要数据库层面的监控了session
PS:开发的时候经过
SQLServer Profiler
来监控架构
先说说本质吧:SQLServer2012的XEVENT机制已经完善,eg:经常使用的扩展事件error_reported
就能够在错误的时候经过邮件来通知管理员了
PS:扩展事件性能较高,并且比较轻量级
PS:SQLServer的监控大致思路三步走:发邮件
,事件监控
,定时执行
这个以前讲过,这边就再说下SQL的方式:
这个配置一次便可,之后使用就能够直接经过配置名发邮件:
--开启发邮件功能 exec sp_configure 'show advanced options',1 reconfigure with override go exec sp_configure 'database mail xps',1 reconfigure with override go --建立邮件账户信息 exec msdb.dbo.sysmail_add_account_sp @account_name ='dunitian', -- 邮件账户名称 @email_address ='xxx@163.com', -- 发件人邮件地址 @display_name ='SQLServer2014_192.168.36.250', -- 发件人姓名 @MAILSERVER_NAME = 'smtp.163.com', -- 邮件服务器地址 @PORT =25, -- 邮件服务器端口 @USERNAME = 'xxx@163.com', -- 用户名 @PASSWORD = '邮件密码或受权码' -- 密码(受权码) GO --数据库配置文件 exec msdb.dbo.sysmail_add_profile_sp @profile_name = 'SQLServer_DotNetCrazy', -- 配置名称 @description = '数据库邮件配置文件' -- 配置描述 go --用户和邮件配置文件相关联 exec msdb.dbo.sysmail_add_profileaccount_sp @profile_name = 'SQLServer_DotNetCrazy', -- 配置名称 @account_name = 'dunitian', -- 邮件账户名称 @sequence_number = 1 -- account 在 profile 中顺序(默认是1) go
一样我只演示SQL的方式,图形化的方式能够看我之前写的文章:
-- 发邮件测试 exec msdb.dbo.sp_send_dbmail @profile_name = 'SQLServer_DotNetCrazy', --配置名称 @recipients = 'xxx@qq.com', --收件邮箱 @body_format = 'HTML', --内容格式 @subject = '文章标题', --文章标题 @body = '邮件内容<br/><h2>This is Test</h2>...' --邮件内容
效果:
主要用途其实就是出错排查:
-- 查询相关 select * from msdb.dbo.sysmail_allitems --查看全部邮件消息 select * from msdb.dbo.sysmail_mailitems --查看邮件消息(更多列) select * from msdb.dbo.sysmail_sentitems --查看已发送的消息 select * from msdb.dbo.sysmail_faileditems --失败状态的消息 select * from msdb.dbo.sysmail_unsentitems --看未发送的消息 select * from msdb.dbo.sysmail_event_log --查看记录日记
会了邮件的发送,那下面就是监控了
不推荐使用图形化的方式,但能够来理解扩展事件的监控
1.新建一个会话向导(熟悉后能够直接新建会话)
2.设置须要捕获的扩展事件
3.这边捕获的全局字段和左边SQL是同样的(截图全太麻烦了,因此偷个懒,后面会说怎么生成左边的核心SQL)
4.本身根据服务器性能设置一个合理的值(IO、内存、CPU)
5.生成核心SQL(咱们图形化的目的就是生成核心SQL,后面能够根据这个SQL本身扩展)
6.核心代码以下
7.启动会话后一个简单的扩展事件监控就有了
8.SQLServer提供了查看方式
9.日志能够本身查下xxx\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Log
上面只是过家家,主要目的就是让你们知道核心SQL是怎么来的,凭什么这么写
下面就来个制定化监控:
先截图演示下各个核心点,而后贴一个我封装的存储过程附件
1.扩展事件相关的核心代码
2.内存中数据存储到临时表
3.临时表中的数据存储到本身创建的表中
我抛一个课后小问给你们:为何先存储在临时表中?(提示:效率)
4.发送监控提醒的邮件
5.看看数据库层面多了什么:
6.来个测试
7.效果(能够本身美化)
-- 切换到须要监控的数据库 USE [dotnetcrazy] GO --收集服务器上逻辑错误的信息 SET QUOTED_IDENTIFIER ON SET ANSI_NULLS ON GO -- 自定义的错误信息表 IF OBJECT_ID('log_error_message') IS NULL BEGIN CREATE TABLE [dbo].[log_error_message] ( [login_message_id] [uniqueidentifier] NULL CONSTRAINT [DF__PerfLogic__Login__7ACA4E21] DEFAULT (newid()), [start_time] [datetime] NULL, [database_name] [nvarchar] (128) COLLATE Chinese_PRC_CI_AS NULL, [message] [nvarchar] (max) COLLATE Chinese_PRC_CI_AS NULL, [sql_text] [nvarchar] (max) COLLATE Chinese_PRC_CI_AS NULL, [alltext] [nvarchar] (max) COLLATE Chinese_PRC_CI_AS NULL, -- [worker_address] [nvarchar] (1000) COLLATE Chinese_PRC_CI_AS NULL, [username] [nvarchar] (1000) COLLATE Chinese_PRC_CI_AS NULL, [client_hostname] [nvarchar] (1000) COLLATE Chinese_PRC_CI_AS NULL, [client_app_name] [nvarchar] (1000) COLLATE Chinese_PRC_CI_AS NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] END GO -- 建立存储过程 CREATE PROCEDURE [dbo].[event_error_monitor] AS IF NOT EXISTS( SELECT 1 FROM sys.dm_xe_sessions dxs(NOLOCK) WHERE name = 'event_error_monitor') -- 不存在就建立EVENT -- 建立扩展事件,并把数据放入内存中 BEGIN CREATE EVENT session event_error_monitor on server ADD EVENT sqlserver.error_reported -- error_reported扩展事件 ( ACTION -- 返回结果 ( sqlserver.session_id, -- 会话id sqlserver.plan_handle, -- 计划句柄,可用于检索图形计划 sqlserver.tsql_stack, -- T-SQ堆栈信息 package0.callstack, -- 当前调用堆栈 sqlserver.sql_text, -- 遇到错误的SQL查询 sqlserver.username, -- 用户名 sqlserver.client_app_name, -- 客户端应用程序名称 sqlserver.client_hostname, -- 客户端主机名 -- sqlos.worker_address, -- 当前任务执行时间 sqlserver.database_name -- 当前数据库名称 ) WHERE severity >= 11 AND Severity <=16 -- 指定用户级错误 ) ADD TARGET package0.ring_buffer -- 临时放入内存中 WITH (max_dispatch_latency=1seconds) -- 启动监控事件 ALTER EVENT SESSION event_error_monitor on server state = START END ELSE -- 存储过程已经存在就把数据插入表中 BEGIN -- 将内存中已经收集到的错误信息转存到临时表中(方便处理) SELECT DATEADD(hh, DATEDIFF(hh, GETUTCDATE(), CURRENT_TIMESTAMP), n.value('(event/@timestamp)[1]', 'datetime2')) AS [timestamp], n.value('(event/action[@name="database_name"]/value)[1]', 'nvarchar(128)') AS [database_name], n.value('(event/action[@name="sql_text"]/value)[1]', 'nvarchar(max)') AS [sql_text], n.value('(event/data[@name="message"]/value)[1]', 'nvarchar(max)') AS [message], n.value('(event/action[@name="username"]/value)[1]', 'nvarchar(max)') AS [username], n.value('(event/action[@name="client_hostname"]/value)[1]', 'nvarchar(max)') AS [client_hostname], n.value('(event/action[@name="client_app_name"]/value)[1]', 'nvarchar(max)') AS [client_app_name], n.value('(event/action[@name="tsql_stack"]/value/frames/frame/@handle)[1]', 'varchar(max)') AS [tsql_stack], n.value('(event/action[@name="tsql_stack"]/value/frames/frame/@offsetStart)[1]', 'int') AS [statement_start_offset], n.value('(event/action[@name="tsql_stack"]/value/frames/frame/@offsetEnd)[1]', 'int') AS [statement_end_offset] into #error_monitor -- 临时表 FROM ( SELECT td.query('.') as n FROM ( SELECT CAST(target_data AS XML) as target_data FROM sys.dm_xe_sessions AS s JOIN sys.dm_xe_session_targets AS t ON t.event_session_address = s.address WHERE s.name = 'event_error_monitor' --AND t.target_name = 'ring_buffer' ) AS sub CROSS APPLY target_data.nodes('RingBufferTarget/event') AS q(td) ) as TAB -- 把数据存储到本身新建的表中(有SQL语句的直接插入到表中) INSERT INTO log_error_message(start_time,database_name,message,sql_text,alltext,username,client_hostname,client_app_name) SELECT TIMESTAMP,database_name,[message],sql_text,'',username,client_hostname,client_app_name FROM #error_monitor as a WHERE a.sql_text != '' --AND client_app_name !='Microsoft SQL Server Management Studio - 查询' AND a.MESSAGE NOT LIKE '找不到会话句柄%' AND a.MESSAGE NOT LIKE '%SqlQueryNotification%' --排除server broker AND a.MESSAGE NOT LIKE '远程服务已删除%' -- 插入应用执行信息(没有SQL的语句经过句柄查询下SQL) INSERT INTO log_error_message(start_time,database_name,message,sql_text,alltext,username,client_hostname,client_app_name) SELECT TIMESTAMP,database_name,[message], SUBSTRING(qt.text,a.statement_start_offset/2+1, (case when a.statement_end_offset = -1 then DATALENGTH(qt.text) else a.statement_end_offset end -a.statement_start_offset)/2 + 1) sql_text,qt.text alltext, username,client_hostname,client_app_name FROM #error_monitor as a CROSS APPLY sys.dm_exec_sql_text(CONVERT(VARBINARY(max),a.tsql_stack,1)) qt -- 经过句柄查询具体的SQL语句 WHERE a.sql_text IS NULL AND tsql_stack != '' --AND client_app_name = '.Net SqlClient Data Provider' DROP TABLE #error_monitor -- 删除临时表 --重启清空 ALTER EVENT SESSION event_error_monitor ON SERVER STATE = STOP ALTER EVENT SESSION event_error_monitor on server state = START END -- 美化版预警邮箱 DECLARE @body_html VARCHAR(max) set @body_html = '<table style="width:100%" cellspacing="0"><tr><td colspan="6" align="center" style="font-weight:bold;color:red">数据库错误监控</td></tr>' set @body_html = @body_html + '<tr style="text-align: left;"><th>运行时间</th><th>数据库</th><th>发生错误的SQL语句</th><th>消息</th><th>用户名</th><th>应用</th><th>应用程序名</th></tr>' -- 格式处理(没内容就空格填充) select @body_html = @body_html + '<tr><td>' + case (isnull(start_time, '')) when '' then ' ' else convert(varchar(20), start_time, 120) end + '</td><td>' + case (isnull(database_name, '')) when '' then ' ' else database_name end + '</td><td>' + case (isnull(sql_text, '')) when '' then ' ' else sql_text end + '</td><td>' + case (isnull(message, '')) when '' then ' ' else message end + '</td><td>' + case (isnull(username, '')) when '' then ' ' else username end + '</td><td>' + case (isnull(client_hostname, '')) when '' then ' ' else client_hostname end + '</td><td>' + case (isnull(client_app_name, '')) when '' then ' ' else client_app_name end + '</td></tr>' from ( select start_time, database_name,sql_text, message, username, client_hostname, client_app_name from [dbo].[log_error_message] where start_time >= dateadd(hh,-2,getdate()) -- 当前时间 - 定时任务的时间间隔(2h) and client_app_name != 'Microsoft SQL Server Management Studio - 查询' -- and client_hostname in('') ) as temp_message set @body_html= @body_html+'</table>' -- 发送警告邮件 exec msdb.dbo.sp_send_dbmail @profile_name = 'SQLServer_DotNetCrazy', --配置名称 @recipients = 'xxxxx@qq.com', --收件邮箱 @body_format = 'HTML', --内容格式 @subject = '数据库监控通知', --文章标题 @body = @body_html --邮件内容 go
下节预估:定时任务、完整版监控
PS:估计先得更八字的文章(拖过久)而后更完SQLServer更MySQL,等MySQL监控更完会说下备份与恢复,接着咱们开架构篇(MyCat系列先不讲放在Redis和爬虫系列的后面)
晚点在下面补上