1. 程式人生 > >SYBASE ASE上排查問題自定義存儲過程

SYBASE ASE上排查問題自定義存儲過程

gravity div sys page datetime 對象 定位問題 star 命令

背景

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上排查問題自定義存儲過程