Fourinone4.0:一個高性能的數據庫引擎CoolHash
一、前言:如何寫一個數據庫
就像華為想進軍軟件市場已經很久了,我腦海里一直惦記著數據庫技術,幾年來不斷收集數據庫實現技術,但是一直不得要領,幾欲放棄,沒有碰到一個白胡子老頭傳授秘籍,國內也幾乎沒有這方面的書籍,找了幾本國外的教材,要么翻譯的不好,要么英文啃起來費勁,書里更多是講數據庫相關的基礎知識,但是不會告訴你怎么做,好比是你想學變臉,但是總在教你唱戲,其實你只想知道變臉是用線拉的就行了。行業(yè)技術機密都是避而不談的,感覺從書本上是學不到的。
去年給一個銀行技術老總講解大數據方案,他突然問到,你們能不能不用別人的,自己寫個ORACLE這樣的數據庫出來,我們每年花在license和服務費非常昂貴,升級也很痛苦,但是也沒有辦法。我承諾可以抽業(yè)余時間研究一下,不過他馬上又說,就算有也不會馬上用,可以先開源出來,可以看出他無比糾結的心態(tài),也許他對太多架構師說過這樣的話,可能現在都忘了,但是這件事再次給了我觸動,言語之間能感受到客戶寄托出的一種期望,希望中國企業(yè)能成長為ORACLE、IBM這樣的角色。
要實現一個數據庫,首先不能不談起數據庫引擎。數據庫引擎和數據庫產品的關系,就像汽車發(fā)動機和汽車的關系,有了發(fā)動機,剩下的只是裝配工作。知名的數據庫引擎有“ISAM、MyISAM、InnoDB、PostgreSQL、BerkeleyDB等”,另外也有些產品模糊在“數據庫引擎、數據庫server、數據庫管理系統(tǒng)”之間的,如近幾年的redis,還有JDK6.0起自帶一個java編寫的只有2m大的關系數據庫Derby(由IBM捐獻),另外值得一提的還有SQLite,一個很輕量級嵌入式的數據庫(3萬c代碼,250k大小),但是功能齊全,實現了ACID和SQL標準,目前廣泛應用于蘋果的Mac操作系統(tǒng)和Android移動操作系統(tǒng)中,缺點是多用戶高并發(fā)承受能力較弱。
世界上大部分的數據庫產品都是圍繞部分數據庫引擎擴充出來的,比如大家熟悉的Mysql,它的數據庫引擎叫MyISAM,MyISAM是在ISAM發(fā)展而來,ISAM也是一個知名的數據庫引擎,最初被IBM開發(fā),它讀的性能大于寫,但是索引功能和事務處理缺乏,MyISAM相對于ISAM做了很多改進,優(yōu)化了表鎖和并發(fā)操作,但是由于繼承的原因,仍然傾向于多讀少寫操作。ISAM系列引擎的大致設計原理:采用B樹設計,分成表元數據、表數據、表索引3部分存儲,讀的快是因為維持大量的索引結構指向數據存儲位置,但是由于刪除更新容易導致大量數據碎片和空間浪費,常常需要執(zhí)行“OPTIMIZE TABLE”,從而又導致索引常常需要重新計算。Mysql5.5以后默認采用InnoDB引擎,還可以使用BerkeleyDB,InnoDB和BerkeleyDB包括了對事務處理和外鍵的支持,這是ISAM系列引擎所沒有的特性,另外InnoDB的鎖設計的要精明一點,鎖到少數行的數據塊上,而不是整表鎖。
關于ISAM系列的發(fā)展有很多,IBM開發(fā)了VSAM代替ISAM,VSAM被IBM一個數據庫產品所使用 ,就是大名鼎鼎的DB2。雖然MySql的數據庫引擎換成了InnoDB,但是MySql的作者邁克爾(Michael Widenius)從MySql舊版本發(fā)展了另一個分支,并以自己的小女兒名字命名為MariaDB(瑪麗亞),MariaDB的數據庫引擎叫做Aria,但實際上還是MyISAM,只是增加了些key緩存改進(Segmented Key Cache),MariaDB數據庫得到了google等企業(yè)的大力支持,普通的觀點認為oralce收購Mysql的最終目的是想其慢慢死亡,而不是想著如何把它發(fā)展更好。
PostgreSQL和BerkeleyDB都是來源于加州伯克利分校,一個面向關系數據,一個面向k/v數據,雖然知名和流行程度不如Mysql,但是也各有優(yōu)勢,PostgreSQL的關系數據庫功能實現的比較完整,包括了很多高級特性,并且采用BSD/MIT開源協(xié)議,因此比較適合用來封裝成數據庫產品,這種協(xié)議允許任何人使用修改代碼,但是要保留版權聲明,BerkeleyDB提供了一個高并發(fā)訪問的k/v數據庫引擎,但不是一個數據庫server,不提供網絡訪問,不支持sql,但可以支持函數式操作,數據庫產品HyperTable和MemcacheDB內部都使用了BerkeleyDB。
綜上所述,我們可以看出:
-
大部分數據庫產品都是都是近親結婚,由少數數據庫引擎(如ISAM、PostgreSQL、BerkeleyDB等)發(fā)展而來。(但是oracle和sqlserver的數據庫引擎是內置的,鮮有人知)
-
你會驚奇的發(fā)現,MyISAM、BerkeleyDB、InnoDB等大部分數據庫引擎的vendor都是oracle,oracle公司一直處心積慮的收購著市場上有競爭力的數據庫引擎,用來捍衛(wèi)自己的壟斷地位,
-
幾乎所有的數據庫引擎都是GPL協(xié)議,擅自閉源用于商業(yè)將承擔法律責任。
因此,要寫一個數據庫,首先要從實現數據庫引擎入手,掌握數據庫引擎技術有重要意義,因為無論是“關系數據庫還是K/V數據庫,SQL數據庫還是NOSQL數據庫,分布式數據庫還是并行數據庫,列數據庫還是對象數據庫...”,存儲引擎部分都有著相似的實現原理,掌握了數據庫引擎技術后,只要公司愿意投資,就可以長足拓展到任何數據庫領域,一切只是工作量問題,數據庫引擎一經研發(fā)成功,就值得長期放養(yǎng),哪怕是5年10年。同時對個人職業(yè)意義來說,對一個玩了一輩子軟件技術的架構師,沒有寫過數據庫是一種遺憾,就跟喜歡女優(yōu)的工程師沒有見過真人一樣。
二、CoolHash是個什么樣的數據庫
1、CoolHash是一個數據庫引擎
CoolHash只做數據庫最基礎核心的引擎部分,支持大部分數據類型的“插入、獲取、更新、刪除、批量插入、批量獲取、批量更新、批量刪除、高效查詢(精確查、模糊查、按樹節(jié)點查、按key查、按value查)、分頁,排序、and操作、or操作、事務處理、key指針插入和查詢、緩存持久互轉”等操作和遠程操作。
其他的“監(jiān)控、管理、安全、備份、命令行操作、運維工具”等外圍特性都剝離出去不做,開發(fā)者可以根據自己需求擴充這些功能,CoolHash也不做自動擴容,CoolHash認為分布式集群特性也可以在外圍通過“分庫分表”或者“分布式擴容”等中間件技術去做,目前國內很多企業(yè)都具備基于開源軟件做外圍中間件的能力,這樣,CoolHash只維持一個高性能又輕量級的最小存儲引擎。
CoolHash高度產品化,易用性強,容易嵌入使用和復制傳播(200k大小),采用apache2.0開源協(xié)議,使用java實現(jdk1.7),對外提供java接口,同時支持windows和linux(unix-like),由于依賴底層操作系統(tǒng),windows和linux的實現稍有不同。
2、CoolHash是一個k/v數據庫
實現數據庫存儲結構索引有多種方法,有比較平衡減少深入的b樹、b+樹系列,結合內存再合并的LSM-tree系列(bigTable),借鑒字典索引技術的Trie樹(前綴樹、三叉樹)等,不過對于key/value結構,感覺最合適的還是Hash,不過java里的Hash算法是實現在內存組件里,無法持久化,只能快速讀寫,但是無法模糊查詢,傳統(tǒng)的Hash不是一個“cool”的Hash,需要進行改進。
CoolHash改進后的Hash算法是一個完整的key樹結構,我們知道傳統(tǒng)Hash的key和key之間沒有關系,互相獨立,但是在CoolHash里,key可以表示為“user.001.name”的用“.”分開的樹層次結構,如“user.001.age”和“user.001.name”都屬于“user.001.*”分支,既可獨立獲取,也可以從父節(jié)點查詢,還可以“user.*.name”方式只查詢所有子節(jié)點。
提出key指針的概念:CoolHash的key可以是一個指針,指向其他樹的key,這樣能將兩棵key樹連起來,這樣的設計能避免大量join操作,如果兩棵key樹沒有直接關系,需要動態(tài)join將會非常耗時,但有了key指針可以很好的描述數據之間“1對1、1對多、多對多”的關聯(lián)關系。key指針也不同于數據庫外健,它可以模糊指,比如只模糊指向一個key前綴“user.001”,再補充需要獲取的屬性形成一個完整key;還能連續(xù)指,比如一個key指針指向另一個key指針再指向其他key指針,連續(xù)指可以將更多的樹聯(lián)系起來形成一個大的數據森林。CoolHash能很好的控制key指針最終值返回、key指針死循環(huán)、讀寫死鎖等問題。
我們知道關系數據庫的數據表是一個行列的二維矩陣的數據結構,表和表之間沒有層次感,一個表不會是另一個表的父子節(jié)點,這就導致需要大量關聯(lián),從一個表獲取另一個表的數據需要通過join操作,同時由于表和表之間彼此獨立,又導致數據量大后join性能不好,多個矩陣需要動態(tài)的“求并”“求或”獲取主健,再返回所有屬性。很多朋友是開發(fā)業(yè)務系統(tǒng)出身,復雜一點的業(yè)務邏輯通常需要關聯(lián)7-8個數據庫表甚至更多,是相當頭疼的事情。

