coolfiry

          認(rèn)認(rèn)真真做人,兢兢業(yè)業(yè)做事!
          posts - 39, comments - 17, trackbacks - 0, articles - 0

          從LiveJournal后臺發(fā)展看大規(guī)模網(wǎng)站性能優(yōu)化方法

          一、LiveJournal發(fā)展歷程

          LiveJournal是99年始于校園中的項目,幾個人出于愛好做了這樣一個應(yīng)用,以實現(xiàn)以下功能:

          • 博客,論壇
          • 社會性網(wǎng)絡(luò),找到朋友
          • 聚合,把朋友的文章聚合在一起

          LiveJournal采用了大量的開源軟件,甚至它本身也是一個開源軟件。

          在上線后,LiveJournal實現(xiàn)了非常快速的增長:

          • 2004年4月份:280萬注冊用戶。
          • 2005年4月份:680萬注冊用戶。
          • 2005年8月份:790萬注冊用戶。
          • 達(dá)到了每秒鐘上千次的頁面請求及處理。
          • 使用了大量MySQL服務(wù)器。
          • 使用了大量通用組件。

          二、LiveJournal架構(gòu)現(xiàn)狀概況

          livejournal_backend.png

          三、從LiveJournal發(fā)展中學(xué)習(xí)

           

          LiveJournal從1臺服務(wù)器發(fā)展到100臺服務(wù)器,這其中經(jīng)歷了無數(shù)的傷痛,但同時也摸索出了解決這些問題的方法,通過對LiveJournal的學(xué)習(xí),可以讓我們避免LJ曾經(jīng)犯過的錯誤,并且從一開始就對系統(tǒng)進(jìn)行良好的設(shè)計,以避免后期的痛苦。

          下面我們一步一步看LJ發(fā)展的腳步。

          1、一臺服務(wù)器

          一 臺別人捐助的服務(wù)器,LJ最初就跑在上面,就像Google開始時候用的破服務(wù)器一樣,值得我們尊敬。這個階段,LJ的人以驚人的速度熟悉的Unix的操 作管理,服務(wù)器性能出現(xiàn)過問題,不過還好,可以通過一些小修小改應(yīng)付過去。在這個階段里L(fēng)J把CGI升級到了FastCGI。

          最終問題出現(xiàn)了,網(wǎng)站越來越慢,已經(jīng)無法通過優(yōu)過化來解決的地步,需要更多的服務(wù)器,這時LJ開始提供付費服務(wù),可能是想通過這些錢來購買新的服務(wù)器,以解決當(dāng)時的困境。
          毫無疑問,當(dāng)時LJ存在巨大的單點問題,所有的東西都在那臺服務(wù)器的鐵皮盒子里裝著。

          LJ-backend-7.png

          2、兩臺服務(wù)器

          用付費服務(wù)賺來的錢LJ買了兩臺服務(wù)器:一臺叫做Kenny的Dell 6U機器用于提供Web服務(wù),一臺叫做Cartman的Dell 6U服務(wù)器用于提供數(shù)據(jù)庫服務(wù)。

          LJ-backend-8.png

          LJ有了更大的磁盤,更多的計算資源。但同時網(wǎng)絡(luò)結(jié)構(gòu)還是非常簡單,每臺機器兩塊網(wǎng)卡,Cartman通過內(nèi)網(wǎng)為Kenny提供MySQL數(shù)據(jù)庫服務(wù)。

          暫時解決了負(fù)載的問題,新的問題又出現(xiàn)了:

          • 原來的一個單點變成了兩個單點。
          • 沒有冷備份或熱備份。
          • 網(wǎng)站速度慢的問題又開始出現(xiàn)了,沒辦法,增長太快了。
          • Web服務(wù)器上CPU達(dá)到上限,需要更多的Web服務(wù)器。

          3、四臺服務(wù)器

          又買了兩臺,Kyle和Stan,這次都是1U的,都用于提供Web服務(wù)。目前LJ一共有3臺Web服務(wù)器和一臺數(shù)據(jù)庫服務(wù)器。這時需要在3臺Web服務(wù)器上進(jìn)行負(fù)載均橫。

          LJ-backend-9.png

          LJ把Kenny用于外部的網(wǎng)關(guān),使用mod_backhand進(jìn)行負(fù)載均橫。

          然后問題又出現(xiàn)了:

          • 單點故障。數(shù)據(jù)庫和用于做網(wǎng)關(guān)的Web服務(wù)器都是單點,一旦任何一臺機器出現(xiàn)問題將導(dǎo)致所有服務(wù)不可用。雖然用于做網(wǎng)關(guān)的Web服務(wù)器可以通過保持心跳同步迅速切換,但還是無法解決數(shù)據(jù)庫的單點,LJ當(dāng)時也沒做這個。
          • 網(wǎng)站又變慢了,這次是因為IO和數(shù)據(jù)庫的問題,問題是怎么往應(yīng)用里面添加數(shù)據(jù)庫呢?

          4、五臺服務(wù)器

          又買了一臺數(shù)據(jù)庫服務(wù)器。在兩臺數(shù)據(jù)庫服務(wù)器上使用了數(shù)據(jù)庫同步(Mysql支持的Master-Slave模式),寫操作全部針對主數(shù)據(jù)庫(通過Binlog,主服務(wù)器上的寫操作可以迅速同步到從服務(wù)器上),讀操作在兩個數(shù)據(jù)庫上同時進(jìn)行(也算是負(fù)載均橫的一種吧)。

          LJ-backend-10.png

          實現(xiàn)同步時要注意幾個事項:

          • 讀操作數(shù)據(jù)庫選擇算法處理,要選一個當(dāng)前負(fù)載輕一點的數(shù)據(jù)庫。
          • 在從數(shù)據(jù)庫服務(wù)器上只能進(jìn)行讀操作
          • 準(zhǔn)備好應(yīng)對同步過程中的延遲,處理不好可能會導(dǎo)致數(shù)據(jù)庫同步的中斷。只需要對寫操作進(jìn)行判斷即可,讀操作不存在同步問題。

          5、更多服務(wù)器

          有錢了,當(dāng)然要多買些服務(wù)器。部署后快了沒多久,又開始慢了。這次有更多的Web服務(wù)器,更多的數(shù)據(jù)庫服務(wù)器,存在 IO與CPU爭用。于是采用了BIG-IP作為負(fù)載均衡解決方案。

          LJ-backend-11.png

          6、現(xiàn)在我們在哪里:

          LJ-backend-1.png

          現(xiàn)在服務(wù)器基本上夠了,但性能還是有問題,原因出在架構(gòu)上。

          數(shù)據(jù)庫的架構(gòu)是最大的問題。由于增加的數(shù)據(jù)庫都是以Slave模式添加到應(yīng)用內(nèi),這樣唯一的好處就是將讀操作分布到了多臺機器,但這樣帶來的后果就是寫操作被大量分發(fā),每臺機器都要執(zhí)行,服務(wù)器越多,浪費就越大,隨著寫操作的增加,用于服務(wù)讀操作的資源越來越少。

          LJ-backend-2.png

          由一臺分布到兩臺

          LJ-backend-3.png

          最終效果

          現(xiàn)在我們發(fā)現(xiàn),我們并不需要把這些數(shù)據(jù)在如此多的服務(wù)器上都保留一份。服務(wù)器上已經(jīng)做了RAID,數(shù)據(jù)庫也進(jìn)行了備份,這么多的備份完全是對資源的浪費,屬于冗余極端過度。那為什么不把數(shù)據(jù)分布存儲呢?

          問題發(fā)現(xiàn)了,開始考慮如何解決?,F(xiàn)在要做的就是把不同用戶的數(shù)據(jù)分布到不同的服務(wù)器上進(jìn)行存儲,以實現(xiàn)數(shù)據(jù)的分布式存儲,讓每臺機器只為相對固定的用戶服務(wù),以實現(xiàn)平行的架構(gòu)和良好的可擴展性。

          為 了實現(xiàn)用戶分組,我們需要為每一個用戶分配一個組標(biāo)記,用于標(biāo)記此用戶的數(shù)據(jù)存放在哪一組數(shù)據(jù)庫服務(wù)器中。每組數(shù)據(jù)庫由一個master及幾個slave 組成,并且slave的數(shù)量在2-3臺,以實現(xiàn)系統(tǒng)資源的最合理分配,既保證數(shù)據(jù)讀操作分布,又避免數(shù)據(jù)過度冗余以及同步操作對系統(tǒng)資源的過度消耗。

          LJ-backend-4.png

          由一臺(一組)中心服務(wù)器提供用戶分組控制。所有用戶的分組信息都存儲在這臺機器上,所有針對用戶的操作需要先查詢這臺機器得到用戶的組號,然后再到相應(yīng)的數(shù)據(jù)庫組中獲取數(shù)據(jù)。

          這樣的用戶架構(gòu)與目前LJ的架構(gòu)已經(jīng)很相像了。

          在具體的實現(xiàn)時需要注意幾個問題:

          • 在數(shù)據(jù)庫組內(nèi)不要使用自增ID,以便于以后在數(shù)據(jù)庫組之間遷移用戶,以實現(xiàn)更合理的I/O,磁盤空間及負(fù)載分布。
          • 將userid,postid存儲在全局服務(wù)器上,可以使用自增,數(shù)據(jù)庫組中的相應(yīng)值必須以全局服務(wù)器上的值為準(zhǔn)。全局服務(wù)器上使用事務(wù)型數(shù)據(jù)庫InnoDB。
          • 在數(shù)據(jù)庫組之間遷移用戶時要萬分小心,當(dāng)遷移時用戶不能有寫操作。

          7、現(xiàn)在我們在哪里

          LJ-backend-5.png

          問題:

          • 一個全局主服務(wù)器,掛掉的話所有用戶注冊及寫操作就掛掉。
          • 每個數(shù)據(jù)庫組一個主服務(wù)器,掛掉的話這組用戶的寫操作就掛掉。
          • 數(shù)據(jù)庫組從服務(wù)器掛掉的話會導(dǎo)致其它服務(wù)器負(fù)載過大。

          對于Master-Slave模式的單點問題,LJ采取了Master-Master模式來解決。所謂Master-Master實際上是人工實現(xiàn)的,并不是由MySQL直接提供的,實際上也就是兩臺機器同時是Master,也同時是Slave,互相同步。

          Master-Master實現(xiàn)時需要注意:

          • 一個Master出錯后恢復(fù)同步,最好由服務(wù)器自動完成。
          • 數(shù)字分配,由于同時在兩臺機器上寫,有些ID可能會沖突。

          解決方案:

          • 奇偶數(shù)分配ID,一臺機器上寫奇數(shù),一臺機器上寫偶數(shù)
          • 通過全局服務(wù)器進(jìn)行分配(LJ采用的做法)。

           

          Master-Master模式還有一種用法,這種方法與前一種相比,仍然保持兩臺機器的同步,但只有一臺機器提供服務(wù)(讀和寫),在每天晚上的時候進(jìn)行輪換,或者出現(xiàn)問題的時候進(jìn)行切換。

          8、現(xiàn)在我們在哪里

          LJ-backend-6.png

          現(xiàn)在插播一條廣告,MyISAM VS InnoDB。

          使用InnoDB:

          • 支持事務(wù)
          • 需要做更多的配置,不過值得,可以更安全的存儲數(shù)據(jù),以及得到更快的速度。

          使用MyISAM:

          • 記錄日志(LJ用它來記網(wǎng)絡(luò)訪問日志)
          • 存儲只讀靜態(tài)數(shù)據(jù),足夠快。
          • 并發(fā)性很差,無法同時讀寫數(shù)據(jù)(添加數(shù)據(jù)可以)
          • MySQL非正常關(guān)閉或死機時會導(dǎo)致索引錯誤,需要使用myisamchk修復(fù),而且當(dāng)訪問量大時出現(xiàn)非常頻繁。

          9、緩存

          去年我寫過一篇文章介紹memcached,它就是由LJ的團(tuán)隊開發(fā)的一款緩存工具,以key-value的方式將數(shù)據(jù)存儲到分布的內(nèi)存中。LJ緩存的數(shù)據(jù):

          • 12臺獨立服務(wù)器(不是捐贈的)
          • 28個實例
          • 30GB總?cè)萘?
          • 90-93%的命中率(用過squid的人可能知道,squid內(nèi)存加磁盤的命中率大概在70-80%)

          如何建立緩存策略?

          想緩存所有的東西?那是不可能的,我們只需要緩存已經(jīng)或者可能導(dǎo)致系統(tǒng)瓶頸的地方,最大程度的提交系統(tǒng)運行效率。通過對MySQL的日志的分析我們可以找到緩存的對象。

          緩存的缺點?

          • 沒有完美的事物,緩存也有缺點:
          • 增大開發(fā)量,需要針對緩存處理編寫特殊的代碼。
          • 管理難度增加,需要更多人參與系統(tǒng)維護(hù)。
          • 當(dāng)然大內(nèi)存也需要錢。

          10、Web訪問負(fù)載均衡

          在數(shù)據(jù)包級別使用BIG-IP,但BIG-IP并不知道我們內(nèi)部的處理機制,無法判斷由哪臺服務(wù)器對這些請求進(jìn)行處理。反向代理并不能很好的起到作用,不是已經(jīng)夠快了,就是達(dá)不到我們想要的效果。

          所以,LJ又開發(fā)了Perlbal。特點:

          • 快,小,可管理的http web 服務(wù)器/代理
          • 可以在內(nèi)部進(jìn)行轉(zhuǎn)發(fā)
          • 使用Perl開發(fā)
          • 單線程,異步,基于事件,使用epoll , kqueue
          • 支持Console管理與http遠(yuǎn)程管理,支持動態(tài)配置加載
          • 多種模式:web服務(wù)器,反向代理,插件
          • 支持插件:GIF/PNG互換?

          11、MogileFS

          LJ使用開源的MogileFS作為分布式文件存儲系統(tǒng)。MogileFS使用非常簡單,它的主要設(shè)計思想是:

          • 文件屬于類(類是最小的復(fù)制單位)
          • 跟蹤文件存儲位置
          • 在不同主機上存儲
          • 使用MySQL集群統(tǒng)一存儲分布信息
          • 大容易廉價磁盤

          到目前為止就這么多了,更多文檔可以在http://www.danga.com/words/找到。Danga.comLiveJournal.com的 同學(xué)們拿這個文檔參加了兩次MySQL Con,兩次OS Con,以及眾多的其它會議,無私的把他們的經(jīng)驗分享出來,值得我們學(xué)習(xí)。在web2.0時代快速開發(fā)得到大家越來越多的重視,但良好的設(shè)計仍是每一個應(yīng) 用的基礎(chǔ),希望web2.0們在成長為Top500網(wǎng)站的路上,不要因為架構(gòu)阻礙了網(wǎng)站的發(fā)展。

           http://blog.csdn.net/xmr_gxcfe/archive/2007/09/14/1785292.aspx

           

          posted @ 2007-09-29 21:26 Coolfiry 閱讀(553) | 評論 (0)編輯 收藏

          posted @ 2007-09-25 14:30 Coolfiry 閱讀(358) | 評論 (0)編輯 收藏

          UML類圖的各種標(biāo)識法
          關(guān)鍵字:   UML    
          ·------>虛線箭頭表示依賴關(guān)系(dependency),一個類需要與另外一個類一起工作,是它一種最弱的關(guān)聯(lián)關(guān)系,常見于各種工具類之間的關(guān)系
          ·——實線表示聯(lián)合關(guān)系(association),一個類包含對另外一個類對象的引用,這個通常是使用屬性來實現(xiàn)的,為了表明之間的包含關(guān)系,有時候會在實線的一端加上箭頭(navigability arrow)來表示導(dǎo)航關(guān)系,如果關(guān)聯(lián)的雙方又都和第三個類有關(guān)聯(lián)關(guān)系,那么可以在實線的中間加一個虛線和第三個類關(guān)聯(lián)來表示這種association classes關(guān)系
          ·◇——空心菱形加實線表示聚合關(guān)系(aggregation),它是一種更強的關(guān)聯(lián)關(guān)系,表示一個類可以擁有或者享有一個類的實例對象,在java代碼表現(xiàn)上跟聯(lián)合是一樣的。
          ·◆——實心菱形加實線表示組合關(guān)系(composition),它的關(guān)聯(lián)性比聚合更強,被組合的對象是組合對象的一部分,沒法跟其他的對象共享,而且如果組合對象銷毀的話,被組合的對象也會同時被銷毀,其表現(xiàn)形式跟聯(lián)合一樣
          ·空心箭頭加實線,表示泛化generalization(繼承inheritance)關(guān)系,這個很簡單
          ·在rose中要建立enumeration,只需要在建立的class中將其stereotype設(shè)置為enumeration即可。stereotype只是用來做一個標(biāo)記,并不包含別的意義

          posted @ 2007-06-10 18:03 Coolfiry 閱讀(482) | 評論 (0)編輯 收藏

          明天18號,要從沈陽回成都了

          posted @ 2007-05-17 09:46 Coolfiry 閱讀(214) | 評論 (0)編輯 收藏

          PO BO VO DTO POJO DAO概念及其作用(附轉(zhuǎn)換圖)

             J2EE開發(fā)中大量的專業(yè)縮略語很是讓人迷惑,尤其是跟一些高手討論問題的時候,三分鐘就被人家滿口的專業(yè)術(shù)語噴暈了,PO VO BO DTO POJO DAO,一大堆的就來了(聽過老羅對這種現(xiàn)象的批判的朋友會會心一笑)。

              首先聲明偶也不是什么高手,以下總結(jié)都是自己的體會。不對之處請您多指教。

          PO:
          persistant object持久對象

          最形象的理解就是一個PO就是數(shù)據(jù)庫中的一條記錄。
          好處是可以把一條記錄作為一個對象處理,可以方便的轉(zhuǎn)為其它對象。

           



          BO:
          business object業(yè)務(wù)對象

          主要作用是把業(yè)務(wù)邏輯封裝為一個對象。這個對象可以包括一個或多個其它的對象。
          比如一個簡歷,有教育經(jīng)歷、工作經(jīng)歷、社會關(guān)系等等。
          我們可以把教育經(jīng)歷對應(yīng)一個PO,工作經(jīng)歷對應(yīng)一個PO,社會關(guān)系對應(yīng)一個PO。
          建立一個對應(yīng)簡歷的BO對象處理簡歷,每個BO包含這些PO。
          這樣處理業(yè)務(wù)邏輯時,我們就可以針對BO去處理。

           



          VO :
          value object值對象
          ViewObject表現(xiàn)層對象

          主要對應(yīng)界面顯示的數(shù)據(jù)對象。對于一個WEB頁面,或者SWT、SWING的一個界面,用一個VO對象對應(yīng)整個界面的值。

           



          DTO :
          Data Transfer Object數(shù)據(jù)傳輸對象
          主要用于遠(yuǎn)程調(diào)用等需要大量傳輸對象的地方。
          比如我們一張表有100個字段,那么對應(yīng)的PO就有100個屬性。
          但是我們界面上只要顯示10個字段,
          客戶端用WEB service來獲取數(shù)據(jù),沒有必要把整個PO對象傳遞到客戶端,
          這時我們就可以用只有這10個屬性的DTO來傳遞結(jié)果到客戶端,這樣也不會暴露服務(wù)端表結(jié)構(gòu).到達(dá)客戶端以后,如果用這個對象來對應(yīng)界面顯示,那此時它的身份就轉(zhuǎn)為VO

           



          POJO :
          plain ordinary java object 簡單java對象
          個人感覺POJO是最常見最多變的對象,是一個中間對象,也是我們最常打交道的對象。

          一個POJO持久化以后就是PO
          直接用它傳遞、傳遞過程中就是DTO
          直接用來對應(yīng)表示層就是VO

           


          DAO:
          data access object數(shù)據(jù)訪問對象
          這個大家最熟悉,和上面幾個O區(qū)別最大,基本沒有互相轉(zhuǎn)化的可能性和必要.
          主要用來封裝對數(shù)據(jù)庫的訪問。通過它可以把POJO持久化為PO,用PO組裝出來VO、DTO


                總結(jié)下我認(rèn)為一個對象究竟是什么O要看具體環(huán)境,在不同的層、不同的應(yīng)用場合,對象的身份也不一樣,而且對象身份的轉(zhuǎn)化也是很自然的。就像你對老婆來說就是老公,對父母來說就是子女。設(shè)計這些概念的初衷不是為了唬人而是為了更好的理解和處理各種邏輯,讓大家能更好的去用面向?qū)ο?/font>的方式處理問題.

                大家千萬不要陷入過度設(shè)計,大可不必為了設(shè)計而設(shè)計一定要在代碼中區(qū)分各個對象。一句話技術(shù)是為應(yīng)用服務(wù)的。

          歡迎指正。



          畫了個圖,感覺沒有完全表達(dá)出自己的意思。。。。。誰幫忙完善下,最好能體現(xiàn)各個O在MVC中的位置
          snap20070108.jpg 


          轉(zhuǎn)自:http://www.aygfsteel.com/vip01/archive/2007/01/08/92430.html

          posted @ 2007-05-17 09:44 Coolfiry 閱讀(340) | 評論 (0)編輯 收藏

          I love English from then on.I study English hard form then on.I love it.It is very lovely.

          posted @ 2006-11-26 14:13 Coolfiry 閱讀(270) | 評論 (0)編輯 收藏

          一、引言

            隨著Internet的飛速發(fā)展,人們越來越依靠網(wǎng)絡(luò)來 查找他們所需要的信息,但是,由于網(wǎng)上的信息源多不勝數(shù),也就是我們經(jīng)常所說的"Rich Data, Poor Information"。所以如何有效的去發(fā)現(xiàn)我們所需要的信息,就成了一個很關(guān)鍵的問題。為了解決這個問題,搜索引擎就隨之誕生。

             現(xiàn)在在網(wǎng)上的搜索引擎也已經(jīng)有很多,比較著名的有AltaVista, Yahoo, InfoSeek, Metacrawler, SavvySearch等等。國內(nèi)也建立了很多的搜索引擎,比如:搜狐、新浪、北極星等等,當(dāng)然由于它們建立的時間不長,在信息搜索的取全率和取準(zhǔn)率上都 有待于改進(jìn)和提高。

            Alta Vista是一個速度很快的搜索引擎,由于它強大的硬件配置,使它能夠做及其復(fù)雜的查詢。它主要是基于關(guān)鍵字進(jìn)行查詢,它漫游的領(lǐng)域有Web和 Usenet。支持布爾查詢的"AND","OR"和"NOT",同時還加上最相近定位"NEAR",允許通配符和"向后"搜索(比如:你可以查找鏈接到 某一頁的所有Web站點)。你可以決定是否對搜索的短語加上權(quán)值,在文檔的什么部位去查找它們。能夠進(jìn)行短語查詢而不是簡單的單詞查詢的優(yōu)點是很明顯的, 比如,我們想要查找一個短語"to be or not to be",如果只是把它們分解成單詞的話,這些單詞都是屬于Stop Word,這樣這個查詢就不會有任何結(jié)果,但是把它當(dāng)作一個整體來查詢,就很容易返回一些結(jié)果,比如關(guān)于哈姆雷特或者是莎士比亞等等的信息。系統(tǒng)對查詢結(jié) 果所得到的網(wǎng)頁的打分是根據(jù)在網(wǎng)頁中所包含的你的搜索短語的多少,它們在文檔的什么位置以及搜索短語在文檔內(nèi)部之間的距離來決定的。同時可以把得到的搜索 結(jié)果翻譯成其他的語言。

            Exite是稱為具有"智能"的搜索引擎,因為它建立了一個基于概念的索引。當(dāng)然,它所謂的"智能"是基 于對概率統(tǒng)計的靈活應(yīng)用。它能夠同時進(jìn)行基于概念和關(guān)鍵字的索引。它能夠索引Web,Usenet和分類的廣告。支持"AND","OR","NOT"等 布爾操作,同時也可以使用符號"+"和"-"。缺點是在返回的查詢結(jié)果中沒有指定網(wǎng)頁的尺寸和格式。

            InfoSeek是一個簡單 但是功能強大的索引,它的一個優(yōu)點是有一個面向主題搜索的可擴展的分類。你可以把你的搜索短語和相似的分類目錄的主題短語相互參照,而那些主題短語會自動 加到你的查詢中去。使你的搜索有更好的主題相關(guān)性。同時它也支持對圖象的查詢。它能夠漫游Web,Usenet,Usenet FAQs等等。不支持布爾操作,但是可以使用符號"+"和"-"(相當(dāng)于"AND"和"NOT")

            Yahoo實際上不能稱為是一 個搜索引擎站點,但是它提供了一個分層的主題索引,使你能夠從一個通常的主題進(jìn)入到一個特定的主題,Yahoo對Web進(jìn)行了有效的組織和分類。比如你想 要建立一個網(wǎng)頁,但是你不知道如何操作,為了在Yahoo上找到關(guān)于建立網(wǎng)頁的信息,你可以先在Yahoo上選擇一個主題:計算機和Internet,然 后在這個主題下,你可以發(fā)現(xiàn)一些子主題,比如:Web網(wǎng)頁制作,CGI編程,JAVA,HTML,網(wǎng)頁設(shè)計等,選擇一個和你要找的相關(guān)的子主題,最終你就 可以得到和該子主題相關(guān)的所有的網(wǎng)頁的鏈接。也就是說,如果你對要查找的內(nèi)容屬于哪個主題十分清楚的話,通過目錄查詢的方法要比一般的使用搜索引擎有更好 的準(zhǔn)確率。你可以搜索Yahoo的索引,但是事實上,你并沒有在搜索整個Web。但是Yahoo提供了選項使你可以同時搜索其他的搜索引擎,比如: Alta Vista。但是要注意的是Yahoo實際上只是對Web的一小部分進(jìn)行了分類和組織,而且它的實效性也不是很好。

            搜索引擎的基本原理是通過網(wǎng)絡(luò)機器人定期在web網(wǎng)頁上爬行,然后發(fā)現(xiàn)新的網(wǎng)頁,把它們?nèi)』貋矸诺奖镜氐臄?shù)據(jù)庫中,用戶的查詢請求可以通過查詢本地的數(shù)據(jù)庫來得到。如yahoo每天會找到大約500萬個新的網(wǎng)頁。

             搜索引擎的實現(xiàn)機制一般有兩種,一種是通過手工方式對網(wǎng)頁進(jìn)行索引,比如yahoo的網(wǎng)頁是通過手工分類的方式實現(xiàn)的,它的缺點是Web的覆蓋率比較 低,同時不能保證最新的信息。查詢匹配是通過用戶寫入的關(guān)鍵字和網(wǎng)頁的描述和標(biāo)題來進(jìn)行匹配,而不是通過全文的匹配進(jìn)行的。第二種是對網(wǎng)頁進(jìn)行自動的索 引,象AltaVista則是完全通過自動索引實現(xiàn)的。這種能實現(xiàn)自動的文檔分類,實際上采用了信息提取的技術(shù)。但是在分類準(zhǔn)確性上可能不如手工分類。

            搜索引擎一般都有一個Robot定期的訪問一些站點,來檢查這些站點的變化,同時查找新的站點。一般站點有一個robot.txt文 件用來說明服務(wù)器不希望Robot訪問的區(qū)域,Robot 都必須遵守這個規(guī)定。如果是自動索引的話,Robot在得到頁面以后,需要對該頁面根據(jù)其內(nèi)容進(jìn)行索引,根據(jù)它的關(guān)鍵字的情況把它歸到某一類中。頁面的信 息是通過元數(shù)據(jù)的形式保存的,典型的元數(shù)據(jù)包括標(biāo)題、IP地址、一個該頁面的簡要的介紹,關(guān)鍵字或者是索引短語、文件的大小和最后的更新的日期。盡管元數(shù) 據(jù)有一定的標(biāo)準(zhǔn),但是很多站點都采用自己的模板。文檔提取機制和索引策略對Web搜索引擎的有效性有很大的關(guān)系。高級的搜索選項一般包括:布爾方法或者是 短語匹配和自然語言處理。一個查詢所產(chǎn)生的結(jié)果按照提取機制被分成不同的等級提交給用戶。最相關(guān)的放在最前面。每一個提取出來的文檔的元數(shù)據(jù)被顯示給用 戶。同時包括該文檔所在的URL地址。

            另外有一些關(guān)于某一個主題的專門的引擎,它們只對某一個主題的內(nèi)容進(jìn)行搜索和處理,這樣信息的取全率和精度相對就比較高。

             同時,有一類搜索引擎,它本身不用Robot去定期的采集網(wǎng)頁。象SavvySearch 和 MetaCrawler是通過向多個搜索引擎同時發(fā)出詢問并對結(jié)果進(jìn)行綜合返回給用戶實現(xiàn)搜索功能。當(dāng)然實際上象SavvySearch能夠?qū)Ω鱾€搜索引 擎的功能進(jìn)行分析和比較,根據(jù)不同的用戶查詢提交給不同的搜索引擎進(jìn)行處理,當(dāng)然用戶自己也可以指定利用哪一個搜索引擎。

            一個優(yōu)秀的搜索引擎必須處理以下幾個問題:1 網(wǎng)頁的分類2 自然語言的處理3 搜索策略的調(diào)度和協(xié)作 4 面向特定用戶的搜索。所以很多搜索引擎不同程度的使用了一些人工智能的技術(shù)來解決這些方面的問題。

            二、網(wǎng)絡(luò)Spider的實現(xiàn)描述

             現(xiàn)在有很多文章對Web引擎做了大量的介紹和分析,但是很少有對它們的實現(xiàn)做一個詳細(xì)的描述,這里我們主要來介紹一個具有基本功能的Web引擎的實現(xiàn)。 本文,我們以類C++語言的形式來描述Web引擎如何采集網(wǎng)頁并存放到數(shù)據(jù)庫中的過程。同時描述了如何根據(jù)用戶輸入的關(guān)鍵字查詢數(shù)據(jù)庫并得到相關(guān)網(wǎng)頁的過 程。

            2.1數(shù)據(jù)庫結(jié)構(gòu)

            首先,我們要建立一個數(shù)據(jù)庫表用來存放我們得到的網(wǎng)頁。這里一般需要建立如下的表:

            1.字典表的建立,事實上這里是用文檔中有意義的單詞和它們的出現(xiàn)頻率來代表一個文檔。

            該表(WordDictionaryTbl)主要要包括三個字段,主要是用來存放和一個網(wǎng)頁相關(guān)的單詞的情況

          ????url_id? ? 對每一個URL的唯一的ID號
          ? ? word? ? ? 該URL中的經(jīng)過stem的單詞
          ? ? intag? ? 該單詞在該網(wǎng)頁中的出現(xiàn)的次數(shù)

            2.存儲每一個URL信息的表

            該表(URLTbl)中主要的關(guān)鍵字段有:

          ? rec_id? ? ? ? 每一條記錄的唯一的ID號
          ? status? ? 得到該URL內(nèi)容的狀態(tài),比如HTTP_STATUS_TIMEOUT表示
          ? ? ? ? ? ? 下載網(wǎng)頁的最大允許超時
          ? url? ? ? ? URL的字符串名稱
          ? content_type? ? ? 內(nèi)容的類型
          ? last_modified? ? 最新的更改時間
          ? title? ? ? ? ? ? 該URL的標(biāo)題
          ? docsize? ? ? ? ? 該URL的文件的尺寸
          ? last_index_time? 最近一次索引的時間
          ? next_index_time? 下一次索引的時間
          ? tag? ? 對于網(wǎng)頁,用來表示它的類型,比如:是text,或者是html,
          ? ? ? ? ? ? ? ? ? ? 或者是圖片等等
          ? hops? ? ? ? ? ? ? 得到文件時候的曾經(jīng)失敗的次數(shù)
          ? keywords? ? ? ? ? 對于網(wǎng)頁,和該網(wǎng)頁相關(guān)的關(guān)鍵字
          ? description? ? ? 對于網(wǎng)頁,指網(wǎng)頁的內(nèi)容的描述
          ? lang? ? ? ? ? ? ? 文檔所使用的語言

             3.因為網(wǎng)頁中有很多單詞是一些介詞和語氣助詞或者是非常常用的常用詞,它們本身沒有多少意義。比如:英語中的about,in,at,we,this 等等。中文中的如"和","一起","關(guān)于"等等。我們統(tǒng)一的把它們稱為停止詞(stop word)。所以我們要建立一個表,來包括所有這些停止詞。該表(StopWordTbl)主要有兩個字段。
          word char(32)? ? 表示那些停止詞
          lang char(2)? ? ? 表示所使用的語言

            4.我們要建立一個關(guān)于robot的表,我們在前面說過,所有的網(wǎng)站一般都有一個robot.txt文件用來表示網(wǎng)絡(luò)上的robot可以訪問的權(quán)限。該表(RobotTbl)主要有以下字段。
          ? ? hostinfo? ? ? ? ? Web站點主機的信息
          ? ? path? ? ? ? ? ? ? 不允許robot訪問的目錄

            5.建立我們需要屏蔽的那些網(wǎng)頁(比如一些內(nèi)容不健康的或者沒有必要去搜索的站點)的一張表(ForbiddenWWWTbl),主要的字段就是網(wǎng)頁的URL。

             6.另外我們需要建立一個我們所要得到的文件類型的表(FileTypeTbl),比如,對于一個簡單的Web搜索引擎,我們可能只需要得到后綴為. html,htm,.shtml和txt的類型文件。其他的我們只是簡單的忽略它們。主要的字段就是文件的類型和說明。

            其中關(guān)于停止詞的表的內(nèi)容是我們要實現(xiàn)要根據(jù)各種語言的統(tǒng)計結(jié)果,把那些意義不大的單詞放進(jìn)去。關(guān)于文檔單詞、URL和Robot的表的內(nèi)容都是在獲取Web網(wǎng)頁的時候動態(tài)增加記錄的。

            2.2 具體網(wǎng)頁獲取算法描述

            具體的網(wǎng)頁的獲取步驟是這樣的:

             我們可以設(shè)定我們的搜索程序最大可以開的線程的數(shù)目,然后這些線程可以同時在網(wǎng)上進(jìn)行搜索,它們根據(jù)數(shù)據(jù)庫中已有的關(guān)于網(wǎng)頁的信息,找出那些需要更新的 網(wǎng)頁(如何判斷哪些網(wǎng)頁需要更新是一個值得研究的過程,現(xiàn)在有很多啟發(fā)式和智能的算法,基本上是基于統(tǒng)計規(guī)律進(jìn)行建模。最簡單的當(dāng)然是設(shè)定一個時間范圍, 在某個時間范圍以前的網(wǎng)頁被重新去搜索一遍),然后判斷那些網(wǎng)頁是否在屏蔽表中,如果是的話,就從關(guān)于URL的表中刪除該條記錄。否則,我們就到相應(yīng)的 WWW站點去得到URL指定的文件(這里需要注意的是根據(jù)不同的URL的特點,需要使用不同的協(xié)議,比如對于FTP站點要采用FTP協(xié)議,對于HTTP站 點要采用HTTP協(xié)議,新聞?wù)军c要采用NNTP協(xié)議等等)事實上,我們先得到關(guān)于該網(wǎng)頁的頭信息,如果該網(wǎng)頁的最新修改時間和我們最近提取的時間是一樣的 話,表示該網(wǎng)頁內(nèi)容沒有任何更新,則我們就不必去得到它的內(nèi)容,只需要修改最近一次更新它的時間為當(dāng)前的時間就可以了。如果該網(wǎng)頁最近做了修改,我們就要 得到該網(wǎng)頁,并對它的內(nèi)容進(jìn)行分析,主要要包括和它相關(guān)的鏈接,把它們加到相應(yīng)的數(shù)據(jù)庫中,同時判斷網(wǎng)頁所包含的各種其他的文件,如文本文件、圖形文件、 聲音文件和其他多媒體文件是否是我們所需要的文件,如果是的話,就把它加到我們響應(yīng)的數(shù)據(jù)庫中。同時要根據(jù)網(wǎng)頁的內(nèi)容提取所有的有意義的單詞和它們的出現(xiàn) 的次數(shù),放到相應(yīng)的數(shù)據(jù)庫中。為了更好的描述這個過程,我們來看跟這個過程相關(guān)的主要的幾個對象和數(shù)據(jù)結(jié)構(gòu)。對象主要是針對三個層次來講的。第一層是針對 WWW服務(wù)器,第二層是針對每一個頁面,第三層是針對每一個頁面的全文的索引。

            2.3 和實現(xiàn)相關(guān)的主要類對象和功能描述下面的結(jié)構(gòu)是針對一個站點來說的。

          ? ? Class? CServer {
          ? ? 主要的屬性有:
          ????char *url;? ? ? ? ? ? //WWW站點的URL名稱
          ????char *proxy;? ? ? ? ? //使用的代理的名稱
          ????char *basic_auth;? ? ? //進(jìn)行基本的HTTP認(rèn)證
          ????int? proxy_port;? ? ? //代理的端口號
          ????int? period;? ? ? ? ? //再次索引的周期
          ????int? net_errors;? ? ? //網(wǎng)絡(luò)連接不通的次數(shù)
          ????int? max_net_errors;? //可以允許的最大的網(wǎng)絡(luò)錯誤
          ????int? read_timeout;? ? //下載文件允許的最大的延遲
          ????int? maxhops;? ? ? ? ? //表示URL可以最大跳轉(zhuǎn)的深度
          ????int? userobots;? ? ? ? //是否遵守robot.txt中的約定
          ????int? bodyweight;? // 在< body >....< /body >之間的單詞的權(quán)重
          ????int? titleweight; // 在< title >....< /title >之間的單詞的權(quán)重
          ????int? urlweight;? // 在文檔的URL中的單詞的權(quán)重
          ????int descweight;//在? ? < META
          NAME="Description"? ? ? ? Content="..." >之間單詞的權(quán)重
          ????int? keywordweight; //在< META NAME="Keywords" Content="..." >
          ? 之間的單詞的權(quán)重

            主要方法有:
          FindServer();//用來查找該服務(wù)器是否存在并可以連接
          FillDefaultAttribute() //用來針對所有的WWW服務(wù)器填寫默認(rèn)的屬};

          以上的對象中的成員變量是和一個站點相關(guān)的參數(shù)的設(shè)置,我們對所有的站點有一個默認(rèn)的設(shè)置,但是可以對某些站點做一些特殊的設(shè)置。這些設(shè)置可以在配置文件中設(shè)定。
            下面是關(guān)于文檔的結(jié)構(gòu)的主要的數(shù)據(jù)成員:

          Class CNetDocument
          ? ? 主要屬性有:
          ? ? int????url_id; //該URL的ID號
          ? ? int????status;? //獲取該文檔時候的狀態(tài)
          ? ? int????size;? //文檔的尺寸
          int????tag;? //和該文檔相關(guān)的標(biāo)簽,表示該文檔是
          HTML,TEXT或者是其他類型
          ? ? int????hops;? ? //URL跳轉(zhuǎn)的次數(shù)
          ? ? char????*url; //和該文檔相關(guān)的URL的名稱
          ? ? char????*content_type;? ? ? //該內(nèi)容的類型
          ? ? char????*last_modified;? ? //最近一次的更新時間
          ? ? char????*title;? ? ? ? ? ? //該文檔的標(biāo)題
          ? ? char????*last_index_time;? //上次索引的時間
          ? ? char????*next_index_time;? //下次索引的時間
          ? ? char????*keywords;? ? ? ? ? //該文檔中的關(guān)鍵字
          ? ? char????*description;? ? ? //該文檔的描述

          ? 主要方法有:
          ? FillDocInfo(…) //根據(jù)數(shù)據(jù)庫,得到該文檔相關(guān)信息
          ? AddHerf(…)? ? //加入網(wǎng)頁中存在的新的鏈接的網(wǎng)址
          ? DeleteURL(…)? //刪除一個存在的網(wǎng)址
          ? CanGetThisURL(…) //根據(jù)配置決定是否去得到該網(wǎng)頁
          ? //下面三個方法是根據(jù)不同的URL,用不同的協(xié)議去獲得文檔
          ? NNTPGet(…)? ? ?
          ? FTPGet(….)
          ? HTTPGet(….)
          ? ParseHead(…)? //如果是HTTP協(xié)議得到的話,分析頭信息
          ? ParseMainBody(…)? ? //對獲得的文檔的主體進(jìn)行分析
          ? ServerResponseType (….)? //得到服務(wù)器端的響應(yīng)消息
          ? UpdateURLDB(….)? //更新的數(shù)據(jù)入庫
          } ;

            事實上,我們在要提取一個網(wǎng)頁的時候,都要建立一個CNetDocument對象,然后再對這個網(wǎng)頁進(jìn)行分析的時候,把相關(guān)的內(nèi)容放到這個CNetDocument的成員變量里面。下面是關(guān)于頁面全文索引的結(jié)構(gòu)的主要數(shù)據(jù)成員:
          Class CIndexer {
          主要屬性有:
          ? char????*url;? ? ? //我們要處理的文檔相關(guān)的URL的名稱
          ? int mwords;????? //? 我們事先設(shè)定的一個網(wǎng)頁的最大的單詞數(shù)目
          ????int nwords;????????? // 實際的得到的單詞的數(shù)目
          ????int swords;????????? // 我們已經(jīng)排序的單詞的數(shù)目
          ????WORD *Word;????? //所有單詞的內(nèi)容
          ????char *buf;? ? ? //我們?yōu)槲臋n所分配的空間
          主要方法有:
          ? InitIndexer(…)? ? //進(jìn)行初始設(shè)置和分配
          ? ParseGetFile(…)? //對得到的網(wǎng)頁進(jìn)行全文索引
          ? AddWord(…)? ? //把網(wǎng)頁的可以索引的單詞加到Word數(shù)組中去
          ? InToDB(….)? ? //關(guān)于網(wǎng)頁全文索引的信息入庫
          };

            進(jìn)行網(wǎng)頁提取前,我們要建立一個CIndexer對象,它主要是用來對網(wǎng)頁進(jìn)行全文的索引。一般來說我們只對兩種類型的URL進(jìn)行全文索引,一個是text/html,另外一個是text/plain。其中WORD的數(shù)據(jù)結(jié)構(gòu)如下:
          ? ? ? ? typedef struct word_struct {
          ????int count;? //該單詞出現(xiàn)的次數(shù)
          ????int code;? //該單詞的正常的形式,
          比如單詞可能為 encouraging,它的正常的形式應(yīng)該為
          encourage,這其實是一種對單詞的stem。
          即我們只取單詞的主干部分。
          ????char *word;? //該單詞的內(nèi)容
          } WORD;

            以下的結(jié)構(gòu)是和網(wǎng)頁中的一些鏈接的對象相關(guān)的一個數(shù)據(jù)結(jié)構(gòu)
          ? ? typedef struct href_struct {
          ????char *href;? ? //該鏈接的名稱
          ????int hops;? ? ? //發(fā)生的跳轉(zhuǎn)次數(shù)
          ????int stored;? ? //是否已經(jīng)存儲到數(shù)據(jù)庫中
          } HREF;
          ?

            所有需要更新的和新產(chǎn)生的URL都被放到這個結(jié)構(gòu)中,當(dāng)它的數(shù)量超過一定的范圍以后,被一次性的存入數(shù)據(jù)庫。
            關(guān)于URL的一個數(shù)據(jù)結(jié)構(gòu)如下:

          typedef struct url {
          char *schema; //表示該URL是通過什么協(xié)議得到的,比如HTTP,
          ? ? ? ? ? ? ? FTP,NNTP等。
          char *specific;? ? //主機的名稱加上路徑
          char *hostinfo;? ? //主機的名稱加上相關(guān)的協(xié)議端口
          char *hostname;? ? //主機的名稱
          char *path;? ? ? ? //在主機的具體的路徑
          char *filename;? ? //文件的名稱
          char *anchor;? ? ? //相關(guān)的anchor
          int? port;? ? ? ? //協(xié)議相關(guān)的端口
          } URL;

            這是針對URL的一些相關(guān)的屬性的描述的一個數(shù)據(jù)結(jié)構(gòu)。事實上在數(shù)據(jù)庫中,我們存儲的只是對網(wǎng)頁的描述和對一些文本和HTML頁面的關(guān)鍵詞的索引信息。我們并不存儲網(wǎng)頁的實際的內(nèi)容。

            三、用戶查詢實現(xiàn)描述

            關(guān)于對用戶提交的查詢請求的實現(xiàn)分析:

            用戶想要查詢某一方面的信息一般都是通過提供和該領(lǐng)域相關(guān)的幾個關(guān)鍵字來進(jìn)行的。

            我們來看一下關(guān)于用戶查詢的相關(guān)的數(shù)據(jù)結(jié)構(gòu)和類:

            下面是一個關(guān)于單詞和它的權(quán)值的基本結(jié)構(gòu):

          ? typedef struct word_weight_pair
          ? ? {
          ? ? ? char word[WORD_LEN];
          ? ? ? int weight;
          ? ? }word_weight_pair;
          ? ?

            下面的類主要是用來對用戶的查詢進(jìn)行處理和分析:
          ? ? Class CUserQuery
          ? ? {
          char m_UserQuery[MAX_QUERYLEN];? //用戶的查詢表達(dá)式
          CPtrArray word_weight_col;
          //是關(guān)于結(jié)構(gòu)word_weight_pair的動態(tài)數(shù)組
          int m_maxReturnSum;? //用戶希望返回的最多的網(wǎng)頁數(shù)
          int search_mode;
          CObArray m_returnDoc;? //是關(guān)于CNetDocument對象的一個動態(tài)數(shù)組
          NormalizeWord(char* OneWord);? //對單詞進(jìn)行歸整化,即Stem.
          Find(char* odbcName);? //進(jìn)行數(shù)據(jù)庫查找和匹配
          };

            系統(tǒng)實現(xiàn)的基本的步驟如下:

            1.對用戶輸入的查詢表達(dá)式進(jìn)行分析。事實上,我們在前面的Spider搜索過程中對文檔的表示是通過關(guān)鍵字形式描述的,每一個文檔可以表示為這樣的一個集合

          ? ? 其中 ::=< 單詞或短語名稱 >< 單詞或短語的權(quán)值 >

            實際上就是采用矢量空間的表示方法來表示的文檔。

             我們對用戶輸入的查詢表達(dá)式也采用矢量空間的表示方法。我們認(rèn)為用戶輸入的關(guān)鍵字的順序代表了它的重要性的程度,所以對于位置靠前的單詞有相對比較高的 優(yōu)先級,同時我們對所有的內(nèi)容以短語或者是單詞為最小原子,進(jìn)行Stem操作,即象前面所提到的:比如單詞Encouraging就轉(zhuǎn)化成 Encourage的格式。然后去掉那些Stop Word,比如is ,as等等的單詞,這些單詞存放在StopWordTbl表中。 然后把所有歸整化后的內(nèi)容放入動態(tài)數(shù)組word_weight_col中去。

            2.對于動態(tài)數(shù)組word_weight_col中 的每一個元素,即結(jié)構(gòu)word_weight_pair(包括單詞和該單詞的權(quán)重),我們從表WordDictionaryTbl中可以找到和這些單詞相 關(guān)的記錄,這些記錄應(yīng)該是包括了所有的在word_weight_col中的單詞。

            進(jìn)行網(wǎng)頁是否和查詢相匹配的計算。匹配計算的 過程如下:首先我們對所有的記錄按URL地址進(jìn)行排序。因為可能好幾條記錄對應(yīng)的是一個URL,然后對每一個網(wǎng)頁進(jìn)行打分,每一條記錄的單詞權(quán)值為 INITSCORE*WEIGHT+(TOTALTIMES-1)*WEIGHT* INCREMENT。其中INITSCORE為每一個單詞的基準(zhǔn)分?jǐn)?shù),TOTALTIMES為該單詞在網(wǎng)頁中的出現(xiàn)的次數(shù),WEIGHT是該單詞在不同的 內(nèi)容段出現(xiàn)有不同的權(quán)值(比如在KEYWORD段,或者是標(biāo)題段,或者是內(nèi)容段等等)。INCREMENT是該單詞每多出現(xiàn)一次所增加的分?jǐn)?shù)。

            3.根據(jù)用戶指定的m_maxReturnSum,顯示匹配程度最高的前m_maxReturnSum頁。

            四、結(jié)束語

             我們利用上面所討論的機制,在WINDOWS NT操作系統(tǒng)下,用VC++和SQL SERVER實現(xiàn)了一個Web搜索引擎的網(wǎng)頁搜集過程。在建立了一個基本的搜索引擎的框架以后,我們可以基于這個框架,實現(xiàn)一些我們自己設(shè)計的算法,比如 如何更好的進(jìn)行Spider的調(diào)度,如何更好的進(jìn)行文檔的歸類,如何更好的理解用戶的查詢,用來使Web搜索引擎具有更好的智能性和個性化的特點。

          posted @ 2006-11-11 21:37 Coolfiry 閱讀(473) | 評論 (0)編輯 收藏

               摘要: 1.?? 目標(biāo) 使用 apache 和 tomcat 配置一個可以應(yīng)用的 web 網(wǎng)站,要達(dá)到以下要求: 1、? Apache 做為 HttpServer ,后面連接多個 tomcat...  閱讀全文

          posted @ 2006-11-06 17:20 Coolfiry 閱讀(729) | 評論 (0)編輯 收藏

          JDBC學(xué)習(xí)筆記
          2004-9-13?????星期一?????小雨

          l.?連接到數(shù)據(jù)庫的方法
          答:1)?ODBC(Open?Database?Connectivity)
          ???????一個以C語言為基礎(chǔ)訪問SQL為基礎(chǔ)數(shù)據(jù)庫引擎的接口,它提供了一致的接口用于和數(shù)據(jù)庫溝通以及訪問數(shù)據(jù)。
          ????2)?JDBC
          ???????Java版本的ODBC

          2.?JDBC應(yīng)用編程接口
          答:JDBC應(yīng)用編程接口是:
          ????1)?標(biāo)準(zhǔn)的數(shù)據(jù)訪問接口,可以連到不同的數(shù)據(jù)庫;
          ????2)?JAVA編程語言的一組類和接口。
          ????JDBC應(yīng)用編程接口能夠:
          ????1)?連接到數(shù)據(jù)庫;
          ????2)?發(fā)SQL查詢字符串到數(shù)據(jù)庫;
          ????3)?處理結(jié)果。
          ????JDBC應(yīng)用編程接口有二個主要的部分:
          ????1)?JAVA應(yīng)用程序開發(fā)接口面向JAVA應(yīng)用程序開發(fā)者;
          ????2)?JDBC驅(qū)動程序開發(fā)接口
          ????
          3.?JDBC?Driver
          答:1)?一大堆實現(xiàn)了JDBC類和接口的類;
          ????2)?提供了一個實現(xiàn)java.sql.Driver接口的類。

          4.?JDBC?Driver的四種類型
          答:1)?JDBC-ODBC橋
          ????由ODBC驅(qū)動提供JDBC訪問
          ????2)?本地API
          ????部分Java?driver把JDBC調(diào)用轉(zhuǎn)化成本地的客戶端API
          ????3)?JDBC-net
          ????純的Java?driver,將JDBC調(diào)用轉(zhuǎn)入DBMS,與網(wǎng)絡(luò)協(xié)議無關(guān)。然后通過服務(wù)器將調(diào)用轉(zhuǎn)為DBMS協(xié)議。
          ????4)?本地協(xié)議
          ????純的java?driver,將JDBC調(diào)用直接轉(zhuǎn)為DBMS使用的網(wǎng)絡(luò)協(xié)議

          5.?JDBC開發(fā)者接口
          答:1)?java.sql--java?2平臺下JDBC的主要功能,標(biāo)準(zhǔn)版(J2SE)
          ????2)?javax.sql--java?2平臺下JDBC增強功能,企業(yè)版(J2EE)

          6.?使用URL確認(rèn)數(shù)據(jù)庫
          答:我們使用URL來確定一個數(shù)據(jù)庫(正確的Driver,正確的主機,正確的協(xié)議,正確的協(xié)議,正確的用戶名和密碼);
          ????語法:protocol:subprotocol:subname
          ????范例:jdbc:db2:MyTest
          ??????????jdbc:db2://localhost:6789/MyTest

          7.?javax.sql包JDBC2.0的增強功能
          答:1)?數(shù)據(jù)源接口;
          ????2)?連接池;
          ????3)?分布式交易;
          ????4)?行集;

          8.?創(chuàng)建一個基本的JDBC應(yīng)用
          答:1)?步驟一:注冊一個driver;
          ????2)?步驟二:建立一個到數(shù)據(jù)庫的連接;
          ????3)?步驟三:創(chuàng)建一個statement;
          ????4)?步驟四:執(zhí)行SQL語句;
          ????5)?步驟五:處理結(jié)果;
          ????6)?步驟六:關(guān)閉JDBC對象

          9.?注冊一個Driver(步驟一)
          答:1)?driver被用于連接到數(shù)據(jù)庫;
          ????2)?JDBC應(yīng)用編程接口使用第一個能成功連接到給定URL的driver;
          ????3)?在同一時間可以裝載多個driver

          10.注冊一個driver的方法:
          答:1)?使用類loader(裝載;實例化;注冊入DriverManager)
          ???????a.?Class.forName("Com.ibm.db2.jdbc.app.DB2Driver");
          ???????b.?Class.forName("Com.ibm.db2.jdbc.net.DB2Driver");
          ???????c.?Class.forName("Com.microsoft.jdbc.sqlServer.SQLServerDriver);
          ???????d.?Class.forName("oracl.jdbc.driver.OracleDriver");
          ???????e.?Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
          ????2)?實例化一個Driver
          ???????a.?Driver?drv?=?new?COM.cloudscape.core.RmiJdbcDriver();

          2004-9-14?????星期二?????陰

          1.?建立一個到數(shù)據(jù)庫的連接(步驟二)
          答:DriverManager調(diào)用getConnection(urlString)方法,實際上調(diào)用的是driver的connect(urlString)方法;
          ????1)?當(dāng)一個driver肯定地對應(yīng)到一個數(shù)據(jù)庫URL,DriverManager建立一個連接;
          ????2)?當(dāng)沒有driver匹配,返回null然后下一個driver被檢驗;
          ????3)?假如沒有建立連接,拋出一個SQLExcepiton異常

          2.?經(jīng)常使用的一些JDBC?URL
          答:1)?JDBC-ODBC:?jdbc:odbc:<DB>
          ????2)?Oracle:?jdbc:oracle:oci:@<sid>?or?jdbc:oracle:thin:@<SID>
          ????3)?Weblogic?MS-SQL:?jdbc:weblogic:mssqlserver4:<DB>@<HOST>:<PORT>
          ????4)?DB2:?jdbc:db2:MyTest?or?jdbc.db2://localhost:6789/MyTest(需要用戶名和密碼)

          3.?Driver連接方法
          答:1)?創(chuàng)建一個到指定Driver實例的直接調(diào)用;
          ????2)?避免一般訪問的問題
          ???????Driver?drv?=?new?COM.ibm.db2.jdbc.app.DB2Driver();
          ???????Connection?con?=?null;
          ???????try?{con?=?drv.connect("jdbc:db2:MyTest",new?Properties())}
          ???????catch(SQLException?e){}

          4.?創(chuàng)建一個Statement(步驟三)
          答:1)?Statement的三個接口:
          ???????a.?Statement;
          ???????b.?PreparedStatement(繼承自Statement);
          ???????c.?CallableStatement(繼承自PreparedStatement);
          ????2)?使用方法Connection.createStatement()得到一個Statement對象

          5.?PreparedStatement對象
          答:1)?調(diào)用ProparedStatement比statement更為高效;
          ????2)?繼承自Statement;
          ????3)?語法:PreparedStatement?pstm?=?connection.prepareStatement(sqlString);

          6.?CallableStatement對象
          答:1)?通過CallableStatement調(diào)用數(shù)據(jù)庫中的存儲過程;
          ????2)?繼承自PreparedStatement;
          ????3)?CallableStatement?cstm?=?connection.prepareCall("{call?return_student[?,?]}");
          ???????cstm.setString(1,"8623034");
          ???????cstm.registerOutparameter(2,?Types.REAL);
          ???????cstm.execute();
          ???????float?gpa?=?cstm.getFloat(2);

          7.?Statement接口的比較
          答:?????????????|?Statement???????????|?PreparedStatement?????????|??CallableStatement
          ????------------------------------------------------------------------------------
          ????寫代碼位置???|???客戶端????????????|?客戶端????????????????????|??服務(wù)器端
          ????------------------------------------------------------------------------------
          ????寫代碼位置???|???客戶端????????????|?服務(wù)器端??????????????????|??服務(wù)器端
          ????------------------------------------------------------------------------------
          ????編寫代碼技術(shù)?|Java,SQL操作????????|Java,SQL操作??????????????|??數(shù)據(jù)庫的程序語言,如PL/SQL
          ????------------------------------------------------------------------------------
          ????可配置性?????|???高????????????????|第一次高,以后低???????????|??低
          ????------------------------------------------------------------------------------
          ????可移植性?????|???高????????????????|假設(shè)支持PreparedStatement的話高????
          ????------------------------------------------------------------------------------
          ????傳輸效率?????|???低????????????????|第一次低,以后高???????????|??高

          8.?執(zhí)行SQL?Statement(步驟四)
          答:通過接口方法將SQL語句傳輸至?認(rèn)的數(shù)據(jù)庫連接,返回結(jié)果可能是一個數(shù)據(jù)表,可以通過java.sql.ResultSet訪問。
          ????1)?Statement的接口方法:
          ????a.?executeQuery(sqlString):?執(zhí)行給定的SQL聲明,返回一個結(jié)果集(ResultSet)對象;
          ????b.?executeUpdate(sqlString):?執(zhí)行給定的SQL聲明,可以是INSERT、UPDATE或DELETE聲明,也可以是SQL?DDL聲明;
          ????c.?execute(sqlString):?執(zhí)行給定的SQL聲明。

          9.?處理結(jié)果(步驟五)
          答:1)?使用結(jié)果集(ResultSet)對象的訪問方法獲取數(shù)據(jù);
          ???????a.?next():下一個記錄
          ???????b.?first():第一個記錄
          ???????c.?last():最后一個記錄
          ???????d.?previous():上一個記錄
          ????2)?通過字段名或索引取得數(shù)據(jù)
          ????3)?結(jié)果集保持了一個指向了當(dāng)前行的指針,初始化位置為第一個記錄前。

          10.?關(guān)閉JDBC對象(步驟六)
          答:1)?首先關(guān)閉記錄集;
          ????2)?其次關(guān)閉聲明;
          ????3)?最后關(guān)閉連接對象。

          11.?數(shù)據(jù)表和類對應(yīng)的三種關(guān)系:
          答:1)?一個表對應(yīng)一個類;
          ????2)?一個表對應(yīng)相關(guān)類;
          ????3)?一個表對應(yīng)整個類關(guān)系層

          12.?類間關(guān)系的幾種表設(shè)計:
          答:1)?多對一,
          ????2)?一對一:?
          ????3)?一對多:
          ????4)?多對多:

          13.?SQL數(shù)據(jù)類型及其相應(yīng)的Java數(shù)據(jù)類型
          答:SQL數(shù)據(jù)類型?????????????????????Java數(shù)據(jù)類型??????????????說明
          ????------------------------------------------------------------------
          ????INTEGER或者INT??????????????????int?????????????????????通常是個32位整數(shù)
          ????SMALLINT????????????????????????short???????????????????通常是個16位整數(shù)
          ????NUMBER(m,n)?DECIMAL(m,n)????????Java.sql.Numeric????????合計位數(shù)是m的定點十進(jìn)制數(shù),小數(shù)后面有n位數(shù)
          ????DEC(m,n)????????????????????????Java.sql.Numeric????????合計位數(shù)是m的定點十進(jìn)制數(shù),小數(shù)后面有n位數(shù)
          ????FLOAT(n)????????????????????????double??????????????????運算精度為n位二進(jìn)制數(shù)的浮點數(shù)
          ????REAL????????????????????????????float???????????????????通常是32位浮點數(shù)
          ????DOUBLE??????????????????????????double??????????????????通常是64位浮點數(shù)
          ????CHARACTER(n)或CHAR(n)???????????String??????????????????長度為n的固定長度字符串
          ????VARCHAR(n)??????????????????????String??????????????????最大長度為n的可變長度字符串
          ????BOOLEAN?????????????????????????boolean?????????????????布爾值
          ????DATE????????????????????????????Java.sql.Date???????????根據(jù)具體設(shè)備而實現(xiàn)的日歷日期
          ????TIME????????????????????????????Java.sql.Time???????????根據(jù)具體設(shè)備而實現(xiàn)的時戳
          ????TIMESTAMP???????????????????????Java.sql.Timestamp??????根據(jù)具體設(shè)備而實現(xiàn)的當(dāng)日日期和時間
          ????BLOB????????????????????????????Java.sql.Blob???????????二進(jìn)制大型對象
          ????CLOB????????????????????????????Java.sql.Clob???????????字符大型對象
          ????ARRAY???????????????????????????Java.sql.Array
          ????

          2004-9-15?????星期三??????陰

          1.?元數(shù)據(jù)
          答:關(guān)于數(shù)據(jù)的信息,例如類型或者容量。通過JDBC?API可以訪問:
          ????1)?數(shù)據(jù)庫元數(shù)據(jù);
          ???????a.?使用connection.getMetadata方法返回DataMetaData引用
          ???????b.?能夠使用isReadOnly此類方法獲取信息
          ????2)?結(jié)果集元數(shù)據(jù);
          ???????a.?使用ResultSet.getMetadata方法返回ResultSetMetaData引用
          ???????b.?能夠使用getColumnCount此類方法獲取信息

          2.?事務(wù)處理
          答:1)?一系列的動作作為一個不可分的操作;
          ????2)?JDBC?API中使用事務(wù)處理步驟:
          ???????a.?用false作為參數(shù)調(diào)用setAutoCommit方法;
          ???????b.?執(zhí)行一或多個關(guān)于數(shù)據(jù)庫的操作;
          ???????c.?調(diào)用commit方法完成改變;
          ???????d.?恢復(fù)上次提交后的改變,調(diào)用rollback方法.

          ???????try
          ???????{
          ??????????con.setAutoCommit(false);
          ??????????Statement?stm?=?con.createStatement();
          ??????????stm.executeUpdate("insert?into?student(name,?age,?gpa)?values('gzhu',?30,?4.8)");
          ??????????stm.commit();
          ???????}
          ???????catch(SQLException?e)
          ???????{
          ??????????try
          ??????????{
          ?????????????con.rollback();
          ??????????}
          ??????????catch(Exception?e)
          ??????????{
          ??????????}
          ???????}

          3.?并發(fā)控制
          答:1)?設(shè)置隔離級別方法:setTransactionIsolation
          ????2)?隔離級別靜態(tài)變量
          ???????a.?TRANSACTION_NONE:只讀的數(shù)據(jù)字典;
          ???????b.?TRANSACTION_READ_UNCOMMITTED:只讀未提交數(shù)據(jù);
          ???????c.?TRANSACTION_READ_COMMITTED:只讀未提交數(shù)據(jù);
          ???????d.?TRANSACTION_REPEATABLE_READ:重復(fù)讀取數(shù)據(jù);
          ???????e.?TRANSACTION_SERIALIZABLE:無論做什么操作都不許別人動。
          ????3)?示例:con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

          4.?JDBC?2.0?應(yīng)用程序編程接口增強功能
          答:1)?ResultSet增強:
          ???????a.?可以回卷;
          ???????b.?可以修改;
          ???????設(shè)置示例:Statement?stm?=?con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
          ????2)?Statement增強了批量修改能力(batch?updates);
          ????3)?更高級的數(shù)據(jù)類型(例:Struct)。

          5.?JDBC?2.0標(biāo)準(zhǔn)擴展
          答:1)?JNDI(Java?Naming?and?Directory?Interface):?解決離散狀態(tài)下Object的查找;
          ????2)?連接池:在內(nèi)存中保存了一個數(shù)據(jù)庫連接,不需要注冊驅(qū)動器,提高性能的重要方法。

          posted @ 2006-11-03 10:14 Coolfiry 閱讀(239) | 評論 (0)編輯 收藏

          問題引入:
          在實習(xí)過程中發(fā)現(xiàn)了一個以前一直默認(rèn)的錯誤,同樣char *c = "abc"和char c[]="abc",前者改變其內(nèi)

          容程序是會崩潰的,而后者完全正確。
          程序演示:
          測試環(huán)境Devc++
          代碼
          #include <iostream>
          using namespace std;

          main()
          {
          ?? char *c1 = "abc";
          ?? char c2[] = "abc";
          ?? char *c3 = ( char* )malloc(3);
          ?? c3 = "abc";
          ?? printf("%d %d %s\n",&c1,c1,c1);
          ?? printf("%d %d %s\n",&c2,c2,c2);
          ?? printf("%d %d %s\n",&c3,c3,c3);
          ?? getchar();
          }??
          運行結(jié)果
          2293628 4199056 abc
          2293624 2293624 abc
          2293620 4199056 abc

          參考資料:
          首先要搞清楚編譯程序占用的內(nèi)存的分區(qū)形式:
          一、預(yù)備知識—程序的內(nèi)存分配
          一個由c/C++編譯的程序占用的內(nèi)存分為以下幾個部分
          1、棧區(qū)(stack)—由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于

          數(shù)據(jù)結(jié)構(gòu)中的棧。
          2、堆區(qū)(heap)—一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由OS回收。注意它與數(shù)據(jù)

          結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
          3、全局區(qū)(靜態(tài)區(qū))(static)—全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)

          變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。程序結(jié)束后由系統(tǒng)

          釋放。
          4、文字常量區(qū)—常量字符串就是放在這里的。程序結(jié)束后由系統(tǒng)釋放。
          5、程序代碼區(qū)
          這是一個前輩寫的,非常詳細(xì)
          //main.cpp
          ? int a=0;??? //全局初始化區(qū)
          ? char *p1;?? //全局未初始化區(qū)
          ? main()
          ? {
          ?? int b;棧
          ?? char s[]="abc";?? //棧
          ?? char *p2;???????? //棧
          ?? char *p3="123456";?? //123456\0在常量區(qū),p3在棧上。
          ?? static int c=0;?? //全局(靜態(tài))初始化區(qū)
          ?? p1 = (char*)malloc(10);
          ?? p2 = (char*)malloc(20);?? //分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
          ?? strcpy(p1,"123456");?? //123456\0放在常量區(qū),編譯器可能會將它與p3所向"123456"優(yōu)化成一個

          地方。
          }
          二、堆和棧的理論知識
          2.1申請方式
          stack:
          由系統(tǒng)自動分配。例如,聲明在函數(shù)中一個局部變量int b;系統(tǒng)自動在棧中為b開辟空間
          heap:
          需要程序員自己申請,并指明大小,在c中malloc函數(shù)
          如p1=(char*)malloc(10);
          在C++中用new運算符
          如p2=(char*)malloc(10);
          但是注意p1、p2本身是在棧中的。
          2.2
          申請后系統(tǒng)的響應(yīng)
          棧:只要棧的剩余空間大于所申請空間,系統(tǒng)將為程序提供內(nèi)存,否則將報異常提示棧溢出。
          堆:首先應(yīng)該知道操作系統(tǒng)有一個記錄空閑內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序的申請時,
          會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結(jié)點,然后將該結(jié)點從空閑結(jié)點鏈表中刪除,并將

          該結(jié)點的空間分配給程序,另外,對于大多數(shù)系統(tǒng),會在這塊內(nèi)存空間中的首地址處記錄本次分配的大

          小,這樣,代碼中的delete語句才能正確的釋放本內(nèi)存空間。另外,由于找到的堆結(jié)點的大小不一定正

          好等于申請的大小,系統(tǒng)會自動的將多余的那部分重新放入空閑鏈表中。
          2.3申請大小的限制
          棧:在Windows下,棧是向低地址擴展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡?/p>

          址和棧的最大容量是系統(tǒng)預(yù)先規(guī)定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯

          時就確定的常數(shù)),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間

          較小。
          堆:堆是向高地址擴展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地

          址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統(tǒng)中有效的

          虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。
          2.4申請效率的比較:
          棧:由系統(tǒng)自動分配,速度較快。但程序員是無法控制的。
          堆:是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便.
          另外,在WINDOWS下,最好的方式是用Virtual Alloc分配內(nèi)存,他不是在堆,也不是在棧,而是直接在進(jìn)

          程的地址空間中保留一塊內(nèi)存,雖然用起來最不方便。但是速度快,也最靈活。
          2.5堆和棧中的存儲內(nèi)容
          棧:在函數(shù)調(diào)用時,第一個進(jìn)棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的

          地址,然后是函數(shù)的各個參數(shù),在大多數(shù)的C編譯器中,參數(shù)是由右往左入棧的,然后是函數(shù)中的局部變

          量。注意靜態(tài)變量是不入棧的。
          當(dāng)本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù),最后棧頂指針指向最開始存的地址,也就是主

          函數(shù)中的下一條指令,程序由該點繼續(xù)運行。
          堆:一般是在堆的頭部用一個字節(jié)存放堆的大小。堆中的具體內(nèi)容由程序員安排。
          2.6存取效率的比較
          char s1[]="aaaaaaaaaaaaaaa";
          char *s2="bbbbbbbbbbbbbbbbb";
          aaaaaaaaaaa是在運行時刻賦值的;
          而bbbbbbbbbbb是在編譯時就確定的;
          但是,在以后的存取中,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快。
          比如:
          #include
          voidmain()
          {
          char a=1;
          char c[]="1234567890";
          char *p="1234567890";
          a = c[1];
          a = p[1];
          return;
          }
          對應(yīng)的匯編代碼
          10:a=c[1];
          004010678A4DF1movcl,byteptr[ebp-0Fh]
          0040106A884DFCmovbyteptr[ebp-4],cl
          11:a=p[1];
          0040106D8B55ECmovedx,dwordptr[ebp-14h]
          004010708A4201moval,byteptr[edx+1]
          004010738845FCmovbyteptr[ebp-4],al
          第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據(jù)

          edx讀取字符,顯然慢了。
          2.7小結(jié):
          堆和棧的區(qū)別可以用如下的比喻來看出:
          使用棧就象我們?nèi)ワ堭^里吃飯,只管點菜(發(fā)出申請)、付錢、和吃(使用),吃飽了就走,不必理會

          切菜、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
          使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。

          自我總結(jié):
          char *c1 = "abc";實際上先是在文字常量區(qū)分配了一塊內(nèi)存放"abc",然后在棧上分配一地址給c1并指向

          這塊地址,然后改變常量"abc"自然會崩潰

          然而char c2[] = "abc",實際上abc分配內(nèi)存的地方和上者并不一樣,可以從
          4199056
          2293624 看出,完全是兩塊地方,推斷4199056處于常量區(qū),而2293624處于棧區(qū)

          2293628
          2293624
          2293620 這段輸出看出三個指針分配的區(qū)域為棧區(qū),而且是從高地址到低地址

          2293620 4199056 abc 看出編譯器將c3優(yōu)化指向常量區(qū)的"abc"


          繼續(xù)思考:
          代碼:
          #include <iostream>
          using namespace std;

          main()
          {
          ?? char *c1 = "abc";
          ?? char c2[] = "abc";
          ?? char *c3 = ( char* )malloc(3);
          ?? //? *c3 = "abc" //error
          ?? strcpy(c3,"abc");
          ?? c3[0] = 'g';
          ?? printf("%d %d %s\n",&c1,c1,c1);
          ?? printf("%d %d %s\n",&c2,c2,c2);
          ?? printf("%d %d %s\n",&c3,c3,c3);
          ?? getchar();
          }??
          輸出:
          2293628 4199056 abc
          2293624 2293624 abc
          2293620 4012976 gbc
          寫成注釋那樣,后面改動就會崩潰
          可見strcpy(c3,"abc");abc是另一塊地方分配的,而且可以改變,和上面的參考文檔說法有些不一定,

          而且我不能斷定4012976是哪個區(qū)的,可能要通過算區(qū)的長度,希望高人繼續(xù)深入解釋,謝謝
          ?

          posted @ 2006-10-16 19:06 Coolfiry 閱讀(1151) | 評論 (2)編輯 收藏

          僅列出標(biāo)題
          共4頁: 上一頁 1 2 3 4 下一頁 
          主站蜘蛛池模板: 丰台区| 广汉市| 民县| 北票市| 米易县| 仁寿县| 巴青县| 贵定县| 建宁县| 宝应县| 新营市| 修文县| 云梦县| 新和县| 沾化县| 普定县| 抚宁县| 延津县| 吴江市| 资讯 | 仁怀市| 丹棱县| 无极县| 汤原县| 蓝田县| 洛川县| 贡山| 蛟河市| 黑河市| 浑源县| 左云县| 宜兰市| 屏东县| 萨迦县| 雷山县| 石狮市| 怀安县| 武宣县| 辰溪县| 大同市| 雷波县|