志當存高遠,功到自然成!

          少年強則中國強,少年進步則中國進步!

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            53 Posts :: 2 Stories :: 2 Comments :: 0 Trackbacks

          #

          首先,有一句話要認識 : 80%的性能問題由SQL語句引起。

          經過看 SYBASE 的書,結合從 MSSQL 遷移過來的系統過程 ,發現以下幾個問題比較重要:
               經驗一、where 條件左邊最好不要使用函數,比如 ★★★★★
                     select ...     where     datediff(day,date1,getdate())>;0
                     這樣即使在 date1 列上建立了索引,也可能不會使用索引,而使用表掃描。
                      這樣的語句要重新規劃設計,保證不使用函數也能夠實現。通過修改,一個系統過程的運行效率提高大約100倍!此外不要使用諸如like '%ab',不能充分利用索引,而要在%前加字符
                經驗二、兩個比較字段最好使用相同數據類型,而不是兼容數據類型。比如 int 與 numeric(感覺一般不是太明顯)★★

                經驗三、復合索引的非前導列做條件時,基本沒有起到索引的作用。★★★★★
                      比如 create index idx_tablename_ab on tablename(a,b)
                          update tablename set c = XX where b>;= XXX and ...
                          在這個語句中,基本上索引沒有發揮作用。 導致表掃描引起blocking 甚至運行十幾分鐘后報告失敗。
                          一定要認真檢查 改正措施: 在接口中附加條件
                              update tablename set c = XX where a = XXX     and b>;= XXX
                           或者建立索引類似于
                              create index idx_tablename_ba on tablename(b,a)  
                經驗四、 多個大表的關聯查詢,如果性能不好,并且其中一個大表中取的數據比較少,可以考慮將查詢分兩步執行。★★★★
                       先將一個大表中的少部分數據 select * into #1 from largetable1 where ...
                        然后再用 #1 去做關聯,效果可能會好不少。(前提:生成 #1表應該使用比較好的索引,速度比較快)

                經驗五、 tempdb 的使用。★★★★★
                        最好多用 select into     ,這樣不記日志 ,尤其是有大量數據的報表時。雖然寫起來麻煩,但值得。
                          create table #tmp1 (......)這樣寫性能不好。尤其是大量使用時,容易發生tempdb 爭用。
                經驗六、 系統級別的參數設置     ★★★★
                         一定要估計一下,不要使用太多,占用資源     ,太少,發生性能問題。
                           連接數,索引打開個數、鎖個數 等、 當然 ,內存配置不要有明顯的問題,比如,procedure cache
                          不夠 (一般缺省20%,如果覺得太多,可以減少一些)。如果做報表經常使用大數據量讀,可以考慮使用
                          16Kdata cache

                 經驗七、索引的建立。很重要。★★★★★
                            clustered index     /nonclustered index 的差異,自己要搞清楚。各適用場合,另外如果
                          clustered index 不允許 重復數,也一定要說明。
                         索引設計是以為數據訪問快速為原則的,不能 完全(!!) 參照數據邏輯設計的,邏輯設計時的一些東西,
                        可能對物理訪問不起作用

                 經驗八、統計數據的更新:大約10天進行 update statistics     ,sp_recompile table_name(★★★)

                 經驗九、強制索引使用 (★★★★)
                       如果懷疑有表訪問時不是使用索引,而且這些條件字段上建立了合適的索引,可以強制使用
                         select * from tableA (index idx_name) where ...
                        這個對一些報表程序可能比較有用。

                 經驗十、找一個好的監視工具 ★★★
                       工欲善其事,比先利其器,一點都不錯呀。
                        我用 DBartisian 5.4 ,監視哪些表被鎖定時間長, blocking 等
                            還有 sp_object_status 20:00:00 , sp_sysmon 20:00:00 等
                 以上是我的一點經驗,在不到一個月的時間內,我修改了20個左右的語句和系統過程     ,

                   系統性能明顯改善,cpu利用 高峰時大約50% 平時 不到30%IO 明顯改善。所有月報表能順利完成 5min 以內。

              經驗十一: 綜合以上對IN/EXISTS的討論,我們可以得出一個基本通用的結論:IN適合于外表大而內表小的情況;EXISTS適合于外表小而內表大的情況。


                   另外,系統中確認不使用的中間數據,可以進行轉移。這些要看系統的情況哦
                    最后祝你好運氣。

          以上為個人經驗,歡迎批評指正!     

               呵呵 寫完后忘記一個     一定要注意熱點表 ,這是影響并發問題的一個潛在因素。!解決方法: 行鎖模式 如果表的行比較小,可以故意增加一些不用的字段
               比如     char(200)     讓一頁中存放的行不要太多。

          posted @ 2008-07-05 17:44 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(684) | 評論 (0)編輯 收藏

          維護目的:

          監測數據庫的當前運行狀況,保證數據庫穩定運行。

          做好數據庫的日常的備份工作,減輕問題發生時的風險和責任

          檢測數據庫的整體運行狀況,對數據庫的性能進行調整,保證數據庫高效的運行。

          可以減少緊急故障發生頻率,減少對系統的影響。

          盡早發現系統存在的潛在問題,使可能的故障消除在萌芽狀態。

          數據庫日常維護的主要內容
          監測數據庫運行情況

          監控CPU和I/O的使用情況

          監控空間的使用情況

          監控數據庫的錯誤日志errorlog

          制定一個合理的備份計劃

          檢查數據庫的一致性

          數據庫的排錯

          數據庫性能調整

          數據庫日常維護的方法

          數據庫服務和備份服務的啟動和關閉方法

          數據庫和備份服務的啟動

          $cd  $SYBASE/ASE-12_*/install

          $startserver –f RUN_Servername(Servername為你的數據庫服務名)

          $startserver –f RUN_Servername_back

          數據庫和備份服務的關閉

          isql –Usa –Pxxx –Sservername

          >shutdown SYB_BACKUP (關閉備份服務)

          >go

          >use dbname (用戶庫)

          >go

          >checkpoint

          >go

          >shutdown

          >go

          查看sybase用戶的運行環境是否正常

          $env

          查看SYBASE,SYBASE_ASE,SYBASE_OCS等環境是否正常

          查看數據庫服務和備份服務進程是否運行正常

          $ps –ef | grep  dataserver

          $ps –ef | grep  backupserver

          查看數據庫的版本和補丁信息

          $dataserver –v

          或是

          isql –Usa –Pxxx –Sservername

          >select  @@version

          檢查數據庫的配置是否合理.

          檢查數據庫內存分配、鎖個數、存儲過程緩沖、多CPU配置、用戶連接配置、網絡包尺寸等重要參數的設置。

          命令:

          sp_configure ‘allocate max shared memory’

          sp_configure ‘max memory‘

          sp_configure ‘procedure cache size‘

          sp_configure ‘user log cache size’

          sp_configure ‘default network packet size’

          sp_cacheconfig

          sp_configure ‘number of user connections‘

          sp_configure ‘number of locks‘

          sp_configure ‘number of engines at startup’

          sp_configure ‘max online engines’

          sp_configure “number of open index”

          sp_configure “number of open objects”

          sp_configure “number of open partitions”

          查看數據庫的用戶,數據庫的鎖狀況

          sp_who(用戶)

          sp_lock(鎖)

          select spid,blocked from sysprocesses where blocked>0(檢查阻塞進程)

          查看最早進程,發現不完整事務

          select *  from master..syslogshold

          其中對應的spid為最早的進程,如果最早的進程開始時間距離當前時間已經非常長(如2、3天),則可能該進程有問題,確認后,應該將其kill,下面有一個存儲過程來檢測不完整的事務,可以將該存儲過程放在操作系統的crontab中。

          create proc proc_kill
          @last_min int=720

          AS

          /*

          ** kill the processes that didn’t commit for a impossible time

          ** the parameter's time unit is minute,default is 12 hours

          ** the procedure is just for user transactions ,not  for xa transaction

          ** you can add this procedure to your opration system crontab

          */

          declare @spid     int

          declare @cspid     char(20)

          select spid into #killtab from master..syslogshold where datediff(mi,starttime,getdate()) >@last_min and spid>0

          declare t1_cur cursor for select * from #killtab

          open t1_cur

          fetch t1_cur into @spid

          while @@sqlstatus!=2

          begin

               select @cspid=convert(char(20),@spid)

               select "kill "+@cspid
          exec("kill "+@cspid)
               fetch t1_cur into @spid

          end

          查看CPU和I/O的使用

          #sar #iostat

          #vmstat

          查看Sybase的資源使用

          sp_sysmon “00:10:00”

          如果CPU或是I/O的使用率長期高于80%,則要看系統使用是否正常還是系統資源配置不夠,具體細節可參考下面數據庫的優化部分。

          定期檢查數據庫日志使用的情況,定期清除數據庫的日志

          如果數據庫日志滿,則業務無法進行。

          sp_helpdb dbname(用戶庫名)

          發現數據庫的日志空間不足,應立刻清除已經完成的事務,或是根據需要擴大日志空間。

          定期清除日志:

          dump tran dbname with  truncate_only

          可以將上述的命令放在crontab中。

          定期檢查磁盤空間的使用。 如果發現操作系統有磁盤分區使用達到100%,應盡快處理。

          #df -k

          定期檢查數據庫的錯誤日志(errorlog),如果錯誤日志中發現有數據庫的嚴重錯誤,應立即處理。

          有計劃的截斷數據庫的錯誤日志。

          數據庫運行一定時間后,可以在數據庫沒有業務時,(下班或周末),將數據庫stop后,將錯誤日志更名,然后重新啟動數據庫,產生一個新的數據庫錯誤日志。

          在錯誤日志中,以下的錯誤級別為不嚴重錯誤,通常是用戶錯誤,如語法,讀寫權限空間不足等。
          10 Status information

          11  Specified database object not found

          12 Wrong datatype encountered

          13 User transaction syntax error

          14 Insufficient permissions to execute commands

          15 Syntax error in SQL statement

          16 Miscellaneous user error

           Hardware/software errors

          17 Insufficient resources

          18 Non-fatal internal error

          在錯誤級別17中,要注意錯誤號1105,1105表示空間不足(數據庫數據或是日志)。

          在錯誤級別18中,最多的是1608,1608表示一個客戶連接不是正常方式退出的,

          這類錯誤不用關注。

          以下錯誤級別為嚴重錯誤,通常是系統(數據庫、磁盤損壞、操作系統)錯誤
          19 Fatal error in resource

          20 Fatal error in current process

          21 Fatal error in database process

          22 Fatal error: table integrity suspect

          23 Fatal error: database integrity suspect

          24 Hardware error or system table corruption

          在錯誤日志中,可以按照"Sybase Technical Support"來搜索,

          找到的錯誤通常是嚴重錯誤。

          檢查數據庫的一致性
          檢測系統數據庫、用戶數據庫的數據分配頁物理可用性,檢查數據庫系統表和用戶表的數據完整性,查看是否有表損壞(可能是邏輯也可能是物理的損壞,如磁盤錯誤可能導致數據庫的表損壞)

          做數據庫的一致性通常需要單用戶狀態,并且都是一些耗時操作(如果數據庫大的話),因此,這些檢查可以考慮在系統檢修時運行。

          dbcc checkalloc(dbname)檢查數據庫的空間分配,必須在單用戶下執行

          checkcatalog(dbname) 檢查系統表的一致性

          dbcc checkdb(dbname) 檢查用戶數據庫的數據庫一致性(包括系統表和用戶表)

          dbcc checktable(tablename) 檢查一張表的數據一致性。

          數據庫的備份
          數據庫的備份對于日常的維護來說十分重要,系統管理員一定要注意數據庫每天都有成功備份。需要檢查備份的介質(磁盤或是磁帶)是否正常。

          備份命令:

          dump database  to ‘/xx/xxx’ (設備名或是磁盤上的文件名)

          用戶可以規劃一個備份的計劃,然后將備份的命令放在crontab 中,讓系統自動定時做數據庫的備份。

          數據庫的排錯
          數據庫通常的錯誤主要是以下幾種

          數據庫服務無法啟動

          解決:查看數據庫的錯誤日志,根據錯誤日志找到無法啟動的原因,原因通常是ip地址,端口不對或是被占用,內存配置過大,或是數據庫設備的屬性不對,sybase用戶沒有訪問權限。

          數據庫不能恢復(recovery)

          解決:查看數據庫的錯誤日志,找到數據庫不能恢復的原因,然后做相應的處理。通常是由于系統突然斷電或是系統非正常關機。

          用戶表不能訪問

          解決:查看數據庫的錯誤日志,找到不能訪問的原因。通常是由于訪問權限或是表損壞,

          最常見的數據庫重大故障分析
          根據我們的經驗,除去硬件故障外,造成重大數據庫故障都是因為日志滿,重啟動時異常刪除日志導致,

          而日志滿主要是一下幾個原因造成:

          1)數據庫配置不合理

          主要是內存、鎖的配置不合理。

          2)存在不完整的事務或是進程

          出現這種情況是由于不完整的事務引起的。引起不完整的事務主要有兩個方面的原因:第一是網絡質量不佳,如果在客戶端向ASE服務端進行事務時,如果網絡突然中斷,會導致事務的不完整。第二是應用程序存在問題,而在這種情況下,重新啟動數據庫服務后,數據庫的恢復可能是非常緩慢的,主要是看日志的大小和事務的類型,(有時用戶為了快速啟動,通常會異常清除數據庫的日志,就有可能會造成數據庫表損壞)所以,在這種情況下千萬不要急于重新啟動數據庫服務。

          如果存在不完整的事務,在該事務之后的所有事務都不能被清除,導致數據庫日志滿。

          不完整的事務可以從master..syslogshold表中發現,如果其中starttime(最早的事務開始時間)距離當前時間很長,比如一天,一月,則該事務應該是一個不完整的事務,可以將該事務對應的數據庫進程kill。

          3)出現過大的事務
          這類問題完全是應用或是人為造成的問題。例如一次刪除一個5000萬條記錄的表,導致日志滿。這種情況下重啟動服務,數據庫的恢復也將是十分緩慢的,防止出現這類問題,是將大事務轉換成許多小事務來執行。在事務之間來刪除數據庫的日志。例如手工刪除日志或是將數據庫設置成自動清日志。

          數據庫的性能優化
          查看數據庫的配置,看能否有不合理的數據庫配置。

          查看方法參考上一節查看數據庫的配置。

          更新數據庫的統計信息(頁鎖)

          update statistics tablename

          對行鎖表回收空間(表鎖)

          reorg rebuild  tablename

          做空間回收時需要在系統維護時做

          監測數據庫的運行,查看是否阻塞縮

          sp_lock

          sp_who

          select spid,bloced from master..sysprocesses where blocked>0

          對數據庫系統運行進行監測,發現可能引起性能差的因素

          在系統運行忙或是性能不佳時運行

          sp_sysmon “00:10:00”


          分析的一些常用工具:

          在應用開發或是執行之前,對可能引起問題的語句檢查命令:

          set showplan on
          set noexec on

          這樣,可以看到該語句的執行過程,而該語句并不執行。


          set showplan off

          set noexec off

          off 以上的選項

          set statistics io on /off
          set statistics time on/off

          在語句執行時可以看到I/O的實際情況(包括物理I/O,邏輯I/O)

          以上語句基于session

          在運行過程中查找可能有問題的語句的命令:

          運行幾次 sp_lock
          查找其中對表意向鎖 (sh_intent,ex_intent)較長時間的進程,記錄下spid

          ( 比如,update 一張100萬條記錄中的一條,如果表不使用索引,最終會有一個Ex_row鎖,但在表掃描期間,一直會有一個

          Ex_intent意向鎖)

          通過spid,可以看到執行命令和執行過程:

          dbcc traceon(3604)

          go

          dbcc sqltext(spid)

          go

          sp_showplan spid,null,null,null

          go

          查找阻塞進程:
          select spid,blocked from master..sysprocesses where blocked>0

          其中blocked 對應阻塞別人的進行,spid對應被阻塞經常,

          查看blocked對應進程執行的命令和執行過程

          方法同上

          查找最耗資源的進程:下面有兩個存儲過程,可以分析在數據庫繁忙時最消耗cpu和i/o資源的數據庫進程,并顯示該進程所執行的語句以及執行的過程,根據執行過程來判斷問題的原因。

          if exists( select name from  sysobjects where  type='P' and name='proc_who_do_io')
           drop proc  proc_who_do_io

          go

          /* print  top n (of  physical_io usages)  applications 's  execute plan and sql */
          /* example useage : proc_who_do_io  */ 

          create proc  proc_who_do_io

          @inter_time    char(8)='00:00:05',

          @topn  int=3

          as

          declare  @spid  int

          select @spid=11

          dbcc traceon(3604)

          select  spid,cmd,physical_io,hostname,program_name into #t1 from  master..sysprocesses order

          by spid

          waitfor delay @inter_time

          select spid,cmd,physical_io ,hostname,program_name into #t2 from  master..sysprocesses order

          by spid

          select  #t1.spid,#t1.cmd,#t1.program_name,#t1.physical_io as phy_io,#t1.hostname,#t2.physical_io - #t1.physical_io as
          phy_io_add  into #t3 from #t1,#t2

          where  #t1.spid=#t2.spid  and abs(#t2.physical_io - #t1.physical_io) >2 order by  #t2.physical_io - #t1.physical_io  desc

          select *  from #t3

          select  *  into #t4  from #t3 where 1=2

          set rowcount 1

          while  @topn >0

          begin

           insert  #t4 select * from #t3

           delete #t3

           select @spid=spid from  #t4

           select "execute plan :"+  str(@spid)

           exec sp_showplan @spid,null,null,null

           dbcc sqltext(@spid)

           delete #t4

           select @topn = @topn-1

          end

          set rowcount 0

          go

           

          if exists( select name from  sysobjects where  type='P' and name='proc_who_use_cpu')
           drop proc  proc_who_use_cpu

          go

          /* print  top n (of  cpu usages)  applications 's  execute plan and sql */
          /* example useage : proc_who_use_cpu  */ 

          create proc  proc_who_use_cpu

          @inter_time    char(8)='00:00:05',

          @topn  int=3

          as

          declare  @spid  int

          select @spid=11

          dbcc traceon(3604)

          select  spid,cmd,cpu,hostname,program_name into #t1 from  master..sysprocesses order

          by spid

          waitfor delay @inter_time

          select spid,cmd,cpu ,hostname,program_name into #t2 from  master..sysprocesses order

          by spid

          select  #t1.spid,#t1.cmd,#t1.program_name,#t1.cpu ,#t1.hostname,#t2.cpu - #t1.cpu as
          cpu_add  into #t3 from #t1,#t2

          where  #t1.spid=#t2.spid  and abs(#t2.cpu - #t1.cpu) >2 order by  #t2.cpu - #t1.cpu  desc

          select *  from #t3

          select  *  into #t4  from #t3 where 1=2

          set rowcount 1

          while  @topn >0

          begin

           insert  #t4 select * from #t3

           delete #t3

           select @spid=spid from  #t4

           select "execute plan :"+  str(@spid)

           exec sp_showplan @spid,null,null,null

           dbcc sqltext(@spid)

           delete #t4

           select @topn = @topn-1

          end

          set rowcount 0

          go

          數據庫版本在 ASE12.5.0.3以上, 尋找索引使用情況
          select s.SPID,s.CpuTime,t.LineNumber,t.SQLText

          from monProcessStatement s,monProcessSQLText t

          where s.SPID=t.SPID

          order by s.CpuTime,s.SPID,t.LineNumber desc

          posted @ 2008-07-05 17:19 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(5160) | 評論 (0)編輯 收藏

          print         'sp_hotobjects'  
            SETUSER     'dbo'  
            go  
            use   sybsystemprocs  
            go  
             
            drop   proc   sp_hotobjects  
            go    
             
            create   procedure   sp_hotobjects  
            @interval   char(12)   =   "",             /*   time   interval   string   */  
            @interval_sample   char(12)   =   "00:05:00"               /*   sample   interval   every   5   minutes   by   default   */  
            as  
             
            declare   @TmpTime   datetime       /*   temporary   datetime   variable   */  
            declare   @Seconds   int                 /*   Interval   converted   to   seconds   */  
            declare   @Endtime   datetime  
             
             
            create   table   #hotobjects_totals  
            (dbname   char(30)   not   null,  
            objname   char(30)   not   null,  
            lockcount   int   null,  
            locktable   int   null,      
            lockshared   int   null,  
            lockexclusive   int   null,  
            lockrow   int   null,  
            lockpage   int   null,  
            lockblk   int   null  
            )  
             
            create   table   #hotobjects  
            (dbname   char(30)   not   null,  
            objname   char(30)   not   null,  
            lockcount   int   null,  
            locktype   int   not   null,  
            primary   key(dbname,   objname,   locktype))  
             
             
            /*   loop   for   the   interval   specified   */  
            select   @TmpTime   =   convert(datetime,   @interval)  
            select   @Seconds   =   datepart(hour,@TmpTime)*3600+datepart(minute,@TmpTime)*60+datepart(second,@TmpTime)  
            select   @Endtime   =   dateadd(second,   @Seconds,   getdate())  
             
            /*   create   a   holding   table   */  
            select   dbname,   objname   into   #hotobjects_holding    
            from   #hotobjects   where   1=2  
             
             
             
             
            while   (getdate()   <   @Endtime)  
            begin  
             
            /*   populate   the   initial   records   */  
            delete   from   #hotobjects  
            insert   into   #hotobjects(dbname,   objname,   lockcount,   locktype)  
            select   distinct   db_name(dbid),   object_name(id,dbid),   count(type),   type   from   master..syslocks    
            where   object_name(id,dbid)   not   like   "#%"       --   and   object_name(id,dbid)   not   like   "sys%"    
            and   object_name(id,dbid)   not   like   "hot%"     group   by   type    
             
            waitfor   delay   @interval_sample  
             
            /*   add   a   record   into   #hotobjects_totals   if   it   does   not   exist   */  
            if   not   exists(select   1   from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname)    
            /*   add   this   new   lock   record   */  
            begin  
             
                  /*   populate   a   holding   table   */  
            delete   from   #hotobjects_holding  
            insert   into   #hotobjects_holding  
            select   distinct   dbname,   objname   from   #hotobjects  
             
                  /*   now   delete   from   the   holding   table   all   records   we   have   done   before   */  
            delete   from   #hotobjects_holding  
            from   #hotobjects_holding   HOLD,   #hotobjects_totals   TOT  
            where   HOLD.dbname   =   TOT.dbname   and   HOLD.objname   =   TOT.objname  
            /*   what   is   left   is   the   new   records.....add   these   into   the   totals   table   */  
             
            insert   into   #hotobjects_totals(dbname,   objname,     lockcount,   locktable,   lockshared,      
                                                                                                                                                            lockexclusive,   lockrow,   lockpage,   lockblk)  
                                    select   distinct   HOLD.dbname,   HOLD.objname,   0,   0,0,0,0,0,0    
            from   #hotobjects_holding   HOLD  
             
            end  
             
             
            /*   from   here   on   we   will   update   this   record     */  
             
            update   #hotobjects_totals  
            set   lockcount   =   TOT.lockcount   +   HOT.lockcount  
            from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname  
             
            update   #hotobjects_totals  
            set   locktable   =   locktable   +   HOT.lockcount  
            from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname  
            and   HOT.locktype   in   (1,2)  
             
            update   #hotobjects_totals  
            set   lockshared   =   lockshared   +   HOT.lockcount  
            from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname  
            and   HOT.locktype   in   (2,6,9)  
             
            update   #hotobjects_totals  
            set   lockexclusive   =   lockexclusive   +   HOT.lockcount  
            from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname  
            and   HOT.locktype   in   (1,5,8)  
             
            update   #hotobjects_totals  
            set   lockrow   =   lockrow   +   HOT.lockcount  
            from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname  
            and   HOT.locktype   in   (8,9,10)  
             
            update   #hotobjects_totals  
            set   lockpage   =   lockpage   +   HOT.lockcount  
            from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname  
            and   HOT.locktype   in   (5,6,7)  
             
            update   #hotobjects_totals  
            set   lockblk   =   lockblk   +   HOT.lockcount  
            from   #hotobjects_totals   TOT,   #hotobjects   HOT  
            where   TOT.dbname   =   HOT.dbname   and   TOT.objname   =   HOT.objname  
            and   HOT.locktype   >   255   and   HOT.locktype   <   269  
             
             
            end  
             
            select   "In   "   +   rtrim(dbname)   +   "the   table   "   +   rtrim(objname)   +   "   had   "   +    
            case    
            when   locktable   >   1   then   "table   level,   "  
            when   lockshared   >   1   then   "shared,   "    
            when   lockexclusive   >   1   then   "exclusive,   "  
            when   lockrow   >   1   then   "row,   "  
            when   lockpage   >   1   then   "page,   "  
            when   lockblk   >   1   then   "blocking   "  
            end  
            +   "   locks"  
              from   #hotobjects_totals    
            return   (0)  
            go  
             
            grant   exec   on   sp_hotobjects   to   public  
            go   
              
             
          posted @ 2008-07-05 16:29 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(457) | 評論 (0)編輯 收藏

          死鎖的發生對系統的性能和吞吐量都有重要影響,經檢測發現,管理信息系統的死鎖主要是因為兩個或多個線程(登錄)搶占同一表數據資源。引起長時間搶占同一資源不是因為我們需要處理的事務太復雜,時間太長,而往往是因為我們在前端應用程序對數據庫作操作時忘了提交。本文介紹一種處理解決這種死鎖的方法。

          Sybase封鎖原理

          數據共享與數據一致性是一對不可調和的矛盾,為了達到數據共享與數據一致,必須進行并發控制。并發控制的任務就是為了避免共享沖突而引起的數據不一致。Sybase SQL Server并發控制的方法是加鎖機制(LOCKING).

          鎖的類型

          可申請的鎖
          已有的鎖
          S U X
          S ×
          U × ×
          X × × ×

          Sybase SQL Server有三種封鎖類型:排它鎖(exclusive lock,簡稱X鎖);共享鎖(share lock,簡稱S鎖);更新鎖(update lock,簡稱U鎖)。這三種鎖的相容矩陣表如下:

          ×:表示不兼容。∨:表示兼容。

          Sybase SQL Server是自動決定加鎖類型的。一般來說,讀(SELECT)操作使用S鎖,寫(UPDATE,INSERT和delete)操作使用X鎖。U鎖是建立在頁級上的,它在一個更新操作開始時獲得,當要修改這些頁時,U鎖會升級為X鎖。

          鎖的力度

          SQL Server有兩級鎖:頁鎖和表鎖。通常頁鎖比表鎖的限制更少(或更小)。頁鎖對本頁的所有行進行鎖定,而表鎖則鎖定整個表。為了減小用戶間的數據爭用和改進并發性,SQL Server試圖盡可能地使用頁鎖。

          當SQL Server決定一個語句將訪問整個表或表的大多數頁時,它用表鎖來提供更有效的鎖定。鎖定策略直接受查詢方案約束,如果update或delete語句沒有可用的索引,它就執行表掃描或請求一個表鎖定。如果update或delete語句使用了索引,它就通過請求頁鎖來開始,如果影響到大多數行,它就要請求表鎖。一旦一個語句積累的頁鎖超過鎖提升閾值,SQL Server就設法給該對象分配一個表鎖。如果成功了,頁鎖就不再必要了,因此被釋放。表鎖也在頁層提供避免鎖沖突的方法。對于有些命令SQL Server自動使用表鎖。

          鎖的狀態

          SQL SERVER加鎖有三種狀態:

          1)意向鎖(intend)—是一種表級鎖,它表示在一個數據頁上獲得一個S或X鎖的意向。意向鎖可以防止其他事務在該數據頁的表上獲得排它鎖。

          2)阻塞(blocking,簡記blk)—它表明目前加鎖進程的狀態,帶有blk后綴的鎖說明該進程目前正阻塞另一個需要獲得鎖的進程,只有這一進程完成,其他進程才可以進行。

          3)需求鎖(demand)—表示此時該進程企圖得到一個排它鎖。它可以防止在這一表或頁上加過多的S鎖,她表示某一事務是下一個去鎖定該表和該頁的事務。

          需求鎖是一個內部過程,因此用sp_lock是無法看見的。

          死鎖DEADLOCK

          簡單地說,有兩個用戶進程,每個進程都在一個單獨的頁或表上有一個鎖,而且每個進程都想在對方進程的頁或表上請求不相容鎖時就會發生“死鎖”。在這種情況下,第一個進程在等待另一進程釋放鎖,但另一進程要等到第一個進程的對象釋放時才會釋放自己的鎖。

          SQL Server檢查是否死鎖,并終止事務中CPU時間積累最小的用戶(即最后進入的用戶)。SQL Server回滾該用戶的事務,并用消息號1205通知有此死鎖行為的應用程序,然后允許其他用戶進程繼續進行。

          在多用戶情形下,每個用戶的應用程序都應檢查每個修改數據的事務是否有1205號消息,以此確定是否有可能死鎖。消息號1025表示該用戶的事務因死鎖而終止并被回滾。應用程序必須重新開始這個事務處理。

          查找死鎖原因

          既然管理信息系統長時間死鎖的原因是由于我們提交或者是提交不當,那么我們就可以通過修改程序防止出現死鎖。定位死鎖出錯處主要經過以下三步:

          1)在死鎖出現時,用SP_WHO,SP_LOCK獲得進程與鎖的活動情況。
          2)結合庫表sysobjects和相應的操作員信息表查出被鎖的庫表與鎖住別人的操作員。
          3)根據鎖定的庫表與操作員的崗位,可以估計出程序大約出錯處。詢問操作員在死鎖時執行的具體操作即可完全定位出錯處。最后查找程序并修改之。

          用sp_who獲取關于被阻礙進程的信息

          系統過程sp_who給出系統進程的報告。如果用戶的命令正被另一進程保持的鎖阻礙,則:
          ◆status列顯示“lock sleep”。
          ◆blk列顯示保持該鎖或這些鎖的進程標識,即被誰鎖定了。
          ◆loginame列顯示登錄操作員。結合相應的操作員信息表,便可知道操作員是誰。
          Fid spid status loginame origname blk dbname cmd
          0 1 lock sleep lm lm 18 QJYD SELECT
          0 2 sleeping NULL NULL 0 master NETWORK HANDLER
          0 3 sleeping NULL NULL 0 master NETWORK HANDLER
          ……

          用sp_lock瀏覽鎖
          要得到關于當前SQL Server上保持的鎖的報告,可用系統過程sp_lock [spid1[,spid2]],spid1,spid2是表master.dbo.sysprocesses中的sql server進程id號,用sp_who可以得到鎖定與被鎖定的spid號:

          ◆locktype列顯示加鎖的類型和封鎖的粒度,有些鎖的后綴還帶有blk表明鎖的狀態。前綴表明鎖的類型:Sh—共享鎖,Ex—排它鎖或更新鎖,中間表明鎖死在表上(”table”或’intent’)還是在頁上(page). 后綴“blk”表明該進程正在障礙另一個需要請求鎖的進程。一旦正在障礙的進程一結束,其他進程就向前移動。“demand”后綴表明當前共享鎖一釋放, 該進程就申請互斥鎖。

          ◆table_id列顯示表的id號,結合sysobjects即可查出被封鎖的表名。

          執行該進程后屏幕顯示

          Fid Spid locktype table_id page row dbname Class context
          0 1 Sh_intent 678293476 0 0 QJYD Non Cursor LockFam dur
          0 1 Sh_page 678293476 31764 0 QJYD Non Cursor Lock
          0 18 Ex_intent 9767092 0 0 QJYD Non Cursor LockFam dur
          ……

          定位出錯處

          根據sp_who與sp_lock命令的結果,結合sysobjects和相應的操作員信息表。得到操作員及其在死鎖時所操作的庫表,便大約可以知道應用程序的出錯處,再詢問操作員在死鎖時執行什么操作以進一步認證。最后查找程序并修正之。


          附:
          鎖的問題一般來講是這樣的  
             
            首先判斷是否存在鎖競爭  
             
            1、基于系統管理員的經驗找出熱點表  
             
            2、通過以下方法判斷:  
             
            sp_who  
            察看系統中是否有status為lock   sleep的用戶。如果存在,再運行命令  
            sp_lock  
            察看系統中被鎖住的表的table_id  
            繼續運行命令  
            select   *   from   sysobjects   where   id   =   table_id  
            找到被鎖住的表名。  
            如果發現sp_who中有多個用戶被鎖,那么,就可認為是鎖造成了數據庫系統的性能底下。  
             
            調優方法1:修改對應表的加鎖模式由allpages為行級鎖,如:  
            alter   table   XXXX   lock   datarows  
             
            調優方法2:改大環境參數number   of   locks,如:  
            sp_configure   “number   of   locks”,   50000  
            注意,在這種情況下需要保持total   memory與number   of   locks的匹配性。  
             
            調優方法3:配置鎖提升閾值,可修改的配置包括兩項內容:  
            Page(HWM)、Row(HWM),命令如下:  
             
            Sp_configure   ‘page   lock   promotion   HWM’,   500  
            Sp_configure   ‘row   lock   promotion   HWM’,   500  

          posted @ 2008-07-02 20:58 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(491) | 評論 (0)編輯 收藏

          sp_who報告的狀態值

          狀態          表示       kill命令的效果

          recv sleep  等待網絡讀取  立即
          send sleep  等待網絡發送  立即
          alarm sleep 等待警報      立即
          lock sleep  等待獲取鎖    立即
          sync sleep  等待同系列另一進程的同步消息   立即.系列中的其它進程也必須變為可注銷的狀態
          sleeping    等待磁盤I/O或某種其它資源.或許表示正在運行的進程,但正在執行大量的磁盤I/O  少數休眠進程不能蘇醒,因而需要服務器重啟以將其清除.
          runnable    在可運行進程隊列中       立即
          running     活躍地運行在一個服務器引擎中  立即
          infected    服務器已檢測到嚴重的錯誤情況;極其少見    不要使用kill命令.可能需要重啟服務器以清除進程.
          background  由Adaptive Server而不是用戶進程運行的進程例如閾值過程   立即;使用kill時必須極其小心.建議在注銷背景進程之前,仔細檢查sysprocesses
          log suspend   到達日志的最后機會閾值時掛起的進程   立即

          參考<sybase數據庫系統管理指南>


           ---★ 本文轉摘自『onlyhot blog ※ blog.onlyhot.cn』http://www.onlyhot.cn/blog/?action=show&id=1178

          posted @ 2008-07-02 11:48 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(2628) | 評論 (0)編輯 收藏

          用一個實例講解了Sybase數據庫性能優化的具體過程,具體內容請參考下文:

          共享鎖

          sp_getapplock 鎖定應用程序資源

          sp_releaseapplock 為應用程序資源解鎖

          SET LOCK_TIMEOUT 1800 鎖超時期限設置

          sp_configure 'deadlock checking period',5000 設置鎖檢測周期

          sp_configure 'lock wait period',5000 設置鎖的等待時間

          sp_setrowlockpromote 設置基本個表的最大行鎖升級數(鎖數)

          sp_setrowlockpromote 'TABLE',TREECODE,500,500,100

          sp_setrowlockpromote 'TABLE',LCD05,500,500,100

          [Lock Manager]

          number of locks = 50000 #鎖數

          deadlock checking period = DEFAULT

          freelock transfer block size = DEFAULT

          max engine freelocks = DEFAULT

          lock spinlock ratio = DEFAULT

          lock hashtable size = DEFAULT

          lock scheme = DEFAULT

          lock wait period = DEFAULT

          read committed with lock = DEFAULT

          當很多事務同時訪問同一個數據庫時,會加劇鎖資源爭奪,嚴重時事務之間會發生死鎖。可用sp_object_stats查明死鎖位置。該過程報告資源爭奪最激烈的10張表、一個數據庫中資源爭奪的表和單個表的爭奪情況。語法為sp_object_stats interval [, top_n [, dbname [, objname [, rpt_option ]]]],查看鎖爭奪情況只需設置interval為“hh:mm:ss”。如果顯示每種鎖的爭奪程度超過15%,應該改變加鎖方式,比如表的全頁鎖改成數據頁鎖,數據頁鎖改成數據行鎖等。

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          allow remote access 1 0 1 1

          print recovery information 0 0 0 0

          recovery interval in minutes 5 0 5 5

          tape retention in days 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          global async prefetch limit 10 0 10 10

          global cache partition number 1 0 1 1

          memory alignment boundary 2048 0 2048 2048

          number of index trips 0 0 0 0

          number of oam trips 0 0 0 0

          procedure cache percent 20 22426 20 20

          total data cache size 0 89698 0 89698

          total memory 47104 196608 98304 98304

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          cis bulk insert batch size 0 0 0 0

          cis connect timeout 0 0 0 0

          cis cursor rows 50 0 50 50

          cis packet size 512 0 512 512

          cis rpc handling 0 0 0 0

          enable cis 1 0 1 1

          max cis remote connections 0 0 0 0

          max cis remote servers 25 19 25 25

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          dtm detach timeout period 0 0 0 0

          dtm lock timeout period 300 0 300 300

          enable xact coordination 1 0 1 1

          number of dtx participants 500 149 500 500

          strict dtm enforcement 0 0 0 0

          txn to pss ratio 16 3692 16 16

          xact coordination interval 60 0 60 60

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          average cap size 200 0 200 200

          caps per ccb 50 0 50 50

          dump on conditions 0 0 0 0

          maximum dump conditions 10 0 10 10

          number of ccbs 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          allow sql server async i/o 1 0 1 1

          disable disk mirroring 0 0 0 0

          disk i/o structures 256 31 256 256

          number of devices 10 #5 10 10

          page utilization percent 95 0 95 95

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          event log computer name LocalSystem 0 LocalSystem LocalSystem

          event logging 1 0 1 1

          log audit logon failure 0 0 0 0

          log audit logon success 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          esp execution priority 8 0 8 8

          esp execution stacksize 77824 0 77824 77824

          esp unload dll 0 0 0 0

          start mail session 0 0 0 0

          xp_cmdshell context 1 0 1 1

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          configuration file 0 0 0 /sybase/hgd

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          enable java 0 0 0 0

          size of global fixed heap 300 0 300 300

          size of process object heap 300 0 300 300

          size of shared class heap 3072 0 3072 3072

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          default character set id 1 0 1 1

          default language id 0 0 0 0

          default sortorder id 50 0 50 50

          disable character set conversi 0 0 0 0

          enable unicode conversions 0 0 1 1

          number of languages in cache 3 4 3 3

          size of unilib cache 0 140 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          deadlock checking period 500 0 500 500

          freelock transfer block size 30 0 30 30

          lock address spinlock ratio 100 0 100 100

          lock hashtable size 2048 48 2048 2048

          lock scheme allpages 0 allpages allpages

          lock spinlock ratio 85 0 85 85

          lock table spinlock ratio 20 0 20 20

          lock wait period 2147483647 0 2147483647 2147483647

          max engine freelocks 10 0 10 10

          number of locks 5000 2344 10000 10000

          print deadlock information 0 0 1 1

          read committed with lock 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          additional network memory 0 0 0 0

          allow resource limits 0 0 0 0

          audit queue size 100 42 100 100

          average cap size 200 0 200 200

          caps per ccb 50 0 50 50

          deadlock pipe max messages 0 0 0 0

          default network packet size 512 #505 512 512

          disk i/o structures 256 31 256 256

          enable rep agent threads 0 0 0 0

          errorlog pipe max messages 0 0 0 0

          event buffers per engine 100 #11 100 100

          executable codesize + overhead 0 20261 0 20261

          lock hashtable size 2048 48 2048 2048

          lock spinlock ratio 85 0 85 85

          max cis remote servers 25 19 25 25

          max number network listeners 5 868 5 5

          max online engines 1 216 1 1

          max roles enabled per user 20 #22 20 20

          memory per worker process 1024 0 1024 1024

          number of alarms 40 3 40 40

          number of aux scan descriptors 200 #258 200 200

          number of ccbs 0 0 0 0

          number of devices 10 #5 10 10

          number of languages in cache 3 4 3 3

          number of large i/o buffers 6 97 6 6

          number of locks 5000 2344 10000 10000

          number of mailboxes 30 1 30 30

          number of messages 64 3 64 64

          number of open databases 12 1239 12 12

          number of open indexes 500 512 500 500

          number of open objects 500 561 500 500

          number of remote connections 20 86 50 50

          number of remote logins 20 23 20 20

          number of remote sites 10 1729 10 10

          number of user connections 25 43141 250 250

          number of worker processes 0 0 0 0

          partition groups 1024 904 1024 1024

          permission cache entries 15 #227 15 15

          plan text pipe max messages 0 0 0 0

          procedure cache percent 20 22426 20 20

          process wait events 0 0 0 0

          remote server pre-read packets 3 #83 3 3

          size of global fixed heap 300 0 300 300

          size of process object heap 300 0 300 300

          size of shared class heap 3072 0 3072 3072

          size of unilib cache 0 140 0 0

          sql text pipe max messages 0 0 0 0

          stack guard size 4096 #1108 4096 4096

          stack size 86016 #23269 86016 86016

          statement pipe max messages 0 0 0 0

          total data cache size 0 89698 0 89698

          total memory 47104 196608 98304 98304

          txn to pss ratio 16 3692 16 16

          wait event timing 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          number of open databases 12 1239 12 12

          number of open indexes 500 512 500 500

          number of open objects 500 561 500 500

          open index hash spinlock ratio 100 0 100 100

          open index spinlock ratio 100 0 100 100

          open object spinlock ratio 100 0 100 100

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          Q diagnostics active 0 0 0 0

          SQL batch capture 0 0 0 0

          deadlock pipe active 0 0 0 0

          deadlock pipe max messages 0 0 0 0

          errorlog pipe active 0 0 0 0

          errorlog pipe max messages 0 0 0 0

          object lockwait timing 0 0 0 0

          per object statistics active 0 0 0 0

          plan text pipe active 0 0 0 0

          plan text pipe max messages 0 0 0 0

          process wait events 0 0 0 0

          sql text pipe active 0 0 0 0

          sql text pipe max messages 0 0 0 0

          statement pipe active 0 0 0 0

          statement pipe max messages 0 0 0 0

          statement statistics active 0 0 0 0

          wait event timing 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          additional network memory 0 0 0 0

          allow remote access 1 0 1 1

          allow sendmsg 0 0 0 0

          default network packet size 512 #505 512 512

          max network packet size 512 0 512 512

          max number network listeners 5 868 5 5

          number of remote connections 20 86 50 50

          number of remote logins 20 23 20 20

          number of remote sites 10 1729 10 10

          remote server pre-read packets 3 #83 3 3

          syb_sendmsg port number 0 0 0 0

          tcp no delay 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          max async i/os per engine 2147483647 0 2147483647 2147483647

          max async i/os per server 2147483647 0 2147483647 2147483647

          o/s file descriptors 0 0 0 1024

          tcp no delay 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          max parallel degree 1 0 1 1

          max scan parallel degree 1 0 1 1

          memory per worker process 1024 0 1024 1024

          number of worker processes 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          additional network memory 0 0 0 0

          lock shared memory 0 0 0 0

          max SQL text monitored 0 7 0 0

          shared memory starting address 0 0 0 0

          total memory 47104 196608 98304 98304

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          max online engines 1 216 1 1

          min online engines 1 0 1 1

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          enable rep agent threads 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          SQL Perfmon Integration 1 0 1 1

          abstract plan cache 0 0 0 0

          abstract plan dump 0 0 0 0

          abstract plan load 0 0 0 0

          abstract plan replace 0 0 0 0

          allow backward scans 1 0 1 1

          allow nested triggers 1 0 1 1

          allow resource limits 0 0 0 0

          allow updates to system tables 0 0 1 1

          audit queue size 100 42 100 100

          cpu accounting flush interval 200 0 200 200

          cpu grace time 500 0 500 500

          deadlock retries 5 0 5 5

          default database size 2 0 2 2

          default exp_row_size percent 5 0 5 5

          default fill factor percent 0 0 0 0

          enable DTM 0 0 0 0

          enable HA 0 0 0 0

          enable housekeeper GC 1 0 1 1

          enable sort-merge join and JTC 0 0 0 0

          event buffers per engine 100 #11 100 100

          housekeeper free write percent 1 0 1 1

          i/o accounting flush interval 1000 0 1000 1000

          i/o polling process count 10 0 10 10

          identity burning set factor 5000 0 5000 5000

          identity grab size 1 0 1 1

          license information 25 0 25 25

          number of alarms 40 3 40 40

          number of aux scan descriptors 200 #258 200 200

          number of large i/o buffers 6 97 6 6

          number of mailboxes 30 1 30 30

          number of messages 64 3 64 64

          number of open databases 12 1239 12 12

          number of open indexes 500 512 500 500

          number of open objects 500 561 500 500

          number of pre-allocated extent 2 0 2 2

          number of sort buffers 500 0 500 500

          page lock promotion HWM 200 0 200 200

          page lock promotion LWM 200 0 200 200

          page lock promotion PCT 100 0 100 100

          partition groups 1024 904 1024 1024

          partition spinlock ratio 10 0 10 10

          print deadlock information 0 0 1 1

          row lock promotion HWM 200 0 200 200

          row lock promotion LWM 200 0 200 200

          row lock promotion PCT 100 0 100 100

          runnable process search count 2000 0 2000 2000

          size of auto identity column 10 0 10 10

          sql server clock tick length 100000 0 100000 100000

          text prefetch size 16 0 16 16

          time slice 100 0 100 100

          upgrade version 1100 0 12000 12000

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          allow procedure grouping 1 0 1 1

          auditing 0 0 0 0

          check password for digit 0 0 0 0

          curread change w/ open cursors 1 0 1 1

          current audit table 1 0 1 1

          max roles enabled per user 20 #22 20 20

          maximum failed logins 0 0 0 0

          minimum password length 6 0 6 6

          msg confidentiality reqd 0 0 0 0

          msg integrity reqd 0 0 0 0

          secure default login guest 0 guest guest

          select on syscomments.text 1 0 1 1

          suspend audit when device full 1 0 1 1

          unified login required 0 0 0 0

          use security services 0 0 0 0

          Parameter Name Default Memory Used Config Value Run Value

          -------------- ------- ----------- ------------ ---------

          default network packet size 512 #505 512 512

          number of pre-allocated extent 2 0 2 2

          number of user connections 25 43141 250 250

          permission cache entries 15 #227 15 15

          stack guard size 4096 #1108 4096 4096

          stack size 86016 #23269 86016 86016

          systemwide password expiration 0 0 0 0

          user log cache size 2048 0 2048 2048

          user log cache spinlock ratio 20 0 20 20

          posted @ 2008-07-01 16:48 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(786) | 評論 (0)編輯 收藏

          注:在下面做的介紹都是以Weblogic8.1為例的,其它版本的Weblogic可能會有些許不同。
          1) 設置JAVA參數;
          a) 編輯Weblogic Server啟動腳本文件;
          l   BEA_HOME\user_projects\domains\domain-name\startWebLogic.cmd(startWebLogic.sh on Unix)
          l BEA_HOME\user_projects\domains\domain-name\startManagedWebLogic.cmd(startManagedWebLogic.sh on Unix)
          b) 編輯set JAVA_OPTIONS命令,如:set JAVA_OPTIONS=-Xms256m –Xmx256m;
          c) 保存,重啟即可。
          注:在WebLogic中,為了獲得更好的性能,BEA公司推薦最小Java堆等于最大Java堆。
          2) 開發模式 vs. 產品模式;
          開發模式和產品模式的一些參數的默認值不同,可能會對性能造成影響,下面是對性能有影響的參數列表:
          參數         開發模式默認值         產品模式默認值
          Execute Queue: Thread Count         15 threads         25 threads
          JDBC Connection Pool: MaxCapacity         15 connnections         25 connections
          通過啟動管理控制臺,在域(如:mydomain)> 配置 > 常規選擇產品模式。
          3) 盡量開啟本地I/O;
          通過啟動管理控制臺,在域(如:mydomain)> 服務器 > server實例(如:myserver)> 配置 > 調整選擇啟用本地I/O。
          注:此值也可通過手動的修改config.xml配置文件。
          4) 調優執行隊列線程;
          a) 修改默認執行線程數
          在這里,執行隊列的線程數表示執行隊列能夠同時執行的操作的數量。但此值不是設的越大越好,應該恰到好處的去設置它,太小了,執行隊列中將會積累很多待處理的任務,太大了,則會消耗大量的系統資源從而影響整體的性能。在產品模式下默認為25個執行線程。
          為了設置理想的執行隊列的線程數,我們可以啟動管理控制臺,在域(如:mydomain)> 服務器 > server實例(如:myserver)> 監視 > 性能中監控最大負載時執行隊列的吞吐量和隊列中的等待請求數,據此確定理想的數值。
          理想的默認執行線程數是由多方面的因素決定的,比如機器CPU性能、總體體系架構、I/O、操作系統的進程調度機制、JVM的線程調度機制。隨著CPU個 數的增加,WebLogic可以近乎線性地提高線程數。線程數越多,花費在線程切換的時間也就越多;線程數越小,CPU可能無法得到充分的利用。為獲取一 個理想的線程數,需要經過反復的測試。在測試中,可以以25*CPU個數為基準進行調整。當空閑線程較少,CPU利用率較低時,可以適當增加線程數的大小 (每五個遞增)。對于PC Server和Windows 2000,則最好每個CPU小于50個線程,以CPU利用率為90%左右為最佳。
          通過啟動管理控制臺,在域(如:mydomain)> 服務器 > server實例(如:myserver)> Execute Queue > weblogic.kernel.Defalt > 配置中修改線程計數。
          b) 設定執行隊列的溢出條件;
          Weblogic Server提供給默認的執行隊列或用戶自定義的執行隊列自定義溢出條件的功能,當滿足此溢出條件時,服務器改變其狀態為“警告”狀態,并且額外的再分配一些線程去處理在隊列中的請求,而達到降低隊列長度的目的。
          通過啟動管理控制臺,在域(如:mydomain)> 服務器 > server實例(如:myserver)> Execute Queue > weblogic.kernel.Defalt > 配置下面幾項:
          L 隊列長度:此值表示執行隊列中可容納的最大請求數,默認值是65536,最后不要手動改變此值。
          L 隊列長度閾值百分比:此值表示溢出條件,在此服務器指出隊列溢出之前可以達到的隊列長度大小的百分比。
          L 線程數增加:當檢測到溢出條件時,將增加到執行隊列中的線程數量。如果CPU和內存不是足夠的高,盡量不要改變默認值“0”。因為Weblogic一旦增加后不會自動縮減,雖然最終可能確實起到了降低請求的作用,但在將來的運行中將影響程序的性能。
          L   最大線程數:為了防止創建過多的線程數量,可以通過設定最大的線程數進行控制。
          在實際的應用場景中,應根據具體情況適當的調整以上參數。
          c) 設定執行隊列監測行為
          Weblogic Server能夠自動監測到當一個執行線程變為“阻塞”。變為“阻塞”狀態的執行線程將無法完成當前的工作,也無法再執行新請求。如果執行隊列中的所有執 行線程都變為“阻塞”狀態,Weblogic server可能改變狀態為“警告”或“嚴重”狀態。如果Weblogic server變為“嚴重”狀態,可以通過Node Manager來自動關閉此服務器并重新啟動它。具體請參考:Node Manager Capabilities文檔。
          通過啟動管理控制臺,在域(如:mydomain)> 服務器 > server實例(如:myserver)>配置 > 調整下可配置下面幾項:
          l   阻塞線程最長時間:在此服務器將線程診斷為阻塞線程之前,線程必須連續工作的時間長度(秒)。默認情況下,WebLogic Server 認為線程在連續工作 600 秒后成為阻塞線程。
          l   阻塞線程計時器間隔:WebLogic Server 定期掃描線程以查看它們是否已經連續工作了 "阻塞線程最長時間" 字段中指定的時間長度的間隔時間(秒)。默認情況下,WebLogic Server 將此時間間隔設置為 600 秒。
          5) 調優TCP連接緩存數;
          WebLogic Server用Accept Backlog參數規定服務器向操作系統請求的隊列大小,默認值為50。當系統重載負荷時,這個值可能過小,日志中報Connection Refused,導致有效連接請求遭到拒絕,此時可以提高Accept Backlog 25%直到連接拒絕錯誤消失。對于Portal類型的應用,默認值往往是不夠的。Login Timeout和SSL Login Timeout參數表示普通連接和SSL連接的超時時間,如果客戶連接被服務器中斷或者SSL容量大,可以嘗試增加該值。
          通過啟動管理控制臺,在域(如:mydomain)> 服務器 > server實例(如:myserver)>配置 > 調整下可配置“接受預備連接”。
          6) 改變Java編譯器;
          標準的Java編譯器是javac,但編譯JSP servlets速度太慢,為了提高編譯速度,可以使用sj或jikes編譯器取代javac編譯器。下面說說更改Java編譯器:
          通過啟動管理控制臺,在域(如:mydomain)> 服務器 > server實例(如:myserver)>配置 > 常規下改變Java 編譯器,默認為javac。輸入完整路徑,如:c:\visualcafe31\bin\sj.exe。然后打開高級選項,在預規劃到類路徑填寫編譯 Java 代碼時為 Java 編譯器類路徑預規劃的選項,如:BEA_HOME\jdk141_02\jre\lib\rt.jar。
          7) 使用Webogic Server集群提高性能;
          具體關于如何配置Weblogic集群,我就不細說了。詳情可參考:Introduction to WebLogic Server Clustering。
          8) Weblogic EJB調優
          由于EJB2.0已經很少項目在用了,EJB3.0再成熟一點,我再補充這一部分吧!
          9) JDBC應用調優
          JDBC Connection Pool的調優受制于WebLogic Server線程數的設置和數據庫進程數,游標的大小。通常我們在一個線程中使用一個連接,所以連接數并不是越多越好,為避免兩邊的資源消耗,建議設置連 接池的最大值等于或者略小于線程數。同時為了減少新建連接的開銷,將最小值和最大值設為一致。
          增加Statement Cache Size對于大量使用PreparedStatement對象的應用程序很有幫助,WebLogic能夠為每一個連接緩存這些對象,此值默認為10。在保 證數據庫游標大小足夠的前提下,可以根據需要提高Statement Cache Size。比如當你設置連接數為25,Cache Size為10時,數據庫可能需要打開25*10=250個游標。不幸的是,當遇到與PreparedStatement Cache有關的應用程序錯誤時,你需要將Cache Size設置為0。
          盡管JDBC Connection Pool提供了很多高級參數,在開發模式下比較有用,但大部分在生產環境下不需調整。這里建議最好不要設置測試表, 同時Test Reserved Connections和Test Released Connections也無需勾上。 當然如果你的數據庫不穩定,時斷時續,你就可能需要上述的參數打開。
          最后提一下驅動程序類型的選擇,以Oracle為例,Oracle提供thin驅動和oci驅動,從性能上來講,oci驅動強于thin驅動,特別是大數 據量的操作。但在簡單的數據庫操作中,性能相差不大,隨著thin驅動的不斷改進,這一弱勢將得到彌補。而thin驅動的移植性明顯強于oci驅動。所以 在通常情況下建議使用thin驅動。而最新驅動器由于WebLogic server/bin目錄下的類包可能不是最新的,請以Oracle網站為準: http://www.oracle.com/technology ... tdocs/jdbc9201.html
          10) JSP調優
          l   設置jsp-param pageCheckSeconds=-1;
          l   設置serlet-reload-check=-1或ServletReloadCheckSecs=-1;
          l   設置jsp-param precompile=true,關閉JSP預編譯選項。(轉載)
          posted @ 2008-06-30 20:55 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(380) | 評論 (0)編輯 收藏

          ORACLE9i連接SYBASE的透明網關的配置

          ORACLE實現異種數據庫連接服務的技術叫做透明網關(Transparent Gateway)。
          目前ORACLE利用透明網關可以實現和SQL SERVER、SYBASE、DB2等多種主流數據庫的互聯。

          現在通過oracle訪問sybase數據庫,把配置oracle9i TRANSPARENT GATEWAY FOR SYBASE
          的步驟寫成文檔,供需要的網友參考!
           
          配置TRANSPARENT GATEWAY FOR SYBASE步驟

          1.
          oracle所在服務器上安裝sybase client(或者在同一臺server上安裝oracle、sybase服務器)
          確保能夠訪問sybase數據庫

          2.
          安裝TRANSPARENT GATEWAY FOR SYBASE選件,要用自定義安裝。
          正確選擇sybase的安裝目錄

          3.
          選擇一個sid字符串準備賦給sybase數據庫。如:tg4sybs
          設置SYBASE的dll路徑到環境變量PATH(這一步很重要)

          4.
          修改初始化文件,默認的是:
          ORACLE_HOME g4sybsadmininittg4sybs.ora
          設置參數
          HS_FDS_CONNECT_INFO
          格式:HS_FDS_CONNECT_INFO= server_name. database_name[,INTERFACE= interface_file]
          server_name. database_name是大小寫敏感的。
          INTERFACE可選

          例子:如下
          $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
          # This is a sample agent init file that contains the HS parameters that are
          # needed for the Transparent Gateway for Sybase

          #
          # HS init parameters
          #
          HS_FDS_CONNECT_INFO=migration_serv.tax
          HS_FDS_TRACE_LEVEL=OFF
          HS_FDS_RECOVERY_ACCOUNT=RECOVER
          HS_FDS_RECOVERY_PWD=RECOVER

          #
          # Environment variables required for Sybase
          #
          set SYBASE=d:sybase
          $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
          上例中
          server_name是migration_serv
          database_name是tax

          5.
          配置oracle網絡服務的listener,配置文件是:listener.ora
          默認路徑:ORACLE_HOME etworkadmin
          加入如下

          SID_LIST_LISTENER=
          (SID_LIST=
          (SID_DESC=
          (SID_NAME= gateway_sid)
          (ORACLE_HOME= oracle_home_directory)
          (PROGRAM=tg4sybs)
          )
          )

          gateway_sid就是3選擇的sid字符串
          oracle_home_directory是ORACLE_HOME
          tg4sybs若是SYBASE是特定的。如果是其他數據庫,會不同。
          例子如下:
          $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
              (SID_DESC=
                (SID_NAME=tg4sybs)
                (ORACLE_HOME = D:oracleora92)
                (PROGRAM=tg4sybs)
              )
          $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

          6.
          停止監聽

          lsnrctl stop


          重新啟動監聽程序

          lsnrctl start

          7.
          配置oracle server的tnsnames.ora使其能夠訪問sybase
          connect_descriptor=
          (DESCRIPTION=
          (ADDRESS=
          (PROTOCOL=TCP)
          (HOST= host_name)
          (PORT= port_number)
          )
          (CONNECT_DATA=
          (SID= gateway_sid))
          (HS=OK))

          connect_descriptor是連接串,任取,一般為sybs
          host_name:oracle server的name
          port_number:oracle監聽端口
          gateway_sid就是3選擇的sid字符串

          例子如下:
          $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
          sybs=
             (DESCRIPTION=
                 (ADDRESS_LIST =
                 (ADDRESS=(PROTOCOL=TCP)(HOST= dw-server1)(PORT= 1521))
                 )
             (CONNECT_DATA=
                 (SID= tg4sybs)
             )
                 (HS=OK)
             )

          $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

          8.建立database link

          如:
          CREATE  DATABASE LINK sybs  CONNECT TO sa
              IDENTIFIED BY prient 
              USING "SBYS" ;
             
          即可訪問sybase 數據庫。


          需要注意的是,sybase數據庫的表名,字段名,如果是小寫的,那么在oracle里訪問的時候要加上雙引號""

          如:
          SQL〉select "a" from
          "b"@sybs;

           

          posted @ 2008-06-29 21:40 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(247) | 評論 (0)編輯 收藏

          一種理想的在關系數據庫中存儲樹型結構數據的方法
          2008-03-07 15:49
              在各種基于關系數據庫的應用系統開發中,我們往往需要存儲樹型結構的數據,目前有很多流行的方法,如鄰接列表模型(The Adjacency List Model),在此基礎上也有很多人針對不同的需求做了相應的改進,但總是在某些方面存在的各種各樣的缺陷。
              那么理想中的樹型結構應具備哪些特點呢?數據存儲冗余小、直觀性強;方便返回整個樹型結構數據;可以很輕松的返回某一子樹(方便分層加載);快整獲以某節 點的祖譜路徑;插入、刪除、移動節點效率高等等。帶著這些需求我查找了很多資料,發現了一種理想的樹型結構數據存儲及操作算法,改進的前序遍歷樹模型 (The Nested Set Model)。

          一、數據

              在本文中,舉一個在線食品店樹形圖的例子。這個食品店通過類別、顏色和品種來組織食品。樹形圖如下:

          二、鄰接列表模型(The Adjacency List Model)

          在這種模型下,上述數據在關系數據庫的表結構數據通常如下圖所示:

          由于該模型比較簡單,在此不再詳細介紹其算法,下面列出它的一些不足:

              在大多數編程語言中,他運行很慢,效率很差。這主要是“遞歸”造成的。我們每次查詢節點都要訪問數據庫。每次數據庫查詢都要花費一些時間,這讓函數處理龐 大的樹時會十分慢。造成這個函數不是太快的第二個原因可能是你使用的語言。不像Lisp這類語言,大多數語言不是針對遞歸函數設計的。對于每個節點造成這 個函數不是太快的第二個原因可能是你使用的語言。不像Lisp這類語言,大多數語言不是針對遞歸函數設計的。對于每個節點,函數都要調用他自己,產生新的 實例。這樣,對于一個4層的樹,你可能同時要運行4個函數副本。對于每個函數都要占用一塊內存并且需要一定的時間初始化,這樣處理大樹時遞歸就很慢了。

          三、改進的前序遍歷樹模型(The Nested Set Model)

          原理:

              我們先把樹按照水平方式擺開。從根節點開始(“Food”),然后他的左邊寫上1。然后按照樹的順序(從上到下)給“Fruit”的左邊寫上2。這樣,你 沿著樹的邊界走啊走(這就是“遍歷”),然后同時在每個節點的左邊和右邊寫上數字。最后,我們回到了根節點“Food”在右邊寫上18。下面是標上了數字 的樹,同時把遍歷的順序用箭頭標出來了。

              我們稱這些數字為左值和右值(如,“Food”的左值是1,右值是18)。正如你所見,這些數字按時了每個節點之間的關系。因為“Red”有3和6兩個 值,所以,它是有擁有1-18值的“Food”節點的后續。同樣的,我們可以推斷所有左值大于2并且右值小于11的節點,都是有2-11的“Fruit” 節點的后續。這樣,樹的結構就通過左值和右值儲存下來了。這種數遍整棵樹算節點的方法叫做“改進前序遍歷樹”算法。

          表結構設計:

          常用的操作:

          下面列出一些常用操作的SQL語句

          返回完整的樹(Retrieving a Full Tree)
          SELECT node.name
            
          FROM nested_category node, nested_category parent
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
             
          AND parent.name = 'electronics'
          ORDER BY node.lft

          返回某結點的子樹(Find the Immediate Subordinates of a Node)
          SELECT V.*
            
          FROM (SELECT node.name,
                          (
          COUNT(parent.name) - (AVG(sub_tree.depth) + 1)) depth
                    
          FROM nested_category node,
                          nested_category parent,
                          nested_category sub_parent,
                          (
          SELECT V.*
                            
          FROM (SELECT node.name, (COUNT(parent.name) - 1) depth
                                    
          FROM nested_category node, nested_category parent
                                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                                     
          AND node.name = 'portable electronics'
                                   
          GROUP BY node.name) V,
                                  nested_category T
                           
          WHERE V.name = T.name
                           
          ORDER BY T.lft) sub_tree
                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                     
          AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
                     
          AND sub_parent.name = sub_tree.name
                   
          GROUP BY node.name) V,
                  nested_category T
          WHERE V.name = T.name
             
          and V.depth <= 1
             
          and V.depth > 0
          ORDER BY T.Lft

          返回某結點的祖譜路徑(Retrieving a Single Path)
          SELECT parent.name
            
          FROM nested_category node, nested_category parent
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
             
          AND node.name = 'flash'
          ORDER BY node.lft

          返回所有節點的深度(Finding the Depth of the Nodes)
          SELECT V.*
            
          FROM (SELECT node.name, (COUNT(parent.name) - 1) depth
                    
          FROM nested_category node, nested_category parent
                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                   
          GROUP BY node.name) V,
                  nested_category T
          WHERE V.name = T.name
          ORDER BY T.Lft

          返回子樹的深度(Depth of a Sub-Tree)
          SELECT V.*
            
          FROM (SELECT node.name,
                          (
          COUNT(parent.name) - (AVG(sub_tree.depth) + 1)) depth
                    
          FROM nested_category node,
                          nested_category parent,
                          nested_category sub_parent,
                          (
          SELECT V.*
                            
          FROM (SELECT node.name, (COUNT(parent.name) - 1) depth
                                    
          FROM nested_category node, nested_category parent
                                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                                     
          AND node.name = 'portable electronics'
                                   
          GROUP BY node.name) V,
                                  nested_category T
                           
          WHERE V.name = T.name
                           
          ORDER BY T.lft) sub_tree
                   
          WHERE node.lft BETWEEN parent.lft AND parent.rgt
                     
          AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
                     
          AND sub_parent.name = sub_tree.name
                   
          GROUP BY node.name) V,
                  nested_category T
          WHERE V.name = T.name
          ORDER BY T.Lft

          返回所有的葉子節點(Finding all the Leaf Nodes)
          SELECT name FROM nested_category WHERE rgt = lft + 1

          插入節點(Adding New Nodes)
          LOCK TABLE nested_category WRITE;

          SELECT @myRight := rgt FROM nested_category WHERE name = 'TELEVISIONS';

          UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
          UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;

          INSERT INTO nested_category
             (name, lft, rgt)
          VALUES
             (
          'GAME CONSOLES', @myRight + 1, @myRight + 2);

          UNLOCK TABLES;

          刪除節點(Deleting Nodes)
          LOCK TABLE nested_category WRITE;

          SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
            
          FROM nested_category
          WHERE name = 'GAME CONSOLES';

          DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

          UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
          UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;

          UNLOCK TABLES;
          posted @ 2008-06-27 15:56 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(492) | 評論 (0)編輯 收藏

          無論你要構建自己的論壇,在你的網站上發布消息還是書寫自己的cms [1]程序,你都會遇到要在數據庫中存儲層次數據的情況。同時,除非你使用一種像XML [2]的數據庫,否則關系數據庫中的表都不是層次結構的,他們只是一個平坦的列表。所以你必須找到一種把層次數據庫轉化的方法。

          存儲樹形結構是一個很常見的問題,他有好幾種解決方案。主要有兩種方法:鄰接列表模型和改進前序遍歷樹算法

          在本文中,我們將探討這兩種保存層次數據的方法。我將舉一個在線食品店樹形圖的例子。這個食品店通過類別、顏色和品種來組織食品。樹形圖如下:

          1105_tree

          本文包含了一些代碼的例子來演示如何保存和獲取數據。我選擇PHP [3]來寫例子,因為我常用這個語言,而且很多人也都使用或者知道這個語言。你可以很方便地把它們翻譯成你自己用的語言。

          鄰接列表模型(The Adjacency List Model)

          我們要嘗試的第一個——也是最優美的——方法稱為“鄰接列表模型”或稱為“遞歸方法”。它是一個很優雅的方法因為你只需要一個簡單的方法來在你的樹中進行迭代。在我們的食品店中,鄰接列表的表格如下:

          1105_table1

          如你所見,對每個節點保存一個“父”節點。我們可以看到“Pear [4]”是“Green”的一個子節點,而后者又是“Fruit”的子節點,如此類推。根節點,“Food”,則他的父節點沒有值。為了簡單,我只用了“title”值來標識每個節點。當然,在實際的數據庫中,你要使用數字的ID。

          顯示樹

          現在我們已經把樹放入數據庫中了,得寫一個顯示函數了。這個函數將從根節點開始——沒有父節點的節點——同時要顯示這個節點所有的子節點。對于這些子節點,函數也要獲取并顯示這個子節點的子節點。然后,對于他們的子節點,函數還要再顯示所有的子節點,然后依次類推。

          也許你已經注意到了,這種函數的描述,有一種普遍的模式。我們可以簡單地只寫一個函數,用來獲得特定節點的子節點。這個函數然后要對每個子節點調用自身來再次顯示他們的子節點。這就是“遞歸”機制,因此稱這種方法叫“遞歸方法”。

          <?php
          // $parent 是我們要查看的子節點的父節點
          // $level 會隨著我們深入樹的結構而不斷增加,
          //        用來顯示一個清晰的縮進格式
          function display_children($parent, $level) {
          // 獲取$parent的全部子節點
          $result = mysql_query('SELECT title FROM tree '.
          'WHERE parent="'.$parent.'";');
          // 顯示每個節點
          while ($row = mysql_fetch_array($result)) {
          // 縮進并顯示他的子節點的標題
          echo str_repeat('  ',$level).$row['title']."\n";
          // 再次調用這個函數來顯著這個子節點的子節點
          display_children($row['title'], $level+1);
          }
          }
          ?>

          要實現整個樹,我們只要調用函數時用一個空字符串作為$parent$level = 0: display_children('',0); 函數返回了我們的食品店的樹狀圖如下:

          Food
          Fruit
          Red
          Cherry
          Yellow
          Banana
          Meat
          Beef
          Pork

          注意如果你只想看一個子樹,你可以告訴函數從另一個節點開始。例如,要顯示“Fruit”子樹,你只要display_children('Fruit',0);

          
          

          節點的路徑

          利用差不多的函數,我們也可以查詢某個節點的路徑如果你只知道這個節點的名字或者ID。例如,“Cherry”的路徑是“Food”>“Fruit”>“Red”。要獲得這個路徑,我們的函數要獲得這個路徑,這個函數必須從最深的層次開始:“Cheery”。但后查找這個節點的父節點,并添加到路徑中。在我們的例子中,這個父節點是“Red”。如果我們知道“Red”是“Cherry”的父節點。

          <?php
          // $node 是我們要查找路徑的那個節點的名字
          function get_path($node) {
          // 查找這個節點的父節點
          $result = mysql_query('SELECT parent FROM tree '.
          'WHERE title="'.$node.'";');
          $row = mysql_fetch_array($result);
          // 在這個array [5] 中保存數組
          $path = array();
          // 如果 $node 不是根節點,那么繼續
          if ($row[’parent’]!=”) {
          //  $node 的路徑的最后一部分是$node父節點的名稱
          $path[] = $row[’parent’];
          // 我們要添加這個節點的父節點的路徑到現在這個路徑
          $path = array_merge(get_path($row[’parent’]), $path);
          }
          // 返回路徑
          return $path;
          }
          ?>

          這個函數現在返回了指定節點的路徑。他把路徑作為數組返回,這樣我們可以使用print_r(get_path('Cherry')); 來顯示,其結果是:

          Array
          (
          [0] => Food
          [1] => Fruit
          [2] => Red
          )

          不足

          正如我們所見,這確實是一個很好的方法。他很容易理解,同時代碼也很簡單。但是鄰接列表模型的缺點在哪里呢?在大多數編程語言中,他運行很慢,效率很差。這主要是“遞歸”造成的。我們每次查詢節點都要訪問數據庫。

          每次數據庫查詢都要花費一些時間,這讓函數處理龐大的樹時會十分慢。

          造成這個函數不是太快的第二個原因可能是你使用的語言。不像Lisp這類語言,大多數語言不是針對遞歸函數設計的。對于每個節點,函數都要調用他自己,產生新的實例。這樣,對于一個4層的樹,你可能同時要運行4個函數副本。對于每個函數都要占用一塊內存并且需要一定的時間初始化,這樣處理大樹時遞歸就很慢了。

          改進前序遍歷樹

          現在,讓我們看另一種存儲樹的方法。遞歸可能會很慢,所以我們就盡量不使用遞歸函數。我們也想盡量減少數據庫查詢的次數。最好是每次只需要查詢一次。

          我們先把樹按照水平方式擺開。從根節點開始(“Food”),然后他的左邊寫上1。然后按照樹的順序(從上到下)給“Fruit”的左邊寫上2。這樣,你沿著樹的邊界走啊走(這就是“遍歷”),然后同時在每個節點的左邊和右邊寫上數字。最后,我們回到了根節點“Food”在右邊寫上18。下面是標上了數字的樹,同時把遍歷的順序用箭頭標出來了。

          1105_numbering

          我們稱這些數字為左值和右值(如,“Food”的左值是1,右值是18)。正如你所見,這些數字按時了每個節點之間的關系。因為“Red”有3和6兩個值,所以,它是有擁有1-18值的“Food”節點的后續。同樣的,我們可以推斷所有左值大于2并且右值小于11的節點,都是有2-11的“Food”節點的后續。這樣,樹的結構就通過左值和右值儲存下來了。這種數遍整棵樹算節點的方法叫做“改進前序遍歷樹”算法。

          在繼續前,我們先看看我們的表格里的這些值:

          1105_table2

          注意單詞“left”和“right”在SQL中有特殊的含義。因此,我們只能用“lft”和“rgt”來表示這兩個列。(譯注——其實Mysql中可以用“`”來表示,如“`left`”,MSSQL中可以用“[]”括出,如“[left]”,這樣就不會和關鍵詞沖突了。)同樣注意這里我們已經不需要“parent”列了。我們只需要使用lft和rgt就可以存儲樹的結構。

          獲取樹

          如果你要通過左值和右值來顯示這個樹的話,你要首先標識出你要獲取的那些節點。例如,如果你想獲得“Fruit”子樹,你要選擇那些左值在2到11的節點。用SQL語句表達:

          SELECT * FROM tree WHERE lft BETWEEN 2 AND 11;

          這個會返回:

          1105_table3

          好吧,現在整個樹都在一個查詢中了。現在就要像前面的遞歸函數那樣顯示這個樹,我們要加入一個ORDER BY子句在這個查詢中。如果你從表中添加和刪除行,你的表可能就順序不對了,我們因此需要按照他們的左值來進行排序。

          SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;

          就只剩下縮進的問題了。

          要顯示樹狀結構,子節點應該比他們的父節點稍微縮進一些。我們可以通過保存一個右值的一個棧。每次你從一個節點的子節點開始時,你把這個節點的右值添加到棧中。你也知道子節點的右值都比父節點的右值小,這樣通過比較當前節點和棧中的前一個節點的右值,你可以判斷你是不是在顯示這個父節點的子節點。當你顯示完這個節點,你就要把他的右值從棧中刪除。要獲得當前節點的層數,只要數一下棧中的元素。

          <?php
          function display_tree($root) {
          // 獲得$root節點的左邊和右邊的值
          $result = mysql_query('SELECT lft, rgt FROM tree '.
          'WHERE title="'.$root.'";');
          $row = mysql_fetch_array($result);
          // 以一個空的$right棧開始
          $right = array();
          // 現在,獲得$root節點的所有后序
          $result = mysql_query('SELECT title, lft, rgt FROM tree '.
          'WHERE lft BETWEEN '.$row['lft'].' AND '.
          $row['rgt'].' ORDER BY lft ASC;');
          // 顯示每一行
          while ($row = mysql_fetch_array($result)) {
          // 檢查棧里面有沒有元素
          if (count($right)>0) {
          // 檢查我們是否需要從棧中刪除一個節點
          while ($right[count($right)-1]<$row['rgt']) {
          array_pop($right);
          }
          }
          // 顯示縮進的節點標題
          echo str_repeat('  ',count($right)).$row['title']."\n";
          // 把這個節點添加到棧中
          $right[] = $row['rgt'];
          }
          }
          ?>

          如果運行這段代碼,你可以獲得和上一部分討論的遞歸函數一樣的結果。而這個函數可能會更快一點:他不采用遞歸而且只是用了兩個查詢

          節點的路徑

          有了新的算法,我們還要另找一種新的方法來獲得指定節點的路徑。這樣,我們就需要這個節點的祖先的一個列表。

          由于新的表結構,這不需要花太多功夫。你可以看一下,例如,4-5的“Cherry”節點,你會發現祖先的左值都小于4,同時右值都大于5。這樣,我們就可以使用下面這個查詢:

          SELECT title FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;

          注意,就像前面的查詢一樣,我們必須使用一個ORDER BY子句來對節點排序。這個查詢將返回:

          +-------+
          | title |
          +-------+
          | Food  |
          | Fruit |
          | Red   |
          +-------+

          我們現在只要把各行連起來,就可以得到“Cherry”的路徑了。

          有多少個后續節點?How Many Descendants

          如果你給我一個節點的左值和右值,我就可以告訴你他有多少個后續節點,只要利用一點點數學知識。

          因為每個后續節點依次會對這個節點的右值增加2,所以后續節點的數量可以這樣計算:

          descendants = (right – left - 1) / 2

          利用這個簡單的公式,我可以立刻告訴你2-11的“Fruit”節點有4個后續節點,8-9的“Banana”節點只是1個子節點,而不是父節點。

          自動化樹遍歷

          現在你對這個表做一些事情,我們應該學習如何自動的建立表了。這是一個不錯的練習,首先用一個小的樹,我們也需要一個腳本來幫我們完成對節點的計數。

          讓我們先寫一個腳本用來把一個鄰接列表轉換成前序遍歷樹表格。

          <?php
          function rebuild_tree($parent, $left) {
          // 這個節點的右值是左值加1
          $right = $left+1;
          // 獲得這個節點的所有子節點
          $result = mysql_query('SELECT title FROM tree '.
          'WHERE parent="'.$parent.'";');
          while ($row = mysql_fetch_array($result)) {
          // 對當前節點的每個子節點遞歸執行這個函數
          // $right 是當前的右值,它會被rebuild_tree函數增加
          $right = rebuild_tree($row['title'], $right);
          }
          // 我們得到了左值,同時現在我們已經處理這個節點我們知道右值的子節點
          mysql_query('UPDATE tree SET lft='.$left.', rgt='.
          $right.' WHERE title="'.$parent.'";');
          // 返回該節點的右值+1
          return $right+1;
          }
          ?>

          這是一個遞歸函數。你要從rebuild_tree('Food',1); 開始,這個函數就會獲取所有的“Food”節點的子節點。

          如果沒有子節點,他就直接設置它的左值和右值。左值已經給出了,1,右值則是左值加1。如果有子節點,函數重復并且返回最后一個右值。這個右值用來作為“Food”的右值。

          遞歸讓這個函數有點復雜難于理解。然而,這個函數確實得到了同樣的結果。他沿著樹走,添加每一個他看見的節點。你運行了這個函數之后,你會發現左值和右值和預期的是一樣的(一個快速檢驗的方法:根節點的右值應該是節點數量的兩倍)。

          添加一個節點

          我們如何給這棵樹添加一個節點?有兩種方式:在表中保留“parent”列并且重新運行rebuild_tree()
          函數——一個很簡單但卻不是很優雅的函數;或者你可以更新所有新節點右邊的節點的左值和右值。

          第一個想法比較簡單。你使用鄰接列表方法來更新,同時使用改進前序遍歷樹來查詢。如果你想添加一個新的節點,你只需要把節點插入表格,并且設置好parent列。然后,你只需要重新運行rebuild_tree() 函數。這做起來很簡單,但是對大的樹效率不高。

          第二種添加和刪除節點的方法是更新新節點右邊的所有節點。讓我們看一下例子。我們要添加一種新的水果——“Strawberry”,作為“Red”的最后一個子節點。首先,我們要騰出一個空間。“Red”的右值要從6變成8,7-10的“Yellow”節點要變成9-12,如此類推。更新“Red”節點意味著我們要把所有左值和右值大于5的節點加上2。

          我們用一下查詢:

          UPDATE tree SET rgt=rgt+2 WHERE rgt>5;
          UPDATE tree SET lft=lft+2 WHERE lft>5;

          現在我們可以添加一個新的節點“Strawberry”來填補這個新的空間。這個節點左值為6右值為7。

          INSERT INTO tree SET lft=6, rgt=7, title='Strawberry';

          如果我們運行display_tree() 函數,我們將發現我們新的“Strawberry”節點已經成功地插入了樹中:

          Food
          Fruit
          Red
          Cherry
          Strawberry
          Yellow
          Banana
          Meat
          Beef
          Pork

          缺點

          首先,改進前序遍歷樹算法看上去很難理解。它當然沒有鄰接列表方法簡單。然而,一旦你習慣了左值和右值這兩個屬性,他就會變得清晰起來,你可以用這個技術來完成臨街列表能完成的所有事情,同時改進前序遍歷樹算法更快。當然,更新樹需要很多查詢,要慢一點,但是取得節點卻可以只用一個查詢。

          總結

          你現在已經對兩種在數據庫存儲樹方式熟悉了吧。雖然在我這兒改進前序遍歷樹算法性能更好,但是也許在你特殊的情況下鄰接列表方法可能表現更好一些。這個就留給你自己決定了

          最后一點:就像我已經說得我部推薦你使用節點的標題來引用這個節點。你應該遵循數據庫標準化的基本規則。我沒有使用數字標識是因為用了之后例子就比較難讀。

          進一步閱讀

          數據庫指導 Joe Celko寫的更多關于SQL數據庫中的樹的問題:
          http://searchdatabase.techtarget.com/tip/1,289483,sid13_gci537290,00.html [6]

          另外兩種處理層次數據的方法:
          http://www.evolt.org/article/Four_ways_to_work_with_hierarchical_data/17/4047/index.html [7]

          Xindice, “本地XML數據庫”:
          http://xml.apache.org/xindice/ [8]

          遞歸的一個解釋:
          http://www.strath.ac.uk/IT/Docs/Ccourse/subsection3_9_5.html [9]

          posted @ 2008-06-27 15:55 存鷹之心于高遠,取鷹之志而凌云,習鷹之性以涉險,融鷹之神在山巔. 閱讀(310) | 評論 (0)編輯 收藏

          僅列出標題
          共6頁: 上一頁 1 2 3 4 5 6 下一頁 
          主站蜘蛛池模板: 云龙县| 长治市| 永福县| 偃师市| SHOW| 普定县| 安康市| 诏安县| 连城县| 柏乡县| 浠水县| 固原市| 西丰县| 雷州市| 潼关县| 怀柔区| 松江区| 嵊泗县| 江都市| 侯马市| 铁力市| 韶关市| 阿坝县| 东明县| 濉溪县| 荃湾区| 涪陵区| 丰都县| 兴业县| 安溪县| 霍城县| 雷山县| 崇明县| 恩平市| 万源市| 周口市| 宽城| 靖安县| 西和县| 平湖市| 江津市|