SYBASE ASE上排查問題自定義存儲過程
背景
SYBASE 自帶不少排查問題用的存儲過程和MDA表,但是在排查問題時這些存儲過程要麽輸出太詳實太專業不容易聚焦問題,要麽需要聯查多張表在轉瞬即逝的性能問題面前不能捕獲有效的信息。編寫整理幾個存儲過程,希望對大家排查問題有所幫助,同時不足之處還請大家指正!
一、排查全局超大表
過程名稱:sp_dba_largetable
排查場景:隨著業務系統發展,生產環境的大表已經不是規劃時的實體主表,而是一些意料之外附屬表,包括日誌表、歷史表、備份表、臨時表等,超大表嚴重占用數據庫系統資源,影響核心業務流程。
SQL代碼:
use sybsystemprocs
go
if object_id(‘sp_dba_largetable‘ ) is not null
drop procedure sp_dba_largetable
GO
create procedure sp_dba_largetable
AS
--查看超大數據對象
--add by wangzhen 2017-07-11
begin
declare @temp_sql varchar(500)
declare @sql varchar(1000)
declare @dbname varchar(100)
declare dbname_cursor cursor for select name from master..sysdatabases
create table #objectinfo (
dbname varchar(300),
objid int,
objname varchar(300),
pagecnt bigint,
leafcnt bigint,
rowcnt bigint
)
set @temp_sql = ‘insert into #objectinfo select ‘‘@dbname#‘‘ as dbname,ind.id,ind.name,stat.pagecnt,stat.leafcnt,stat.rowcnt from @dbname#..systabstats stat left join @dbname#..sysindexes ind on stat.id = ind.id and stat.indid = ind.indid‘
open dbname_cursor
while @@sqlstatus =0
BEGIN
FETCH dbname_cursor into @dbname
set @sql = str_replace(@temp_sql,‘@dbname#‘,@dbname)
EXECUTE(@sql)
END
close dbname_cursor
select top 100 t.dbname as "庫名",t.objid as "對象ID",t.objname as "對象名",
t.rowcnt as "行數",t.datasize as "數據大小(KB)",
t.indexsize as "索引大小(KB)" from
(select dbname ,max(objname) objname,objid ,
max(rowcnt) rowcnt,(sum(pagecnt) * @@maxpagesize/1024) as datasize,
(sum(leafcnt) * @@maxpagesize/1024) as indexsize
from #objectinfo group by dbname,objid ) t order by t.rowcnt desc
drop table #objectinfo
end
go
- 示例結果:
二、排查全局聚簇索引表
過程名稱:sp_dba_citable
排查場景:很多項目使用UUID做為主鍵,如NP。這種情況下默認的聚簇索引主鍵會造成一定的性能問題,NP項目開發規範要求及SMD默認生成的SYBASE主鍵都是非聚簇索引。因為一些歷史原因實際生產環境中可能存在不少聚簇索引,需要排查矯正。
SQL代碼:
use sybsystemprocs
go
if object_id(‘sp_dba_citable‘) is not null
drop procedure sp_dba_citable
go
create procedure sp_dba_citable
AS
--查看聚簇索引表
--add by wangzhen 2017-07-17
begin
declare @temp_sql varchar(500)
declare @sql varchar(1000)
declare @dbname varchar(100)
declare dbname_cursor cursor for select name from master..sysdatabases
create table #objectinfo (
dbname varchar(100),
objid int,
tablename varchar(300),
indexid int,
indexname varchar(300),
keycnt int,
indextype varchar(100)
)
create table #objectinfo2 (
dbname varchar(100),
objid int,
tablename varchar(300),
indexid int,
indexname varchar(300),
keycnt int,
indexkey varchar(1000) null,
indextype varchar(100)
)
set @temp_sql = ‘insert into #objectinfo ‘
+ ‘select ‘‘@dbname#‘‘ , ‘
+ ‘obj.id , ‘
+ ‘obj.name , ‘
+ ‘ind.indid , ‘
+ ‘ind.name , ‘
+ ‘ind.keycnt , ‘
+ ‘‘‘culster index‘‘ ‘
+‘ from @dbname#..sysindexes ind left join @dbname#..sysobjects obj on ind.id = obj.id ‘
+‘ where (ind.status2 & 512 = 512 or ind.indid = 1) and obj.type = ‘‘U‘‘ ‘
open dbname_cursor
while @@sqlstatus =0
BEGIN
FETCH dbname_cursor into @dbname
set @sql = str_replace(@temp_sql,‘@dbname#‘,@dbname)
EXECUTE(@sql)
END
close dbname_cursor
insert into #objectinfo2 (t.dbname,objid,tablename,indexid,indexname,keycnt,indextype,indexkey)
select
t.dbname ,
t.objid ,
t.tablename ,
max(t.indexid) ,
t.indexname ,
max(t.keycnt) ,
t.indextype ,
case when max(t.keycnt) =2 then
index_col(t.dbname+‘..‘+t.tablename,max(t.indexid),1)+‘ ‘+index_colorder(t.dbname+‘..‘+t.tablename,max(t.indexid),1)
when max(t.keycnt) =3 then
index_col(t.dbname+‘..‘+t.tablename,max(t.indexid),1)+‘ ‘+index_colorder(t.dbname+‘..‘+t.tablename,max(t.indexid),1)
+‘,‘+
index_col(t.dbname+‘..‘+t.tablename,max(t.indexid),2)+‘ ‘+index_colorder(t.dbname+‘..‘+t.tablename,max(t.indexid),2)
when max(t.keycnt) =4 then
index_col(t.dbname+‘..‘+t.tablename,max(t.indexid),1)+‘ ‘+index_colorder(t.dbname+‘..‘+t.tablename,max(t.indexid),1)
+‘,‘+
index_col(t.dbname+‘..‘+t.tablename,max(t.indexid),2)+‘ ‘+index_colorder(t.dbname+‘..‘+t.tablename,max(t.indexid),2)
+‘,‘+
index_col(t.dbname+‘..‘+t.tablename,max(t.indexid),3)+‘ ‘+index_colorder(t.dbname+‘..‘+t.tablename,max(t.indexid),3)
else
null
end
from #objectinfo t
where t.dbname not in (‘master‘,‘tempdb‘,‘sybsecurity‘,‘sybsystemdb‘,‘sybsystemprocs‘)
group by t.dbname,t.objid,t.tablename,t.indexname,t.indextype order by t.dbname asc,t.objid asc
select
t.dbname as "庫名",
t.objid as "對象ID",
t.tablename as "表名",
t.indexname as "索引名",
t.indexkey as "索引鍵",
t.keycnt -1 as "索引鍵數量",
t.indextype as "索引描述"
from #objectinfo2 t group by t.dbname,t.objid,t.tablename,t.indexname,t.keycnt,t.indextype order by t.dbname asc,t.tablename asc
end
go
- 示例結果:
三、排查數據庫表分析
過程名稱: sp_dba_statistics
排查場景: 數據庫表分析是否及時更新極大影響著數據庫SQL的執行效率,每隔一段時間,數據矯正,數據遷移完成後都應該及時更新數據庫表分析,超過一個月未更新,要引起註意。
SQL代碼
use sybsystemprocs
go
if object_id(‘sp_dba_statistics‘) is not null
drop procedure sp_dba_statistics
go
create procedure sp_dba_statistics
AS
--查看超過一個月未更新的統計值
--add by wangzhen 2017-08-08
begin
declare @temp_sql varchar(500)
declare @sql varchar(1000)
declare @dbname varchar(100)
declare dbname_cursor cursor for select name from master..sysdatabases
create table #objectinfo (
dbname varchar(100),
objid int,
tablename varchar(100),
moddate datetime,
curdate datetime
)
set @temp_sql = ‘insert into #objectinfo ‘
+ ‘select ‘‘@dbname#‘‘ , ‘
+ ‘obj.id , ‘
+ ‘obj.name , ‘
+ ‘stat.moddate,‘
+ ‘getdate() ‘
+‘ from @dbname#..sysstatistics stat left join @dbname#..sysobjects obj on stat.id = obj.id ‘
+‘ where obj.type = ‘‘U‘‘ and stat.moddate < dateadd(day,-60,getdate()) ‘
open dbname_cursor
while @@sqlstatus =0
BEGIN
FETCH dbname_cursor into @dbname
set @sql = str_replace(@temp_sql,‘@dbname#‘,@dbname)
EXECUTE(@sql)
END
close dbname_cursor
select
t.dbname as "庫名",
t.tablename as "表名",
max(t.moddate) as "更改時間",
max(t.curdate) as "當前時間"
from #objectinfo t where t.dbname not in (‘master‘,‘tempdb‘,‘sybsecurity‘,‘sybsystemdb‘,‘sybsystemprocs‘)
group by t.dbname,t.tablename
having max(t.moddate) < dateadd(day,-60,getdate())
order by t.dbname asc,max(t.moddate) desc
end
go
- 示例結果:
四、排查全局庫空間信息
過程名稱:sp_dba_dbspaceinfo
排查場景:生產環境中數據增長速度很快,數據庫空間不足會引起不少問題,需要經常排查。
SQL代碼:
use sybsystemprocs
go
if object_id(‘sp_dba_dbspaceinfo‘) is not null
drop procedure sp_dba_dbspaceinfo
GO
create procedure sp_dba_dbspaceinfo
AS
--查看數據庫的空間信息
--add by wangzhen 2017-07-11
begin
select
convert(char(16),db_name(data_segment.dbid)) as "庫名",
str(round(total_data_pages / ((1024.0 * 1024) / @@maxpagesize),2),10,2) "總數據空間(MB)",
str(round(free_data_pages / ((1024.0 * 1024) / @@maxpagesize),2),10,2) "剩余數據空間(MB)",
str(round(total_log_pages / ((1024.0 * 1024) / @@maxpagesize),2),10,2) "總日誌空間(MB)",
str(round(free_log_pages / ((1024.0 * 1024) / @@maxpagesize),2),10,2) "剩余日誌空間(MB)",
str( round(100.0 * free_data_pages / total_data_pages ,2),10,2) "剩余數據百分比%",
str( round(100.0 * free_log_pages / total_log_pages,2),10,2) "剩余日誌百分比%"
from
(select dbid,
sum(size) total_log_pages,
lct_admin(‘logsegment_freepages‘, dbid ) free_log_pages
from master.dbo.sysusages
where segmap & 4 = 4
group by dbid
) log_segment
,
(select dbid,
sum(size) total_data_pages ,
sum(curunreservedpgs(dbid, lstart, unreservedpgs)) free_data_pages
from master.dbo.sysusages
where segmap <> 4
group by dbid
) data_segment
where data_segment.dbid = log_segment.dbid
order by str( round(100.0 * free_data_pages / total_data_pages ,2),10,2) asc
end
GO
- 示例結果:
五、排查鎖信息
過程名稱:sp_dba_lock
排查場景:鎖是數據庫問題排查的一個必須步驟,默認的sp_lock顯示的信息不夠詳細,不能很好判斷問題
SQL代碼:
use sybsystemprocs
go
if object_id(‘sp_dba_lock‘) is not null
drop procedure sp_dba_lock
go
create procedure sp_dba_lock
AS
--查看鎖
--add by dba team
--2017-07-12
begin
declare @temp_sql varchar(500)
declare @sql varchar(10000)
declare @unionsql varchar(10000)
declare @unionsql2 varchar(10000)
declare @dbname varchar(100)
declare dbname_cursor cursor for select name from master..sysdatabases
set @temp_sql = ‘ select id as objid,name as objname,db_id(‘‘@dbname‘‘) as dbid from @dbname..sysobjects ‘
set @sql = ‘ select pr.spid as "進程ID" , ‘
+‘ pr.ipaddr as "IP地址", ‘
+‘ pr.program_name as "應用名稱", ‘
+‘ pr.cmd AS "執行命令", ‘
+‘ db_name(lc.dbid) as "數據庫名", ‘
+‘ obj.objname as "對象名", ‘
+‘ (case when lc.type = 1 then ‘‘排他表鎖‘‘ ‘
+‘ when lc.type = 2 then ‘‘共享表鎖‘‘ ‘
+‘ when lc.type = 3 then ‘‘排他意向鎖‘‘ ‘
+‘ when lc.type = 4 then ‘‘共享意圖鎖‘‘ ‘
+‘ when lc.type = 5 then ‘‘排他?鎖‘‘ ‘
+‘ when lc.type = 6 then ‘‘共享?鎖‘‘ ‘
+‘ when lc.type = 7 then ‘‘更新?鎖‘‘ ‘
+‘ when lc.type = 8 then ‘‘排他行鎖‘‘ ‘
+‘ when lc.type = 9 then ‘‘共享行鎖‘‘ ‘
+‘ when lc.type = 10 then ‘‘更新行鎖‘‘ ‘
+‘ when lc.type = 11 then ‘‘共享下一鍵鎖‘‘ ‘
+‘ when lc.type = 256 then ‘‘鎖阻塞另一進程‘‘ ‘
+‘ when lc.type = 512 then ‘‘請求鎖‘‘ ‘
+‘ end) as "鎖類型名稱", ‘
+‘ lc.type as "鎖類型", ‘
+‘ pr.blocked as "被阻塞進程ID",‘
+‘ bl.program_name as "被阻塞應用名稱", ‘
+‘ bl.ipaddr as "被阻塞IP地址" ‘
+‘ from master..syslocks lc ‘
+‘ left join master..sysprocesses pr on lc.spid = pr.spid ‘
+‘ left join master..sysprocesses bl on bl.spid = pr.blocked ‘
+‘ left join ( ‘
open dbname_cursor
while @@sqlstatus = 0
begin
FETCH dbname_cursor into @dbname
set @unionsql = @unionsql + str_replace(@temp_sql,‘@dbname‘,@dbname)
set @unionsql = @unionsql + ‘union all‘
end
close dbname_cursor
set @unionsql2 = substring(@unionsql,1,char_length(@unionsql) - 9)
set @sql = @sql + @unionsql2 + ‘ ) obj on lc.id = obj.objid and lc.dbid = obj.dbid ‘
execute(@sql)
end
GO
- 示例結果:
六、排查耗費CPU資源的SQL
過程名稱:sp_dba_cpu
排查場景:數據庫CPU高時,需要排查的一個方面是正在耗費CPU資源的SQL
SQL代碼:
use sybsystemprocs
GO
if object_id(‘sp_dba_cpu‘) is not null
drop procedure sp_dba_cpu
GO
create proc sp_dba_cpu
as
begin
select top 100 s.SPID,p.ipaddr,p.program_name,s.CpuTime,t.LineNumber,t.SQLText
from
master..monProcessStatement s,
master..monProcessSQLText t,
master..sysprocesses p
where
s.SPID=t.SPID
and s.SPID = p.spid
and p.spid != @@spid
order by
s.CpuTime DESC
end
總結
通過自定義的存儲過程,我們可以更加快速的獲取信息,定位問題。還有一些查看索引缺失、IO高的SQL等存儲過程不再這裏贅述!
SYBASE ASE上排查問題自定義存儲過程