這種矩陣行列式結構還有個不好,就是一加全加,一減全減,一行加了一列屬性,所有行都要跟著加,假如有個“子女”屬性,有的人有1個子女,有的人有2-3個子女,這個屬性應該有時是1列,有時是3列,有時沒有才好。CoolHash的父節(jié)點下的key值可以任意添加和減少,或者再任意向下擴充葉子結點,也可以任意查詢,能夠非常靈活,比如“子女”屬性可以這樣表示第一個小孩:“user.001.children.001”。
關系數據庫還容易產生數據碎片,需要經常執(zhí)行“OPTIMIZE TABLE”或者“壓縮數據庫”等操作回收空間,CoolHash對此做了設計和算法改進,對于大量數據頻繁寫入和刪除能很好重復利用存儲空間。
key和value的數據格式:CoolHash的key只能是字符串,默認最大長度為256字節(jié)。value能支持非常廣泛的數據類型,基本數據類型“String(變長字符)、short(短整形)、int(整型)、long(長整形)、 double(雙精度浮點型)、 float(浮點型)、Date(日期型)”,高級數據類型的大部分的java集合都能支持(List、Map、Set等),以及任意可序列化的自定義java類型,底層數據類型也可以支持二進制型。基本數據類型的value可支持按內容查詢,高級數據類型和二進制類型不支持按內容查詢。所有類型的value默認最大長度為2M,默認配置保證一對key/value長度不會太大,能夠容易加載到內存進行處理,但是key和value的最大長度、以及region大小都是可以根據計算機性能進行配置。
CoolHash還支持HashMap緩存和持久化的統(tǒng)一和互相轉換,并共用一套API,可以將數據放入CoolHashMap,也可以放入持久化,可以將CoolHashMap對象轉成持久化,也可以從持久化數據里直接獲取CoolHashMap對象。
3、CoolHash是一個并行數據庫(mpp)
前幾年有篇文章,國外的數據庫大牛猛烈抨擊map/reduce,讓重復造輪子的人應該去學習一下關系數據庫幾十年的理論和實踐積累,一個借助蠻力而不善于設計索引的數據庫系統(tǒng)是愚蠢和低效的。這一方面說明了map/reduce技術已經侵犯到了傳統(tǒng)數據庫領域的核心利益,另一方面也暴露了分布式存儲技術的某些不足。
這里的蠻力就是并行計算,在CoolHash的底層,會維持一個數據工人進程組,根據計算機性能少則幾個多則上百個,對于數據庫的每種操作,數據工人們像演奏交響樂一樣,時而獨奏,時而合奏,統(tǒng)一調度,緊密協(xié)作的完成任務,CoolHash從一開始就是高度并行化的設計,數據庫引擎本質就是在尋求cpu、內存、硬盤的充分利用和均衡利用,“一力勝十巧”,在大量數據的讀寫和查詢中并行計算的效果猶為明顯。
同時一個好的數據庫引擎應該是蠻力和技巧的結合,Hbase的一個重要啟示,就是可以靈活設計它的key達到近似索引的效果,CoolHash將此特性發(fā)揮的更深入,樹型結構的key本身就是最好的索引,除外CoolHash幾乎不需要另外再建立索引,只需要按照業(yè)務數據特點設計好你的key。并行計算和索引的結合會得到一個很好的互補,讓你的索引結構不需要維持的太精準而節(jié)省開銷,舉個例子:我們從一個城市找一個人,一種方法是我們有精確的索引,知道他在哪個區(qū)哪個樓那個房間,另一種是我們只大致知道他在哪棟樓,但是我們有幾百個工人可以每間房同時去找,這樣也能很快找到。
4、CoolHash是一個nosql數據庫
nosql不等于沒有sql的功能,關系數據庫的sql仍然是非常方便的交互語言,而且有專門的標準,CoolHash通過函數方式實現大部分sql的功能,如果需要擴充sql支持,在外圍用正則表達式做一個sql解析,然后調用CoolHash的函數支持即可。
由于CoolHash沒有關系數據庫的“db、table、row、col”等概念,使用樹型key取代了,所以sql create語句功能就不需要了。
-
“put()函數”對應于“sql insert、update”語句
-
“remove()函數”對應于“sql delete...where”語句
-
“put(map)函數”對應于“sql insert/update into...select...”語句
-
“get()函數”對應于“sql select...where id=?”語句
-
“get(map)”函數對應于“sql select *...”語句
-
“find(*,filter)”函數對于于“sql select * where col=%x%”語句
-
“and()函數”對應于“sql and”語句
-
“or()函數”對應于“sql or”語句
-
“sort(comp)函數”對應于“sql desc/group”語句
5、CoolHash實現了事務處理
CoolHash實現了ACID事務屬性,對寫入、更新、刪除的基本操作提供事務處理,在程序里調用begin()、commit()、rollback()等事務方法。
-
原子性(Atomic):事務內操作要么提交全部生效,要么回滾全部撤消。
-
一致性(Consistent):事務操作前后的數據狀態(tài)保持一致,一致性跟原子性密切相關,比如銀行轉賬前后,兩個賬戶累加總和保持一致。
-
隔離性(Isolated):多個事務操作時,互相不能有影響,保持隔離。
-
持續(xù)性(Durable) :事務提交后需要持久化生效。
另外,對于多個事務并發(fā)操作數據的情況,JDBC規(guī)范歸納出“臟讀(dirty read)、不可重復讀(non-repeatable read) 、幻讀(phantom read) ”三種問題,并提出了4種事務隔離級讓軟件制造商去實現,按照不同等級去容忍這三個問題,其實這種逐級容忍大部分都不實用,一個事務操作未提交時,按照事務隔離級,其他訪問應該是讀取到該事務開始前的數據,而不應該把事情搞復雜,CoolHash實現的是“TRANSACTION_SERIALIZABLE”,禁止容忍三種讀取問題。
6、CoolHash是一個數據庫Server
CoolHash支持遠程網絡訪問,服務端發(fā)布一個IP和監(jiān)聽端口,客戶端連接該IP端口即可進行遠程操作,CoolHash可承受多用戶高并發(fā)的網絡連接訪問,來源于服務端設計的相似之處,大家知道Apache HTTP Server是一個多進程模型+共享內存方式實現,在前面講到的CoolHash會維持一個數據工人的進程組,數據工人不僅是并行計算的執(zhí)行者,同時也是網絡請求的響應者,數據工人身兼多職能很好的將服務端設計統(tǒng)一起來,避免重復設計,因此CoolHash也是一個很好的網絡Server.
7、CoolHash的測試性能
有句話叫做“人生如白駒過隙”,用來形容性能就是一瞬間,數據庫性能最好能接近緩存,或者直接可以當作緩存來用,能在瞬間完成讀寫和各種查詢,這個瞬間就是秒。只有在幾秒內完成操作才能做到實時交互沒有等待感,否則就要離線交互。
-
CoolHash的單條寫入和讀取速度都在毫秒級別,寫和讀差別不大,讀略快于寫。
-
CoolHash的批量寫入和讀取速度都控制在秒級別,100萬數據寫入基準測試,普通臺式機或者筆記本(4核4g)需要5-6秒,標準pc server(24核256g)需要2-3秒;批量讀、批量刪除和批量寫入的速度差不多。
-
CoolHash寫入緩存和寫入持久的速度差別不大,100萬數據寫入緩存基準測試,普通臺式機或者筆記本(4核4g)需要5秒左右,標準pc server(24核256g)需要2秒左右。如果是10萬級別的數據讀寫,緩存和持久的速度大致接近等同。
-
CoolHash的查詢速度控制在秒級別,100萬數據的模糊查詢(如like%str%)在沒有構建索引情況下,普通臺式機或者筆記本(4核4g)需要2-3秒,標準pc server(24核256g)需要1-2秒;如果是重復查詢,由于CoolHash內部做了數據內存映射,第二次以后只需要毫秒級完成。
-
高并發(fā)多客戶端的吞吐量總體速度要快于單客戶端,但是受服務器cpu、內存、io等性能限制,會傾向于一個平衡值。
數據庫引擎的性能通常也會受“key/value數據大小、數據類型、工人數量、硬件配置(內存大小、cpu核數、硬盤io、網絡耗用)”等等因素影響。
-
比如同樣數量但是單條key/value數據很大,整體速度要慢一些;
-
基本數據類型的讀寫速度要快過高級數據類型(如java集合類);
-
工人數量也有影響,對于單條寫入讀取單工人和多工人差不多,但是批量操作和查詢多工人要好過單工人,普通臺式機或者筆記本(4核4g)維持在8-10個工人可以打滿cpu,標準pc server(24核256g)最大可以維持到100個左右工人,但是工人數量就算能打滿cpu,也受限于后端硬盤io,到一定程度速度不再增長;
-
硬件配置對于性能的影響很大,普通筆記本的測試效果明顯不如標準pc server,筆記本內存較小,硬盤io弱,不適合做數據庫服務器。內存大的服務器測試效果好,加載數據和jvm垃圾回收速度會更快,目前測試采用的都是傳統(tǒng)SAS/SATA硬盤,如果采用固態(tài)硬盤進行硬件升級,隨機IO性能會得到進一步提升;
-
另外,由于存在網絡耗用和序列化傳送,遠程網絡操作的速度要比本地速度慢,但是局域網內速度會接近本地速度,這是因為遠程網絡操作涉及帶寬接入限制和線路共享等復雜消耗,而局域網內主要取決于物理設備速度。
關于CoolHash性能的更多體驗,歡迎有興趣的朋友根據demo在各自的機器環(huán)境下,模擬各種極端條件下去壓測。
三、如何使用CoolHash
ch追求極簡的編程體驗,不需要安排配置,服務端啟動CoolHashServer,指定好ip和端口,客戶端大致編程步驟如下:
代碼
- CoolHashClient chc = BeanContext.getCoolHashClient("localhost",2014);//連接CoolHashServer
- chc.put("user.001.name","zhang");//寫入字符
- chc.put("user.001.age",20);//寫入整數
- chc.put("user.001.weight",50.55f);//寫入浮點數
- chc.put("user.001.pet",new ArrayList());//寫入集合對象
- String name = (String)chc.get("user.001.name");//讀取字符
- int age = (int)chc.get("user.001.age");//讀取整數
- float weight = (float)chc.get("user.001.weight");//讀取浮點數
- ArrayList pet = (ArrayList)chc.get("user.001.pet");//讀取集合對象
- chc.put("user.002.name","Li");
- chc.put("user.002.age",25);
- chc.put("user.002.weight",60.55f);
- CoolKeyResult keyresult = chc.findKey("user.001.*");//查找用戶001的所有屬性
- CoolKeySet ks = keyresult.nextBatchKey(4);//分頁獲取前4條結果
- System.out.println(ks);//輸出[user.001.weight, user.001.age, user.001.name, user.001.pet]
- CoolHashResult mapresult = chc.find("user.*.age", ValueFilter.greater(18));//查找年齡大于18歲的用戶
- CoolHashMap hm = mapresult.nextBatch(10);
- System.out.println(hm);//輸出[user.001.age=20, user.002.age=25]
- ......
更多的功能使用請去參考開發(fā)包里的demo
四、Fourinone到底是什么?
Fourinone1.0是一個并行計算框架,2.0是一個分布式文件系統(tǒng)(Fttp),4.0是一個數據庫引擎(CoolHash)...那么Fourinone到底是什么?
我們把Fourinone打開,其實就是純java程序,除外什么都沒有,通篇展示如何用最基礎的java實現上面的功能。
其實不用關注Fourinone的產品定位究竟是什么,Fourinone就是四不象,雖然功能眾多,跨度很大,但是仍然長自同一個身體,由同一個腦袋指揮,只要各項機理運行正常健康就好。
Fourinone就是俄羅斯套娃里最小的一個,2.0是1.0的應用,3.0是2.0的應用,4.0是3.0的應用,層層擴充沒有止境,體現軟件搭建精髓思想。
Fourinone只收集最核心的技術,hadoop的開發(fā)者去使用spark需要重新學習,反之亦然,因為他永遠只是一個使用者,但是Fourinone可以從一個簡單MQ演變成一個復雜數據庫,只有掌握核心技術,才具備強大的變通能力。
Fourinone4.0雖然新增加了一個完整的數據庫引擎,但是依然保持著苗條身材,整體大小只有220k,用不到1萬行java代碼實現,所有的設計都是原創(chuàng)和創(chuàng)新。
除外,4.0版本還增加了以下特性:
-
多進程多線程的無縫融合,同一套接口,改改參數,從多進程變?yōu)槎嗑€程,開發(fā)者無需改寫程序邏輯;
-
提供高容錯任務分配算法API:doTaskCompete(m工人,n任務),將n個任務分給m個工人并行完成,根據任務大小設置工人數量,工人間能者多勞,性能好的工人機器爭搶干更多的任務,同時跟現實工作一樣,如果有工人生病請假(故障),那么他的任務活由其余工人代干,除非所有工人出故障,否則就算只剩一個工人也應該加班把其他所有工人的活干完,對整體計算來說,部分工人故障對計算結果來說不受影響,只是計算時間會延長。
五、結束語:將技術做酷是一種生活態(tài)度
就像搖滾樂手騎行哈雷是一種生活態(tài)度一樣,把技術和產品做酷也是一種生活態(tài)度。
-
用很多代碼體積臃腫實現出來也可以,但是不夠酷,用很少代碼實現功能更強才酷;
-
用很多人花很多時間做出一個產品也可以,但是不夠酷,用很少人花很少時間做出來才酷;
-
全靠整合依賴太多也可以,但是不夠酷,沒有任何依賴才酷;
-
最核心的組件都是用別人的也可以,但是不夠酷,自己做的更好才酷;
-
產品只有專業(yè)人士會用也可以,但是不夠酷,沒文化的人都會用才酷;
-
跳水自由落水也可以,但是不夠酷,翻幾個跟頭不冒一點水花才酷;
-
演死尸直接倒下也可以,但是不夠酷,多晃悠幾下再死才酷;
-
不玩創(chuàng)新也可以,那樣的人生不夠酷,玩創(chuàng)新挨整才酷。
基礎軟件也能像互聯(lián)網產品余額寶、微信那樣做到很酷,追求傻瓜式的客戶體驗和黏性,把草根程序員當做客戶群體,做他們踮踮腳就能夠的到的軟件?;A軟件相對于業(yè)務創(chuàng)新產品來說有一定技術門檻,不容易被復制,觀察我們身邊的互聯(lián)網產品,“從網游到偷菜到手游到微博到微信...”或者是“從b2b到c2c到b2c到團購到o2o...”總在不斷的進行業(yè)務模式創(chuàng)新,保鮮期不超過2年,而且容易遭到山寨,但是微軟的操作系統(tǒng)和office、oracle的數據庫賣了20多年,不需要什么創(chuàng)新好像也沒有競爭對手。
很多畢業(yè)生踏入這個行業(yè)時最初的夢想也是想寫一個很酷的操作系統(tǒng)或者數據庫,但是無數次的現實磨滅了他的夢想,很多人最后變成一個在職場政治和項目扯皮中尋求著人生價值,并且總是把“技術不是問題”掛在嘴邊,是否還記的你曾經的夢想,把技術做酷。
Fourinone4.0(CoolHash)開源地址:http://code.google.com/p/fourinone/
svn地址(瀏覽器可以直接下載):http://fourinone.googlecode.com/svn/trunk/


























