用數(shù)據(jù)庫(kù)框架控制開(kāi)發(fā)環(huán)境本文從數(shù)據(jù)庫(kù)管理員的角度,討論了保護(hù)數(shù)據(jù)庫(kù)的重要性。作者建議通過(guò)添加Java數(shù)據(jù)庫(kù)框架,在開(kāi)發(fā)人員和數(shù)據(jù)庫(kù)之間構(gòu)建一個(gè)經(jīng)過(guò)反復(fù)試驗(yàn)的穩(wěn)固的中間層,以降低風(fēng)險(xiǎn),并提供跟蹤及報(bào)告問(wèn)題的工具。 沒(méi)有什么比這樣一只Java開(kāi)發(fā)小組更影響數(shù)據(jù)庫(kù)性能的了:他們有一堆需求,又要使用數(shù)據(jù)庫(kù),但卻僅僅了解了一些Java數(shù)據(jù)庫(kù)連接(JDBC)的皮毛。連接會(huì)打開(kāi)并閑置好幾個(gè)小時(shí),一旦連接超時(shí),問(wèn)題就會(huì)扔給數(shù)據(jù)庫(kù)管理員(DBA)。未關(guān)閉的語(yǔ)句和占用系統(tǒng)資源的結(jié)果將是數(shù)據(jù)庫(kù)管理員頭痛的問(wèn)題。編程人員使用含糊的JDBC方法和動(dòng)態(tài)SQL導(dǎo)致性能低下。 因此,本文討論了如何使用Java數(shù)據(jù)庫(kù)框架幫助數(shù)據(jù)庫(kù)遠(yuǎn)離隨意操作的開(kāi)發(fā)人員。它提供了連接池和管理、跟蹤及報(bào)告JDBC對(duì)象、有選擇地刪除性能低下的結(jié)構(gòu)和方法。目的在于防止開(kāi)發(fā)人員影響性能、避免沾上常見(jiàn)的不良開(kāi)發(fā)習(xí)慣,并且在無(wú)法防止這類活動(dòng)的情況下提供跟蹤機(jī)制。這樣一來(lái),數(shù)據(jù)庫(kù)管理員就可以找到問(wèn)題的根源,在系統(tǒng)進(jìn)入生產(chǎn)環(huán)境之前改正問(wèn)題。使用Java數(shù)據(jù)庫(kù)框架的另一個(gè)目的在于,讓一切開(kāi)發(fā)工作都保持簡(jiǎn)單,那樣開(kāi)發(fā)人員就可以盡快熟悉情況。 與任何框架一樣,Java數(shù)據(jù)庫(kù)框架的目的在于隱藏復(fù)雜性,并為處理復(fù)雜任務(wù)提供一套標(biāo)準(zhǔn)操作程序。同樣重要的一個(gè)方面是讓執(zhí)行任務(wù)的方式具有一致性。這可以改進(jìn)封裝、大大提高代碼的可維護(hù)性。只要設(shè)想一下:假如每個(gè)人都構(gòu)建各自的類和方法來(lái)建立數(shù)據(jù)庫(kù)連接,勢(shì)必會(huì)導(dǎo)致混亂、無(wú)序的局面。除了數(shù)據(jù)庫(kù)外,框架通常適用的一些方面包括:進(jìn)程間通信、多線程管理和圖形用戶界面(GUI)標(biāo)準(zhǔn)。 本文描述的框架旨在供所有中間件開(kāi)發(fā)人員使用,它在由數(shù)據(jù)庫(kù)開(kāi)發(fā)商提供的實(shí)際的JDBC實(shí)現(xiàn)上添加了一層(如圖1)。 ![]() JDBC問(wèn)題和陷阱 記得下面這一點(diǎn)很重要:JDBC是數(shù)據(jù)庫(kù)開(kāi)發(fā)商提供的實(shí)現(xiàn),但不是所有的實(shí)現(xiàn)都是相同的。但是,在數(shù)據(jù)庫(kù)框架讓,開(kāi)發(fā)人員可以在某種程度上讓它們相同。在個(gè)別情況下,同一家開(kāi)發(fā)商提供的JDBC驅(qū)動(dòng)程序的各個(gè)版本之間會(huì)存在差異。不同開(kāi)發(fā)商提供的JDBC驅(qū)動(dòng)程序免不了總是會(huì)存在差異。差異通常出現(xiàn)在以下幾方面:連接管理;存儲(chǔ)過(guò)程和返回ResultSets;處理ResultSets;元數(shù)據(jù)支持;因語(yǔ)句和ResultSets未結(jié)束而消耗資源;連接未關(guān)閉帶來(lái)的問(wèn)題;性能異常及實(shí)現(xiàn)緩慢;數(shù)據(jù)庫(kù)優(yōu)化器從一個(gè)版本到下一個(gè)版本所出現(xiàn)的變化;數(shù)據(jù)庫(kù)從一個(gè)版本到下一個(gè)版本添加了新特性。 筆者曾有幸參于來(lái)自Sybase和Oracle的JDBC實(shí)現(xiàn),它們采用的方法形成了鮮明對(duì)比。筆者常開(kāi)玩笑說(shuō),Oracle好比是“父親”,Sybase好比是“母親”。如果你在冬天沒(méi)穿衣服就出去,母親會(huì)叫你停下來(lái),穿上衣服,免得感冒;而父親會(huì)一言不發(fā)地看著,覺(jué)得要是天氣寒冷,你會(huì)曉得自己添衣服。Sybase驅(qū)動(dòng)程序在連接、語(yǔ)句和結(jié)果集管理方面可以為開(kāi)發(fā)人員做大量工作;而Oracle驅(qū)動(dòng)程序只會(huì)做開(kāi)發(fā)人員讓它做的那些事情。如果開(kāi)發(fā)人員沒(méi)有結(jié)束語(yǔ)句,它恐怕不會(huì)自動(dòng)結(jié)束,也肯定不會(huì)把會(huì)話、進(jìn)程及打開(kāi)的游標(biāo)清理干凈。這里不會(huì)去研究哪個(gè)方向是正確的,我們只是為框架添加了代碼,讓它們看上去很相似。 圖2顯示了框架示例,旨在處理上面討論的JDBC問(wèn)題。它還在數(shù)據(jù)庫(kù)上提供了抽象層,那樣開(kāi)發(fā)人員可以更迅速、更安全地訪問(wèn)數(shù)據(jù)庫(kù)。CWDatabase類負(fù)責(zé)管理開(kāi)發(fā)人員的所有訪問(wèn),它利用CWConnectionPool管理連接、利用CWSqlRepository管理SQL字符串。較低級(jí)的Connection和Statement類都進(jìn)行了封裝,以便提供跟蹤機(jī)制,并保證連接重新簽入到連接池后,所有語(yǔ)句和結(jié)果集都已關(guān)閉。下文討論了這些類,隨后討論了比較高級(jí)的框架特性,用于跟蹤執(zhí)行性能、限制JDBC特性及報(bào)告連接池。下面的所有框架類都以代表筆者所在公司CodeWorks Software的“CW”開(kāi)頭,這樣它們很容易識(shí)別。 ![]() 重要的類 數(shù)據(jù)庫(kù)接口類:CWDatabase和CWParamList 為開(kāi)發(fā)人員添加用于數(shù)據(jù)庫(kù)訪問(wèn)的一個(gè)簡(jiǎn)單類。通過(guò)創(chuàng)建框架類的實(shí)例,他們可以獲得運(yùn)行SQL命令及存儲(chǔ)過(guò)程的連接及簡(jiǎn)單方法。異常處理得到了適當(dāng)?shù)奶幚砑皥?bào)告;通過(guò)使用CWParamList允許用戶創(chuàng)建參數(shù)列表,數(shù)據(jù)庫(kù)管理員就可以牢牢地控制Java數(shù)據(jù)類型及它們?nèi)绾谓壎ǖ綌?shù)據(jù)庫(kù)中的基本數(shù)據(jù)類型。目的在于絕對(duì)不允許用戶直接控制jdbc.Connection實(shí)例。牢牢獲得這種控制權(quán)的另一個(gè)好處是,通??梢垣@得很高的數(shù)據(jù)速率,因?yàn)閿?shù)據(jù)庫(kù)管理員可以控制數(shù)據(jù)庫(kù)訪問(wèn)。 public class CWDatabase { Connection m_conn = null; public CWDatabase() { m_conn = CWConnectionPool.checkOut(); } public int executeUpdate(String queryName, CWParamList plist) public int executeUpdate(String queryName) public ResultSet executeQuery(String queryName, CWParamList plist) public ResultSet executeQuery(String queryName) private void processException(String msg, Throwable ex) } 有了上述這個(gè)類,用戶可以運(yùn)行如下的簡(jiǎn)單查詢: public void updateSensorType() { CWDatabase theDB = null; try { // 創(chuàng)建數(shù)據(jù)庫(kù)實(shí)例和參數(shù)列表實(shí)例 theDB = new CWDatabase(); CWParamList plist = new CWParamList(); // 添加參數(shù) plist.addParameter(1,"TYPE1"); plist.addParameter(2,1); plist.addParameter(3,"ACT"); // 執(zhí)行更新 int numupdate = theDB.executeUpdate("sensor.updateSensorType",plist); } catch( Exception exception ) { CWExceptionReporter.write(this,CWExceptionReporter.FATAL,"Error updating sensor type."); } finally { theDB.close(); } return; } // End方法 連接池:CWConnectionPool 建立連接很費(fèi)資源,所以通過(guò)重復(fù)使用連接,就可以避免每次重新建立連接帶來(lái)的成本。系統(tǒng)啟動(dòng)時(shí),可以為連接池提供可隨時(shí)使用的幾個(gè)連接。用戶創(chuàng)建數(shù)據(jù)庫(kù)接口類的實(shí)例后,連接就會(huì)簽出。用戶調(diào)用關(guān)閉命令后,連接重新簽入,供其他用戶使用。 雖然重復(fù)使用連接是連接池的主要目的,但還有許多其他好處。因?yàn)樗羞B接都在一個(gè)地方加以管理及創(chuàng)建,所以數(shù)據(jù)庫(kù)管理員就能夠嚴(yán)格管理隔離級(jí)別和數(shù)據(jù)庫(kù)選項(xiàng)。SQL Anywhere在這方面的例子包括:DELAYED_COMMITS、ISOLATION_LEVEL和 COOPERATIVE_COMMITS,以及面向原始設(shè)備制造商(OEM)版本的軟件的授權(quán)代碼方面的設(shè)置。 不過(guò),筆者在建立連接池時(shí)發(fā)現(xiàn)了一個(gè)問(wèn)題,它們會(huì)留下一些“行李(baggage)”。這樣一來(lái),多次重復(fù)使用會(huì)漸漸減慢連接速度。而且,筆者根本查不出這個(gè)問(wèn)題的根源,不過(guò)懷疑它與PreparedStatements、ResultSets或者當(dāng)時(shí)出現(xiàn)的其他某種內(nèi)部跟蹤機(jī)制有關(guān)。鑒于所有連接都由連接池管理,這樣就有可能跟蹤連接存在了多久;重新簽入后,可以“刷新”連接。通過(guò)在過(guò)了限定時(shí)間后丟棄連接,就可以更好地維持很高的性能比率。 這種嚴(yán)密跟蹤機(jī)制的另一個(gè)好處就是,還可以監(jiān)控誰(shuí)把連接簽出了、時(shí)間有多久。長(zhǎng)時(shí)間保持的連接,尤其是作為成員變量,可能會(huì)導(dǎo)致問(wèn)題。如果連接好幾個(gè)小時(shí)都處于休眠狀態(tài),就會(huì)超時(shí),進(jìn)而導(dǎo)致問(wèn)題。有了這種額外的跟蹤機(jī)制,數(shù)據(jù)庫(kù)管理員就可以報(bào)告誰(shuí)擁有哪個(gè)連接、打開(kāi)狀態(tài)保持了多久。如果知道簽出問(wèn)題連接的那一行代碼,就能找到相應(yīng)的開(kāi)發(fā)人員,告訴他如何使用框架。因?yàn)楦欉B接需要一定開(kāi)銷,筆者在編寫(xiě)框架時(shí)在默認(rèn)狀態(tài)下禁用了這項(xiàng)特性,不過(guò)測(cè)試過(guò)程中可以啟用它。 public class CWConnectionPool { // 維護(hù)閑置及簽出列表上的連接 private static ArrayList m_freePool = null; private static HashMap m_outPool = null; public CWConnectionPool() { m_freePool = new ArrayList(); m_outPool = new HashMap(); } public static void initialize() public static synchronized Connection checkOut() public static synchronized void checkIn(Connection conn) public static void discardConnection(Connection conn) private static CWConnection createConnection() public static reportConnectionPool() } SQL存儲(chǔ)庫(kù):CWSqlRepository SQL語(yǔ)句最好保存在不同文件中,那樣不必重新編譯代碼就可以修改語(yǔ)句。譬如說(shuō),如果發(fā)現(xiàn)了某個(gè)性能問(wèn)題,經(jīng)過(guò)分析,發(fā)現(xiàn)是數(shù)據(jù)庫(kù)優(yōu)化器選錯(cuò)了索引,這時(shí)就很容易添加SQL提示。為了管理SQL語(yǔ)句,筆者使用了CWSQLRepository類。該存儲(chǔ)庫(kù)還允許重復(fù)使用代碼;又因?yàn)樗蠸QL語(yǔ)句都在同一個(gè)地方,數(shù)據(jù)庫(kù)管理員就更容易找到可能受模式改變影響的所有語(yǔ)句。 public class CWSqlRepository { private static CWSqlRepository m_SQLRepository; private static Properties m_SQLrepositoryTable; public static void initialize() { if (m_SQLRepository == null) m_SQLRepository = new CWSqlRepository(); return; } private static void loadSQLrepository(String sqlfile) public static String getSQLString(String tag) } 封裝JDBC類 說(shuō)到簡(jiǎn)化數(shù)據(jù)庫(kù)訪問(wèn),通過(guò)提供上面討論的那幾個(gè)簡(jiǎn)單類,就能得到很大成效。不過(guò)說(shuō)到消除JDBC實(shí)現(xiàn)在較低層面上的差異,從事重復(fù)工作毫無(wú)意義。只要封裝JDBC類,就可以處理問(wèn)題、添加功能。JDBC文檔齊全,開(kāi)發(fā)人員很熟悉它,數(shù)據(jù)庫(kù)管理員也是一樣。另外,很容易教人學(xué)會(huì),并提供合理使用的示例。通過(guò)創(chuàng)建封裝器類,數(shù)據(jù)庫(kù)管理員可以添加自己需要的任何跟蹤、定時(shí)及報(bào)告機(jī)制,還可以消除差異。只有在極少數(shù)情況下,開(kāi)發(fā)人員才真正知道自己在使用Connection.prepareStatement()的框架實(shí)現(xiàn),而不是實(shí)際的實(shí)現(xiàn)。 CWConnection 要封裝的最重要的一個(gè)類是java.sql.Connection。數(shù)據(jù)庫(kù)管理員可以在這里跟蹤某連接簽出了多久,并維護(hù)所有已創(chuàng)建語(yǔ)句的列表。為了調(diào)試,數(shù)據(jù)庫(kù)管理員可以維護(hù)堆棧跟蹤信息(stack trace)。這樣在建立連接后,一旦發(fā)現(xiàn)“連接濫用”,就能更準(zhǔn)確地找到建立該連接的代碼,譬如說(shuō),連接簽出時(shí)間超過(guò)規(guī)定。 public class CWConnection implements Connection { // 連接的基本信息 private Connection _conn = null; //封裝的java.sql.Connection private int _connNum ; // 跟蹤號(hào)碼 private long _createTime; // 設(shè)定時(shí)間 private ArrayList _stmtTracker; // 跟蹤語(yǔ)句 public int getConnectionNum() public int getElapseTime() public void closeStatements() private ArrayList getStatementTracker() private void clearStatementTracker() String reportConnnection() // 封裝的JDBC方法 public Statement createStatement() throws SQLException { CWStatement stmt = new CWStatement(_conn.createStatement()); _stmtTracker.add(stmt); return stmt; } } CWStatement、CWPreparedStatement和CWCallableStatement 數(shù)據(jù)庫(kù)管理員可以編寫(xiě)這樣的框架:很少允許開(kāi)發(fā)人員可以控制Statements、CallableStatements和PreparedStatements。封裝這些類的主要原因是可以跟蹤時(shí)間設(shè)定,如果返回結(jié)果集的話,還可以跟蹤ResultSet實(shí)例。雖然在下面討論了串行化的結(jié)果集,但筆者并不建議把結(jié)果集隱藏起來(lái),不讓開(kāi)發(fā)人員看到,因?yàn)榻涌诜矫娴奈臋n很齊全。主要的濫用現(xiàn)象就是讓結(jié)果集打開(kāi)著,不過(guò)對(duì)此進(jìn)行跟蹤卻相當(dāng)簡(jiǎn)單。連接簽入后,所有語(yǔ)句都被關(guān)閉,每個(gè)語(yǔ)句保證結(jié)果集被關(guān)閉。筆者仍封裝了ResultSet,但主要目的是消除性能低下的方法,那樣開(kāi)發(fā)人員就沒(méi)法用它們。稍后會(huì)討論這個(gè)話題。 public class CWStatement implements Statement { // 語(yǔ)句的基本信息 private Statement m_stmt = null; //封裝的java.sql.Statement private int m_stmtNum; // 跟蹤號(hào)碼 private long m_createTime; // 設(shè)定時(shí)間 protected ResultSet m_rsTracker; // 跟蹤結(jié)果集 private String m_sqlTracker; // 隨該語(yǔ)句一起發(fā)出的Sql public String reportStatement() public CWStatement( Statement stmt, int stmtNum) { m_stmt = stmt; m_stmtNum = stmtNum; m_createTime = System.currentTimeMillis(); m_sqlTracker = null; m_rsTracker = null; } public ResultSet executeQuery(String sql) throws SQLException { CWResultSet rs = new CWResultSet(m_stmt.executeQuery(sql)); m_rsTracker = rs; m_sqlTracker = sql; return rs; } } 框架的先進(jìn)思想 上面討論的話題集中于簡(jiǎn)化數(shù)據(jù)庫(kù)接口、連接管理,并提供防范常見(jiàn)JDBC問(wèn)題的方法。接下來(lái)會(huì)介紹框架的附加部分,它們?yōu)楸O(jiān)控及控制使用數(shù)據(jù)庫(kù)的開(kāi)發(fā)人員提供了更有效的機(jī)制,包括:解決結(jié)果集的問(wèn)題、限制性能低下的操作、監(jiān)控SQL性能。 CWResultSetSerialized 許多Java編程人員沒(méi)有認(rèn)識(shí)到(或者忘了)ResultSets實(shí)際上是數(shù)據(jù)庫(kù)游標(biāo)。它們傳遞引用、把它們存儲(chǔ)為成員變量,往往從不關(guān)閉,因而占用了數(shù)據(jù)庫(kù)資源。為了消除所有風(fēng)險(xiǎn),可利用JDBC結(jié)果集來(lái)創(chuàng)建串行化的結(jié)果集。這還可以讓結(jié)果集通過(guò)遠(yuǎn)程方法調(diào)用(RMI)在進(jìn)程之間發(fā)送。 在極端情況下,引起阻塞問(wèn)題的結(jié)果集頻頻傳送,以至筆者查不到該在什么地方關(guān)閉它。一旦用串行化的結(jié)果集取而代之,就能關(guān)閉實(shí)際的ResultSet,所有問(wèn)題都立馬消失了。串行化結(jié)果集的任何實(shí)現(xiàn)都需要限制可以創(chuàng)建的行數(shù),因?yàn)?00萬(wàn)行的串行化結(jié)果集會(huì)引起性能問(wèn)題。筆者開(kāi)始限制在5000行。 下面的代碼表明了結(jié)果集管理不善,因?yàn)樗鼈鞯搅朔椒ㄍ饷妗,F(xiàn)在很難知道它是不是被關(guān)閉了,因?yàn)榉椒ㄖ挥谐霈F(xiàn)了錯(cuò)誤才關(guān)閉數(shù)據(jù)庫(kù)實(shí)例。代碼可以使用,不過(guò),要是結(jié)果集在調(diào)用方法里面沒(méi)有關(guān)閉,連接會(huì)處于簽出狀態(tài),游標(biāo)仍然是打開(kāi)的。 public ResultSet getAllSensors() { CWDatabase theDB = null; ResultSet rs = null; try { // 創(chuàng)建數(shù)據(jù)庫(kù)實(shí)例和參數(shù)列表實(shí)例 theDB = new CWDatabase(); // 執(zhí)行查詢 rs = theDB.executeQuery("sensor.getAllSensors"); } catch( Exception exception ) { CWExceptionReporter.write(this,CWExceptionReporter.FATAL,"Error during query: " + " sensor.getAllSensors "); theDB.close(); } return rs; } // End方法 因?yàn)橛袝r(shí)在開(kāi)發(fā)周期很晚時(shí)才發(fā)現(xiàn)這類問(wèn)題,因而無(wú)法通過(guò)改寫(xiě)方法來(lái)解決,可以通過(guò)以下辦法解決問(wèn)題:傳回串行化的結(jié)果集,通過(guò)finally塊關(guān)閉數(shù)據(jù)庫(kù)實(shí)例,從而保證一切都正常關(guān)閉。筆者仍認(rèn)為,數(shù)據(jù)庫(kù)管理員應(yīng)當(dāng)給引起問(wèn)題的工程師出難題,不過(guò)系統(tǒng)代碼凍結(jié)前一天不是改變大量代碼的時(shí)候。所作的變化用下面的黑體字表明: public ResultSet getAllSensors() { CWDatabase theDB = null; CWResultSetSerialized rss = null; try { // 創(chuàng)建數(shù)據(jù)庫(kù)實(shí)例和參數(shù)列表實(shí)例 theDB = new CWDatabase(); // 執(zhí)行更新 ResultSet rs = theDB.executeQuery("sensor.getAllSensors"); rss = new ResultSetSerialized(rs); } catch( Exception exception ) { CWExceptionReporter.write(this,CWExceptionReporter.FATAL,"Error during query: " + " sensor.getAllSensors "); } finally { theDB.close(); } // 返回串行化結(jié)果集 return rss; } // End方法 性能級(jí)別 因性能需求不同,數(shù)據(jù)庫(kù)管理員的要求可能大不相同,有的是“只允許速度最快的數(shù)據(jù)庫(kù)訪問(wèn)”,有的是“我不在乎訪問(wèn)速度,只要可以使用任何特性”。大部分人介于兩者之間。 能夠“關(guān)閉”已知性能低下的JDBC方法大有幫助。譬如說(shuō),使用ResultSet.update()比使用不同的Statement.execute()來(lái)執(zhí)行同樣的更新慢得多。允許用戶使用rs.last()等方法返回不是“只能向前移動(dòng)的”結(jié)果集也很慢。筆者使用三個(gè)基本的性能級(jí)別: ● 級(jí)別1:開(kāi)發(fā)人員不可以訪問(wèn)連接,也無(wú)法發(fā)出動(dòng)態(tài)SQL。所有性能低下的方法都被關(guān)閉,包括結(jié)果集更新和元數(shù)據(jù)訪問(wèn)。 ● 級(jí)別2:允許動(dòng)態(tài)查詢,但其他所有性能低下的方法仍然受到限制。筆者的架構(gòu)就使用這種默認(rèn)值。 ● 級(jí)別3:全面的JDBC訪問(wèn),沒(méi)有任何限制。 關(guān)閉JDBC特性后,筆者建議發(fā)出異常,這可以解釋特性已被關(guān)閉,需要聯(lián)系數(shù)據(jù)庫(kù)管理員。在開(kāi)發(fā)期間,數(shù)據(jù)庫(kù)管理員可以決定是否真正需要該特性,并確定要不要重新添加到框架上,或者更改該特性的性能級(jí)別。 public class CWResultSet implements ResultSet { private ResultSet m_rs; private long m_createTime; private int m_perf_level; public CWResultSet( ResultSet rs, perf_level) { m_rs = rs; m_createTime = System.currentTimeMillis(); m_perf_level = perf_level; } public void updateRow() throws SQLException { if(perf_level < CWDatabase.HIGH_PERF_ONLY) { CWExceptionReporter.write(this,CWExceptionReporter.ERROR, "Use of method prohibited due to slow performance, “+ " as per the DBA."); } m_rs.updateRow(); } } 為數(shù)據(jù)庫(kù)管理員想要開(kāi)啟或者禁用的每一部分JDBC功能賦予名字,并且利用屬性文件控制這些值相當(dāng)簡(jiǎn)單。不過(guò),筆者發(fā)現(xiàn)自己不需要這種靈活性,于是仍采用了基本級(jí)別。 SQL性能跟蹤 雖然SQL Anywhere提供了監(jiān)控查詢的功能,但筆者還是提供了語(yǔ)句定時(shí)和報(bào)告機(jī)制。這樣數(shù)據(jù)庫(kù)管理員可以對(duì)某個(gè)SQL語(yǔ)句及所有語(yǔ)句開(kāi)啟跟蹤機(jī)制,或者設(shè)定時(shí)間閾值,報(bào)告超過(guò)給定閾值的查詢。筆者還考慮了在結(jié)果集層面的定時(shí),那樣就可以確認(rèn)超過(guò)閾值的結(jié)果集。這會(huì)有所幫助,因?yàn)檎Z(yǔ)句定時(shí)執(zhí)行只提供返回首行的所用時(shí)間,除非語(yǔ)句里面有“按××排序”或者類似子句。跟連接跟蹤的情況很相似,筆者在默認(rèn)情況下關(guān)閉了這項(xiàng)功能,在測(cè)試階段加以利用。 public int executeUpdate(String queryName) { private long startTime; < snip > // 執(zhí)行更新 startQueryTimer(); int numupdate =theDB.executeUpdate queryName,plist); stopQueryTimer(queryName); } 如果查詢跟蹤機(jī)制開(kāi)啟,查詢時(shí)間就會(huì)記錄到數(shù)據(jù)庫(kù)里面。 (沈建苗 編譯) (計(jì)算機(jī)世界報(bào) 2006年10月16日 第40期 B31、B32) |
|
來(lái)自: 鈴兒響叮當(dāng) > 《項(xiàng)目管理》