Oracle性能优化 以及 库缓存命中率及等待事件
http://www.cnblogs.com/hyddd/archive/2009/08/30/1556939.html
前言???
??? 最近hyddd一直看Oracle的資料,今天特地總結一下這段時間了解到的關于Oracle性能優化的下手點。
?
一.數據庫優化的方向
1.程序設計(這點最重要,如果程序本身設計有問題,再怎么進行下面的優化都是徒勞的。)
2.操作系統優化
3.硬件優化
4.數據庫優化
5.SQL語句優化
?
二.硬件優化
這里涉及到的硬件主要有:硬盤,內存。
1.硬盤的讀寫速度:建議增加硬盤的數量,從而增加每秒的并發操作。
2.內存大小。
?
三.數據庫優化
以上是Oracle 9i的體系結構,關于Oracle數據庫的優化,很大程度上圖有關。
1.根據數據庫的使用方式得出合理的優化策略
(1)OLTP:連機事務處理。
需要實時處理大量請求,而每次處請求的數據量都是很小的。OLTP是傳統的關系型數據庫的主要應用,主要是基本的、日常的事務處理,例如銀行交易。
性能好壞的重要指標:響應時間與請求處理并發數。
(2)OLAP:聯機分析處理。
可以簡單地理解為在海量數據中得出統計/綜合信息,是數據倉庫的主要應用。做OLAP應用的數據庫,數據量通常量非常大。和OLTP不同,OLAP應用的并發處理量是很低的,所以基本不用考慮并發問題。而在處理數據量方面,OLAP每次操作所需要處理的數據量通常都是非常大的,這點也和OLTP相反。
性能好壞的重要指標:查詢大量數據的速度。
由于OLTP和OLAP是兩個不同應用方向,所以在優化數據庫時應采取不同的優化策略。
2.內存分配比例:2:1:1
內存分配的較佳方案:SGA占50%的物理內存,PGA和操作系統各占25%的物理內存,當然這也不是絕對的,但是SGA占的物理內存不能超過75%,最多只能占70%,否則Oracle可能會出現各種的異常。
3.SGA中各個POOL的內存分配
(1)Shared Pool:要分配多少內存不定,只要滿足以下兩個條件即可:
-Lib Cache的命中率>98%
-Data Dictionary Cache的命中率>85%
(2)Large Pool
-專用服務器:100-200M
-共享服務器:Session * (Sort_area_size + 2)
(3)Java Pool:無需使用,一般不分配空間。
(4)Redo Log Buffer Cache:小于5M,因為它的信息非常重要,應盡快把緩存數據寫到文件中。
(5)DB_Buffer_Cache:盡量大。
4.在Oracle 10g中,如果我們定義了SGA_MAX_SIZE后,其實我們通過SGA_TARGET讓Oracle自動調整SGA的內存分配。
5.Redo log files,Data files,Archive log files盡量放在不同的磁盤上,以均衡I/O。特別是Redo Log files和Archivelog files。
6.Undo Segment容量大小要符合實際應用,不能太小。
7.熱點文件特殊處理
(1)分開存放Index Segment和Data Segment。
(2)使用分區表。
8.索引(Index)問題
(1)適當使用BTREE,BITMAP以及反向索引:不同索引適用于不同的表,設置索引之前要考慮這個問題。
(2)一個表的索引數最多不要超過5個,否則可能影響性能。
(3)定期重構索引。(單邊樹索引重構時可考慮使用反向索引降低索引樹高度)
(4)注意索引是否失效,這一般是比較爛的SQL語句引起的問題。
9.盡量減少“全表掃描操作” & “排序操作”
10.看執行計劃有助于各位DBA找出性能問題。
? 由于oracle10g簡化了配置參數,而且提供自動根據數據庫訪問情況對各類緩存進行分析優化的功能,因此在oracle參數這層進行優化主要有以下幾點。PRE_PAGE_SGA
oracle實例啟動時,會只載入各個內存區最小的大小。而其他SGA內存只作為虛擬內存分配,只有當進程touch到相應的頁時,才會置換到物理內存中。但我們也許希望實例一啟動后,所有SGA都分配到物理內存。這時就可以通過設置PRE_PAGE_SGA參數來達到目的了。
這個參數的默認值為FALSE,即不將全部SGA置入物理內存中。當設置為TRUE時,實例啟動會將全部SGA置入物理內存中。它可以使實例啟動達到它的最大性能狀態,但是,啟動時間也會更長(因為為了使所有SGA都置入物理內存中,oracle進程需要touch所有的SGA頁)。
LOCK_SGA
為了保證SGA都被鎖定在物理內存中,而不必頁入/頁出,可以通過參數LOCK_SGA來控制。這個參數默認值為FALSE,當指定為TRUE時,可以將全部SGA都鎖定在物理內存中。當然,有些系統不支持內存鎖定,這個參數也就無效了
?
SGA_TARGET / SGA_MAX_SIZE
10G中不需要分別制定SGA的每個部分的大小,假設將SGA_TARGET設置為200M,表明SGA最大為200M 所有SGA組件如share pool,buffer cache,large pool,java pool都需從其中分配,oracle會自動為各組件指定初始值,并在運行過程中動態的調整各組件的大小。
當使用SGA_TARGET時,若未設置SGA_MAX_SIZE 或設置小于SGA_TARGET 則SGA_MAX_SIZE等于SGA-TARGET.SGA_TARGET 是可動態調整的,其值不能大于SGA_MAX_SIZE
目前推薦的參數配置:
LOCK_SGA?= TRUE
SGA_TARGET?>= 4G (在系統物理內存允許的情況下越大越好)
SGA_MAX_SIZE?>= 4G (在系統物理內存允許的情況下越大越好)
但是效率并沒有什么提高,通過以下兩個查詢看,命中率分別為99.2%和98.8%,說明在oracle程序層的優化已經達到了較高的效率:
查詢高速數據緩存命中率:
select sum(pins) "data access",sum(pins)-sum(reloads) "cache hit",
(1-sum(reloads)/sum(pins)) * 100 "hit rate" from v$librarycache
?
查詢數據字典命中率:
select sum(gets) "dictionary access",sum(gets) - sum(getmisses) "dictionary cache hit",
(1-sum(getmisses)/sum(gets)) * 100 "hit rate" from v$rowcache
四.SQL語句優化
1.通過v$librarycache了解命中率,這和3.1中提到的Shared Pool有關系,避免重復解析SQL語句,有助于提高數據庫性能。
2.使用“物化視圖”提高查詢性能。
?
后記
??? 本文列了一些Oracle性能優化要點,如果大家要進行Oracle基礎優化時,可參考以上要點。
http://yeshaochen.blog.51cto.com/3155801/785561
Oracle性能優化 之 庫緩存命中率及等待事件2012-02-21 12:20:01 標簽: oracle 數據庫 性能優化 OCP 庫緩存 原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。 http://yeshaochen.blog.51cto.com/3155801/785561
3.庫緩存的大小
我們上面從程序員的角度上講述了如何共享執行計劃。下面再來看看作為DBA可以為共享執行計劃做什么事。首先我們要知道,每條語句的執行計劃是保存在庫緩存中的,優化器在解析語句時,先要到庫緩存中,以語句的文本為條件,查找有沒有此語句的執行計劃,如果已經有了,就直接取出來交給服務器進程執行,這就是軟解析。如果庫緩存中不存在相同的語句,優化器就為此語句生成執行計劃,再把生成的計劃存入庫緩存,這就硬解析。那么庫緩存的大小是有一定限制的,如果你有非常多的語句,不可能每條語句的執行計劃都能被存放到庫緩存中。假設用戶又發出了一條新的語句A,優化器經過查找,沒有在庫緩存中發現同樣的語句,優化器開始硬解析,生成了執行計劃A。優化器將計劃A存入庫緩存時,發現庫緩存已經沒有空閑空間了,優化器就會把原來的某條語句的執行計劃從庫緩存中清除掉,騰出可用的空間以容納計劃A。被清除的計劃我們稱為犧牲者,清除操作我們稱為語句的“老化”。老化的語句再次執行時,又要重新硬解析。如果你的庫緩存大小設置的比較小,就會頻繁的有語句被老化。這無形中增加了硬解析的次數。因此,庫緩存不能設置的太小。如果庫緩存太多了呢,這也不行,因為白白的占用了寶貴的內存資源。那么,庫緩存到底多大的大小才算合適呢?這沒有統一的標準,你仍然只能借助歷史數據觀察。觀察的標準就是軟、硬解析的數量。
資料視圖中的軟、硬解析資料我們已經說過了。下面,來看看STATSPACK報告中的軟、硬解析數據。在報告中,有個Load?profile部分,我們稱之為概要信息,在概要信息中就包含有軟、硬解析的信息:
Load?Profile
~~~~~~~~~~~~????Per?Second???????Per?Transaction
---------------???????---------------
Redo?size: 16,233.08 422,060.00(每秒或每事務產生的日志數量,單位字節)redo?size
Logical?reads: 1,413.08???? 36,740.00(每秒或每事務的邏輯讀塊數,單位數據庫塊)session?logical?reads
Block?changes:??? 43.19?????? 1,123.00(每秒或每事務塊改變的數量)db?block?changes
Physical?reads:??? 1,198.92??? 31,172.00(每秒或每事務的物理讀數量,單位:塊)physical?reads
Physical?writes:?? 0.00?????? 0.00(每秒或每事務的物理寫數量,單位:塊)physical?writes
User?calls:?????? 0.96?????? 25.00(每秒或每事務用戶調用次數)user?calls
Parses:????????? 0.65?????? 17.00(每秒或每事務的解析數量,包括軟軟、軟和硬解析)parse?count?(hard)
Hard?parses:????? 0.04?????? 1.00(每秒或每事務和硬解析解析數量)parse?count?(hard)
Sorts:?????????? 2.38?????? 62.00(每秒或每事務產生的排序次數)sorts?(memory)、sorts?(disk)
Logons:???????? 0.00?????? 0.00(每秒或每事務登錄的次數)logons?cumulative
Executes:???????? 1.88?????? 49.00(每秒或每事務執行的次數)execute?count
Transactions:????? 0.04(每秒產生的事務數)
這里Hard?parses就是硬解析的次數。在一般的中型規模的OLTP應用中,此值應該控制在100以內。如果超過了100,說明硬解析太多,執行計劃沒有共享。沒有共享計劃的原因可能是沒有使用綁定變量,或者是庫緩存太小,語句老化的太快。這個值只是給你一個參考,準確的你還應該根據歷史資料來分析。
Parses減去Hard?parses就是軟解析的次數了,這個值也不應該太多,中型規模的OLTP一般每秒也就是幾百次,大型OLTP應用每秒軟解析可能很有上千次。(這個值太大的話,應該使用無解析)
除概要信息外,還有一部分“實例有效性”中,也包含解析數據:
Instance?Efficiency?Percentages?(Target?100%)
??????????Buffer?Nowait?%:? 100.00?? ???Redo?NoWait?%: ?? 100.00
Buffer??Hit???%:? 15.16? ?? ?In-memory?Sort?%: ?? 100.00
??????? ??Library?Hit???%:? 99.01??? ??????Soft?Parse?%:?? 94.12
????? Execute?to?Parse?%:?? 65.31?????? ???Latch?Hit?%:? 100.00
Parse?CPU?to?Parse?Elapsd?%: 100.00???? ??%?Non-Parse?CPU:??? 99.41
這其中和庫緩存、軟硬解析相關的有:
Library?Hit?%:Library?cache中的命中比率,軟解析就是庫緩存命中。這個比例通常應該保持在90%以上,否則就是庫緩存太小或沒有使用綁定變量。
Soft?Parse?%:計算公式100×(1-parse?count?(hard)?/?parse?count?(total)),軟解析在所有解析中的比例。這個值小于<95%說明硬解析有點多,需要注意。如果低于80%,執行計劃的共享就出了嚴重問題,解決方法當然還是加大庫緩存或使用綁定變量。
Execute?to?Parse?%:語句執行和分析了次數的度量。這個資料對我們這節課的內容沒什么幫助。寫在這里是想讓大家了解他一下就行了。解析次數/執行次數
最后還有兩個解析時間的比例,這個對我們幫助也不是太大:
Parse?CPU?to?Parse?Elapsd?%:100×parse?time?cpu?/?parse?time?elapsed,即解析時間/解析時墻上壁鐘時間。
%?Non-Parse?CPU:計算公式:100×(1-(parse?time?cpu?/?CPU?used?by?this?session))?。表示了非解析時間在會話所占用CPU總時間的比例。如此值太低,表示解析消耗時間過多。
最后,還應該注意報告中的共享資料部分:
Shared?Pool?Statistics?????????Begin???End
???????????????????????????????------??------
?????????????Memory?Usage?%:???52.76???54.75
????%?SQL?with?executions>1:???65.90???77.17
%?Memory?for?SQL?w/exec>1:???86.34???92.30
1)?Memory?Usage?%:這一項資料雖和解析無關,不過它也是共享池資料的一部分,我們也在這里介紹一下。它是正在使用的共享池的百分率。這個數字應該穩定在75%至90%左右。就是我們不能讓共享池占用太多內存而又閑置著,這將帶來更多的管理負擔。大共享池的管理負擔也更大,如果你用不著這么大,還是調小點好。反過來,我們也不能讓共享池內存占用的太多而沒有一點的空余,這會使共享池內部的數據出現你不希望的老化。根據經驗,在通常的OLTP應用中,此數值應該在75%到略低于90%的范圍內。
2)?%?SQL?with?executions>1:此資料的計算公式是:100×(1-只執行一次的SQL數量?/?所有SQL數量),它是共享池中執行次數大于一次的SQL語句在所有SQL語句的百分比。這個數字當然越大越好。越大說明你的執行計劃共享的越有效。
3)?%?Memory?for?SQL?w/exec>1:此資料的計算公式是:100×(1-只執行一次的SQL所占內存?/?所有SQL所占內存)。此資料很好理解了,執行次數大于一次的語句所占內存與總語句所占內存的一個比例。
好了,STATSPACK中解析有關解析的資料就說的這里,軟、硬解析的比例,在一定程度上代表了你的庫緩存是否夠大。還有一點,如果庫緩存不夠大,DBA是不能直接調大庫緩存的,庫緩存是共享池的重要部分之一。我們可以通過調節共享池的大小,來改變庫緩存或共享池中其他部分的大小。
4.庫緩存命中率與V$librarycache視圖
在上面的Statspack報告中,已經提到了庫緩存命中率這個概念,下面我們用視圖說一下庫緩存的命中率,視圖中的信息比Statspack報告中的更詳細一些。我們可以通過V$librarycache視圖來查看庫緩存的一些情況。在介紹此視圖前,我們要先來介紹幾個有關庫緩存的概念。
(1)、庫緩存句柄和庫緩存內存塊。
每一個進入庫緩存的對象,在庫緩存中都被按照本身內容分割成多塊進行存貯,這就好像一只整雞被分割成雞腿、雞翅、雞爪等等。Oracle這樣做的目的是為了更靈活的內存管理,因為在內存尋找大塊連續的內存,總比尋找小塊連續內存更慢一些,這個我們在此處就不深入討論了。繼續剛才的話題,如果一個庫緩存對象(如一條SQL語句的執行計劃),它所占的內存被切割成4個小塊,它們分別被存放在庫緩存的各處,并且互不相連。為了將這4個小塊組合起來,Oracle另外這個庫緩存對象分配一小塊內存,這塊內存中存有其他4個小塊內存的地址,并且,還有一些有關此庫緩存對象的基本信息,如名字、類型等等,這塊內存就叫庫緩存對象句柄。
在訪問庫緩存對象時,比如軟解析時,要從庫緩存中讀取執行計劃。Oracle首先找到句柄,讀取句柄中的信息,這就叫做一次庫緩存Get。如果庫緩存中不包括對象的句柄信息,Oracle就要重新在庫緩存中分配內存、構造句柄,這就是庫緩存句柄未命中(Get?Miss)。相反,如果在庫緩存中找到了對象句柄,就是庫緩存句柄命中(Get?Hit)。硬解析時,就會發生Get?Miss。而軟解析則是Get?Hit。
在取出句柄中的其他內存塊地址后,每訪問一個內存塊,都叫做一次庫緩存Pin。如果相應的內存塊已經不在內存中了,這就是Pin?Miss,Pin的未命中。相反就是Pin?Hit。
當庫緩存中對象發生改變后,會引起其他一些對象的無效(Invalidation)。比如,你修改了一個表的結構,那么,選擇了這個表的SQL語句的執行計劃就會變的無效。其實大部分對表的DDL操作,都會造成相關SQL語句執行計劃的無效。就連Grant(授權)、Revoke(撤消權限)這樣看來跟執行計劃耗無關系的操作,都會引起無效。如果有一個表TAB1,你發布了?“Grant?某權限?on?tab1?to?某用戶”,或“revoke?某權限?on?tabl?from?某用戶”這樣的操作,那么,所有和TAB1表有關的執行計劃都將無效。無效之后,庫緩存對象除句柄外的所有內存塊,都將被清除。再使用到對象后,必須重新加載對象。如上例,如果你對TAB1使用了DDL語句,那么所有使用了TAB1表語句的執行計劃都將無效,你再使用這些語句時,就必須重新硬解析語句。
因此,使用DDL語句是需要注意的,如果沒有要求立即使用DDL,我們最好等到數據庫并不繁忙的時刻,再執行DDL。作為DBA,這一點一定要牢記,除非要求立即執行,否則等到數據庫并不繁忙時再執行任何DDL。
再補充一點,無效和庫緩存對象的老化并不一樣。老化是連句柄帶對象的所有內存塊都被清除出去了。而無效是只在庫緩存中保留對象句柄,但所有其他內存塊都被清除出去。對于無效的對象,當再次重新調用到它時,Oracle會再次將它調入到庫緩存中,這個操作被稱為Reload。一般說來,Reload與Get是無關的,因為Reload是在對象無效后才發生的,而對象的無效并不影響對象句柄。好,說了這么多,下面我們來看這些值的意義。
我們已經講述了Get、Pin與它們的命中、未命中,還有什么是Invalidation和Reload。在OLTP系統中,Get的命中率應該在90%之上。而Reload和Pin的次數的比值,應該小于1%。如果超出了這些數值范圍,就說明庫緩存的使用有問題。問題的原因主要有兩個,沒有使用綁定變量或是庫緩存太小。不過Reload和Pin的比例過高,也可能是在繁忙時執行了DDL所導致的。
另外,V$librarycache的第一列是NAMESPACE,也就是名稱空間。它相當于存儲在庫緩存中對象的類型。具體的名稱空間是什么意思呢?就是對象的名字所在的范圍,比如表和列的名字就不在一個范圍內。也就是說表名和列名可以重復,還有用戶名和表、列也不在同一范圍內,用戶名也可以和表、列名相同。我可以有個叫做AA的表,也可以有叫做AA的用戶。這兩個AA處在不同的范圍內,也就明它們的名稱空間不同。這就好像在北京有個電話號碼是12345678,在上海也有個12345678,這兩個電話號碼雖然相同,但不會引起問題,因為它們在不同的范圍內。這個范圍,也可以說是類型。
我顯示一下V$librarycache的所有行(列只有一部分):
SQL>?select?*?from?v$librarycache;
NAMESPACE?????????????GETS????GETHITS?GETHITRATIO???????PINS????PINHITS
---------------?----------?----------?-----------?----------?----------
SQL?AREA?????????????21811???????4149??.190225116?????120258?????105272
TABLE/PROCEDURE??????25152??????16372??.650922392??????60649??????46008
BODY??????????????????4360???????4098??.939908257???????5931???????5537
TRIGGER????????????????320????????251?????.784375???????1655???????1576
INDEX??????????????????453????????128??.282560706???????2065???????1531
CLUSTER????????????????755????????728??.964238411???????2339???????2296
OBJECT???????????????????0??????????0???????????1??????????0??????????0
PIPE?????????????????????0??????????0???????????1??????????0??????????0
JAVA?SOURCE??????????????0??????????0???????????1??????????0??????????0
JAVA?RESOURCE????????????0??????????0???????????1??????????0??????????0
JAVA?DATA????????????????0??????????0???????????1??????????0??????????0
可以看在庫緩存中共有11個名稱空間,我們基本上可以說庫緩存中有11種類型的信息。有一個非常重要的指標,就是庫緩存命中率,這個命中率就是庫緩存中所有對象的GETHITRAIO,它的計算方法如下:
select?1-sum(gethits)/sum(gets)?from?v$librarycache;
這個比率應該在90%以上。 還有一個比率也很重要,就是Reload和Pin的比值,這個比值應該小于1%。
如果沒有達到這些比例,解決方法很簡單,問題或者是SQL語句沒有共享,或者是共享池太小。
有關庫緩存命中率,也可以查看STATSPACK報告中的Library?Hit?%項。這個我們上面已經說過了。
三、Library?cache?lock、Library?cache?pin等待事件
我們再補充一點,SG中沒提,我們在調優的課程中,有很多內容將不再按SG順序講。當然我們也會補充很多實踐性比較強的內容。所以我們一定要多記才行。
等待事件在Oracle中無處不在,為了記錄這些數據,是損失了一些性能。但是你是想出了問題一愁么展好,還是可以利用等待事件或各種資料輕松的查找出問題在哪好呢?而且,這些等待事件和資料你即使不去用它,Oracle一樣會消耗CPU去記載它們。因此,一個好的DBA一定要對各種等待事件、資料了如指掌。下面,我們介紹兩個和庫緩存相關的等待事件,如題,就是Library?cache?lock和Library?cache?pin。
我們上面說到庫緩存中的對象在庫緩存中被切割成多個內存塊,另有一個對象句柄記錄了各個內存塊的地址和其他的一些信息。當你要修改句柄中的信息時,需要在句柄上加獨占鎖,而如果另一個進程恰好在這時要求讀、寫句柄中的信息,它就必須等待。此時的等待就被Oracle記入Library?cache?lock事件。而讀、寫對象內存塊也是無法同時進行的,有人如果正在寫,你的讀操作就必須等待,讀寫內存塊的等待事件就是Library?cache?pin。如果這兩個等待事件過多,同樣說明了庫緩存過小或沒有共享執行計劃?;蛘?#xff0c;當你在數據庫繁忙時使用DDL時,也會有這兩個等待事件。
四、庫緩存視圖
有一個視圖可以看到緩存在庫緩存中對象的信息,它是V$db_object_cache。
OWNER?:對象所有者
?NAME?:對象名
?DB_LINK:數據庫鏈接名
?NAMESPACE:名稱空間。如果是SQL游標,它的名稱空間是CURSOR。名稱空間和類型的意義是差不多的。除了CURSOR外,還有TABLE、INDEX等等。
?TYPE:類型
?SHARABLE_MEM:所占用的內存
?LOADS:對象被加載的次數
?EXECUTIONS:對象被執行次數
?LOCKS:正在鎖定對象的會話數
?PINS:正在Pin對象的會話數
?KEPT:對象是否用DBMS_SHARED_POOL.KEEP保持在共享池中
?CHILD_LATCH:對應的子閂
?INVALIDATIONS:對象的無效次數
五、OLAP和OLTP的區別
我們上面所說的庫緩存的調節適合于OLTP,但并不適合大部分的OLAP系統。什么是OLTP呢,它又叫聯機事務處理。各種網站、BBS,或者銀行的ATM機上的應用,銀行前臺電腦中的應用等等,這些都是OLTP型的應用。OLTP偏重于資料收集,但它不會對資料進行分析。OLAP,又叫聯機應用程序,它主要指哪些根據以往資料進行分析、處理,查找規律或預測趨勢的應用。像數據倉庫和DSS(企業決策系統)或數據挖掘型的應用,都是OLAP型應用程序。例如,不知道我們聽說過一個啤酒和尿褲的故事沒有。(在此處補充此故事)在這個故事中,最后分析出啤酒和尿褲有關聯的程序,就是一個OLAP應用。而沃爾瑪超市的收銀臺中的應用,就是OLTP應用。OLTP收集資料,OLAP分析處理。
OLTP的并發會話數可能非常多,但都是執行短小的事務或查詢,而且大多數語句都類似。因此,共享執行過的相似的語句,對OLTP是非常重要的。
而OLAP的并發會話可能很少,而且,以查詢為主。因為OLAP的主要任務就是分析數據。但OLAP的查詢往往需要執行很長時間。對于OLAP來說,共享語句的執行計劃是沒有必要的。因此,對于OLAP,共享池的大小可以盡量的小。
我們在講到后面的內容時,會講述更多OLTP和OLAP這二者的區別,和各種Oracle的特性分別是針對誰設計的。
總結
以上是生活随笔為你收集整理的Oracle性能优化 以及 库缓存命中率及等待事件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTML前端开发入门之表单标签/labe
- 下一篇: Git在同一台电脑上连接多个仓库