基于MySQL數(shù)據(jù)庫(kù)的論壇設(shè)計(jì)方案
基于MySQL數(shù)據(jù)庫(kù)的論壇設(shè)計(jì)方案
1。系統(tǒng)架構(gòu):
采用模塊化思想,分為3層:
a。數(shù)據(jù)存儲(chǔ)層:使用MySQL來(lái)存放bbs的所有數(shù)據(jù),包括用戶(hù)信息,文章數(shù)據(jù),用戶(hù)信件,用戶(hù)消息,系統(tǒng)數(shù)據(jù)(?),關(guān)鍵問(wèn)題: 數(shù)據(jù)庫(kù)的規(guī)劃,是否用文件來(lái)輔助。
b。系統(tǒng)功能層:完成bbs的基本功能,由多個(gè)并列模塊組成,向下調(diào)用mysql的函數(shù)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),向上,接受處理請(qǐng)求,將處理的結(jié)果返回上層,根據(jù)請(qǐng)求類(lèi)型,返回成敗結(jié)果和其他數(shù)據(jù)。而且模塊高度靈活,可以方便的修改增加。包括:
** 用戶(hù)模塊,處理用戶(hù)的注冊(cè),基本數(shù)據(jù)的修改,權(quán)限的變化,網(wǎng)友信息的查詢(xún)。
** 版面模塊,完成文章發(fā)表,文章的讀取,文章的刪除,文章的加標(biāo)記,讀改刪權(quán)限檢查,此模塊對(duì)數(shù)據(jù)庫(kù)的要求最高。
** 精華區(qū)模塊,包括精華區(qū)的文章,目錄的增加,刪除,上下移動(dòng)
(?)讀改刪權(quán)限檢查,目錄結(jié)構(gòu)是其中的難點(diǎn)。
** 信件模塊,包括發(fā)新信件,讀刪信件,信箋標(biāo)記,新信件的通知
** 消息模塊,包括發(fā)送消息,接受消息,新消息通知,消息回顧,消息存信件。
** 系統(tǒng)動(dòng)態(tài)模塊,包括當(dāng)前上站人數(shù),當(dāng)前動(dòng)態(tài),由于變動(dòng)頻繁,此類(lèi)數(shù)據(jù)用共享內(nèi)存實(shí)現(xiàn)可能更好。
** 聊天模塊,雙人聊天是否能借鑒icq的做法,由雙方直接通話(huà),但聊天結(jié)果存信件可能較麻煩,同時(shí),為兼容telnet功能,當(dāng)上層服務(wù)層為telnet時(shí),增加專(zhuān)門(mén)的模塊來(lái)進(jìn)行處理。
** 聊天室模塊,利用共享內(nèi)存還是數(shù)據(jù)庫(kù)?開(kāi)房間,里面的權(quán)限問(wèn)題。根據(jù)需要,還能增加新的功能。例如:活動(dòng)看板模塊,但對(duì)于非telnet終端,意義好象不大。。。。。
3。數(shù)據(jù)庫(kù)設(shè)計(jì)
關(guān)鍵還是MySQL的效率問(wèn)題,合理分配mysql的內(nèi)存,特別是table cache的
大小。另外,當(dāng)系統(tǒng)突然掉電呢?mysql是否robust?
table的名字設(shè)計(jì),采用一位前綴表明類(lèi)型,全部用小寫(xiě)表示(?),例如:
系統(tǒng)的數(shù)據(jù)庫(kù),以s為前導(dǎo),如用戶(hù)表:suser(sUSER 呢?),具體如下:
s :系統(tǒng)表,suser,sclass
m :用戶(hù)信件表,msysop,mdrangon
w :用戶(hù)消息表,wsysop,wdrangon
a :版面索引表,aLinux,acampus
b :版面文章表,blinux,bcampus
c :特殊分類(lèi)版面表,cnewboard
i :精華區(qū)索引表,ilinux,ilinux01,icampus,icampus04
j :精華區(qū)文章表,jlinux,jcampus,
另外,是使用字串還是數(shù)字作為標(biāo)識(shí)呢?例如,一個(gè)叫sysop的帳號(hào),其
id是1,他的信的表是msysop還是m00001呢?同樣,一個(gè)叫campus的版,對(duì)應(yīng)的
代碼是5,則這個(gè)版的文章的表名是bcampus還是b00005呢?可能用字串會(huì)容易
理解,查錯(cuò)吧。
用戶(hù)信息表:suser
usernum int unique, // 唯一標(biāo)識(shí)符,最多30000個(gè)帳號(hào),會(huì)不會(huì)太少了?
userid char[20] primary key, // 排序的關(guān)鍵字,id,全小寫(xiě)。
passwd char[20], // 密碼,存放加密后的密文。
realid char[20], // 實(shí)際id,大小寫(xiě)混合。
username char[24], // 用戶(hù)的泥稱(chēng)
userlevel longint, // 64種權(quán)限?
nUMLogins int,
numposts int,
firstlogin time,
lastlogin time,
staytime time, /* 總共停留時(shí)間 */
lasthost char[32],
email varchar[100],
address varchar[100],
// 還需要其他數(shù)據(jù)嗎?是否需要留出一定的保留值,以后alter table來(lái)
// 增加新的字段時(shí),效率如何?
版面分類(lèi)表:sclass
classnum int unique, // 分類(lèi)標(biāo)識(shí)
classid char[20], // 分類(lèi)的英文id:computer
classname varchar[100],// 分類(lèi)的中文描述:電腦世界
classtable char[20], // 特殊分類(lèi)對(duì)應(yīng)的版面表
// 一般來(lái)說(shuō),每個(gè)版面只屬于一個(gè)分類(lèi),對(duì)于特殊分類(lèi),例如拳頭版塊, #p#page_title#e#
// 新版面,可以用專(zhuān)門(mén)的表來(lái)描述
版面表:sboard
boardnum int unique, // 版面的標(biāo)識(shí)(需要嗎?)
boardid char[20], // 版面的英文名
boardname varchar[100], // 版面的中文名
boardclass char[20], // 版面所屬分類(lèi)
boardsysop varchar[100], // 斑竹名單
boardposts int, // 版面的文章數(shù)
boardlevel int, // 版面的讀寫(xiě)權(quán)限
indextable char[20], // 版面對(duì)應(yīng)的索引表的名稱(chēng):aboardid?
texttable char[20], // 版面對(duì)應(yīng)的文章表名稱(chēng): bboardid?
// 最后兩項(xiàng)有沒(méi)有必要出現(xiàn),是否可以作為必然對(duì)應(yīng)關(guān)系,還是允許
// 出現(xiàn)更大的靈活性?另外版面的大小寫(xiě)問(wèn)題是否可以直接默認(rèn)
// 只開(kāi)頭字母大寫(xiě),
特殊分類(lèi)版面表:snewboard, sstarboard
boardid char[20], // 版面的id
// 這樣的表有必要嗎?
版面索引表:acampus,aLinux,afootball。。。。。。
id int, // 文章序數(shù),要手動(dòng)調(diào)整????
mark char[1], // 文章標(biāo)記,m,g,b,d。。。。
title varchar[100], // 文章標(biāo)題
writer char[20], // 文章作者id
posttime time, // 發(fā)表時(shí)間
textnum longint, // 對(duì)應(yīng)的編號(hào)???不調(diào)整
版面文章表
textnum longint, // 文章編號(hào)?
textword text, // 文章內(nèi)容?
// 有必要將索引和文章內(nèi)容分開(kāi)嗎?從效率上看,況且lazy flush
// 是必然的。刪除也是先做個(gè)標(biāo)記。
// 用戶(hù)中的版面文章是否未讀的數(shù)據(jù)比較繁,是否應(yīng)該再建一堆的表
// 才能實(shí)現(xiàn)呢?
// 投票功能暫不考慮。。。。
4。用戶(hù)模塊設(shè)計(jì)
對(duì)于底層數(shù)據(jù)庫(kù),調(diào)用MySQL的C API函數(shù)來(lái)進(jìn)行數(shù)據(jù)庫(kù)的修改,內(nèi)部保存一定的狀態(tài)變量(例如用戶(hù)名,還是留給上一層完成?),對(duì)上一層,則提供用戶(hù)管理的接口。
Class UserManage {
private:
char myuserid[20]; // 用戶(hù)的id,未登陸前為空
time logintime; // 用戶(hù)登陸時(shí)間,并用于計(jì)算停留時(shí)間
char loginhost[20]; //上站地點(diǎn)。
public:
int NewUser( char *userid, char *passwd );
新建一個(gè)用戶(hù),判斷是否已經(jīng)有,其他資料暫時(shí)為空,
firstlogintime,權(quán)限等設(shè)缺省值。
int UserLogin( char *userid, char *passwd );
用戶(hù)登陸,驗(yàn)證密碼,
int ChangePasswd( char *oldpasswd, char *newpasswd );
修改密碼,要求原密碼一致。
int ChangePriData( char *newname, char *newemail,
char *newaddr );
改變基本數(shù)據(jù),泥稱(chēng),email,住址。。。。
int ModifyNumData( int addlogin, int addpost );
修改文章數(shù),上站次數(shù),等數(shù)據(jù)。。。。注意調(diào)用對(duì)象。
int UserLogout();
用戶(hù)退出,修改lastlogin,staytime,loginhost等
// 普通查詢(xún)命令
int QueryCommonData( const char * userid, int& loginnum,
char * username, int& postnum,
time& lastlogin, char *lasthost );
查詢(xún)網(wǎng)友基本信息。
// 特權(quán)指令,函數(shù)在完成功能前,先判斷權(quán)限。
int QueryPriData( const char * userid, char *email,
char *addr );
查詢(xún)基本信息,普通人只能查自己,有特權(quán)才能查其他人。
int ModifyUserLevel( BOOL isAdd, unsigned long level );
修改用戶(hù)的權(quán)限,
int ModifyUserId( char *oldid, char *newid );
char *newemail, char *newaddr );
修改用戶(hù)的基本數(shù)據(jù)。
int ModifyUserNumdata( char *userid, int addlogin, int addpost );
修改用戶(hù)的文章數(shù)等數(shù)據(jù)。
int ModifyUsERPasswd( char *userid, char *newpasswd );
修改用戶(hù)的密碼。
}
以上各個(gè)函數(shù)難度不大,都是執(zhí)行相應(yīng)的sql語(yǔ)句,訪(fǎng)問(wèn)mysql數(shù)據(jù)庫(kù),是否將一般指令歸到特權(quán)指令中去呢?權(quán)限的檢查,是放在這一層還是上一層? #p#page_title#e#
這更多的是看考慮的著重點(diǎn),是看程序的清晰性還是代碼的簡(jiǎn)練,可能還是看代碼吧,畢竟要考慮訪(fǎng)問(wèn)量,另外,上層服務(wù)層是否也應(yīng)該考慮權(quán)限檢查問(wèn)題呢?
5。版面模塊設(shè)計(jì)
所謂分類(lèi),更多的是為telnet服務(wù)端考慮的,在cq66模式下,用戶(hù)可以按照自己的意愿進(jìn)行分類(lèi),反正最后都是直接以版為基本單位訪(fǎng)問(wèn)的。
對(duì)于版面文章的訪(fǎng)問(wèn),存放的時(shí)候以整篇文章為參數(shù),文章的分塊由本層完成,如果上層以塊為單位傳送,則在上層全部傳完,組合后,再傳參到本層分解;在讀取 的時(shí)候,本層則以塊為單位訪(fǎng)問(wèn),如果上層要以全文為單位訪(fǎng)問(wèn),則在上層做合并 工作,本層不管。
至于要不要獨(dú)立出索引,不影響上層的操作,主要和下層的數(shù)據(jù)庫(kù)構(gòu)造有關(guān),主要考慮可行性,效率需求等。
權(quán)限的檢查放在哪里進(jìn)行呢?還是放在上層吧,其實(shí)就telnet服務(wù)器端,和cq66 的客戶(hù)端,根本不會(huì)給一般用戶(hù)顯示特殊指令的菜單,當(dāng)然,用戶(hù)可以直接發(fā)送cq66 的指令,服務(wù)器方還是要檢查的。但應(yīng)該不用在它下面的功能模塊層再檢查一次吧 。
Class BoardManage {
private:
public:
// 有關(guān)分類(lèi)的操作
int GetClassNameInfo( int maxclass, char **classid,
char ** classname );
返回分類(lèi)的信息,中英文名。
int GetBoardName( int maxboards, char *classid,
char **boardname );
返回某分類(lèi)中的版面信息,一般分類(lèi),直接select ..
from sboard
where boardclass == .... 特殊分類(lèi)則查相應(yīng)的表。。。。
// 修改需要版面管理員以上的特權(quán)
int NewClass( char * newclassname, int type );
新建分類(lèi),普通分類(lèi)還是特殊分類(lèi),
int DeleteClass( char *newclassname );
刪除分類(lèi),但不cascade,即本層不負(fù)責(zé)一致性,由上層負(fù)責(zé)將
相應(yīng)的版面的分類(lèi)信息改為別的。分類(lèi)改名也是先刪再建,
int AddClassBoard( const char *classname, char *newboardname );
將已建好的版加入某分類(lèi)中,專(zhuān)門(mén)針對(duì)特殊分類(lèi),對(duì)一般分類(lèi),其
效果和modifyboardinfo一樣,
int DeleteClassBoard( const char *classname, char *boardname );
從分類(lèi)中刪除某個(gè)版,也是針對(duì)特殊分類(lèi),對(duì)一般分類(lèi),效果也
是和modifyboardinfo一樣,一個(gè)版的分類(lèi)屬性可以為空,即不屬
于任何分類(lèi)。
// 有關(guān)版的信息的操作。
int NewBoard( const char *boardid,char *boardname);
新建一個(gè)版,建立對(duì)應(yīng)的表。其他參數(shù)取默認(rèn)值。
int DeleteBoard( const char *boardid );
刪除一個(gè)版,刪除對(duì)應(yīng)的表。
int GetBoardInfo( const char *boardid, char *boardname,
int& numposts, char *masters, char *class,
long &level );
取的版面的信息。
int ModifyBoardId( const char *oldid, char *newid );
改變版的英文id,對(duì)應(yīng)table的名稱(chēng)也要改變,
int ModifyBoardInfo( const char *boardid, char *boardname,
int numposts, char *masters, char *class,
long level );
修改版面信息,需要特權(quán)。
// 有關(guān)版面文章的操作。
int AddText( char *boardid, char *title, char *writer,
char *text );
往版面中增加文章,內(nèi)部將長(zhǎng)文章分割成2k的塊。
int DeleteText( char *boardid, int num );
刪除文章,只是做一個(gè)標(biāo)記,并不立刻修改對(duì)應(yīng)的table。
int FlushTable( char *boardid );
刷新版面,刪除被刪文章的對(duì)應(yīng)的記錄。
int MarkText( char *boardid, int num, char mark );
給文章做標(biāo)記。
int ModifyTitle( char *boardid, int num, char *newtitle );
修改文章的標(biāo)題。
int ModifyText( char *boardid, int num, char *newtext );
修改文章內(nèi)容,不是自己的文章需要特權(quán)。
int GetTextInfo( const char *boardid, int num, char *title,
char *writer, char& mark );
取得文章的標(biāo)題信息。 #p#page_title#e#
int GetText( const char *boardid, int num, int block,
char *text );
讀取文章的內(nèi)容,以塊為單位。
// 文章和作者的查詢(xún)
// 一次將查詢(xún)的結(jié)果全部返回?
int QueryWriter( const char *boardid, char *writer,
char **result );
查詢(xún)版面上,某作者的文章。
int QueryTitle( const char *boardid, char *title,
char **result );
查詢(xún)版面上,標(biāo)題中包含指定內(nèi)容的文章。
}
參數(shù)的傳遞是一件比較討厭的事,從抽象的角度,希望返回的數(shù)據(jù)與底層無(wú)關(guān),所以應(yīng)該加以處理,但從效率的角度,又不希望數(shù)據(jù)進(jìn)行多次復(fù)制,另一方面,空間的申請(qǐng)釋放,究竟是在上層中完成還是在本層中完成呢?一不小心,很容易有內(nèi)存錯(cuò)誤。