在數(shù)據(jù)庫中,經(jīng)常需要用到大字段類型,如oracle中l(wèi)ong/blob/clob,sqlserver中text/image,mysql中的text/longtext/clob/blob。
存儲的信息大概主要是兩類,一類是長文本,如大段的文字,普通的varchar最長只能存儲4000個漢字,已經(jīng)不能滿足要求;另一類是存儲二進制信息,如上傳的文件等。
 
那么假如現(xiàn)在有一個表,記錄某人發(fā)布的文檔信息,字段包括:發(fā)布人,發(fā)布時間,文檔標題,文檔內(nèi)容(實際中還會有其它字段),一般建表如下(sqlserver):
create table document( 
id 
int identity(1,1not null
createuser_id 
int
document_title 
varchar(255), 
document_context 
text); 

這張表的結(jié)構(gòu),表面上看起來,從數(shù)據(jù)庫設(shè)計角度和對應(yīng)的JAVA類的設(shè)計來講,都是沒有問題的。
但實際上,這里面隱藏著兩個比較嚴重的問題!
一、不能完全跨數(shù)據(jù)庫

 why?問題出在需要查重(distinct)的時候。
  在需要查重時,采用純jdbc技術(shù),則可以自定義要查重的字段,如select distinct id,createuser_id,document_title from document。而當采用hibernate時,若不想自已創(chuàng)建若干個新的Pojo或者使用Object[]方式來處理數(shù)據(jù),則只能使用select distinct d from document as d這樣的語句,而hibernate會將其解析為類似:select distinct id,createuser_id,document_title,document_context from document。
  問題就出在這個document_context字段上!
  對于mysql來講,hibernate生成的sql是可以執(zhí)行的。但對于sqlserver來講,是不允許在text/image列上進行distinct查詢的!oracle中同樣不可以對clob/blob進行distinct查詢。
  因此系統(tǒng)在sqlserver/oracle上部署時,當需要查重時則會出錯。當然如果你用不到查重語句,是一點不受影響的。

二、嚴重影響列表顯示和統(tǒng)計的效率
  影響一張表的查詢速度的,除了行數(shù),還包括表所占的物理空間的大小。此表在數(shù)據(jù)量較小時,在查詢方面感覺不到明顯的差異。但是如果document_context字段所存儲的數(shù)據(jù)都是大段文本或較大的文件時,會導(dǎo)致表的物理空間迅速變大,該字段所占用的空間有可能達到整表所占空間的90%以上。在此基礎(chǔ)上,如果行數(shù)再增加到數(shù)十萬、上百萬級時,整個表所占的空間將達到一個驚人的數(shù)字。
  保守估計,一條記錄占用的空間平均為10K的話,一萬條記錄將占用100M的空間,一百萬條記錄將占用10G!在此表上的CRUD操作,亦將變慢,查詢的速度亦會受到非常大的影響 。當然通過提高服務(wù)器本身的硬件性能和優(yōu)化索引,可以提高查詢速度,但面對無法預(yù)知的巨大洪水,單純加固堤壩是不保險的。
 

解決的方式?
  曾經(jīng)處理過公司內(nèi)的一個老系統(tǒng),表的行數(shù)達到十萬左右,由于采用上面的設(shè)計方式,雖然已經(jīng)盡可能優(yōu)化了索引,但查詢分頁時,仍然需要十秒左右。我單獨建了一個新表,將document_context這個字段移到新表中,在原表中加一個對應(yīng)的外鍵列,經(jīng)過處理后,分頁顯示響應(yīng)時間降到毫秒級以內(nèi)。(二進制數(shù)據(jù)的轉(zhuǎn)移是無法使用普通 的數(shù)據(jù)導(dǎo)入導(dǎo)出方式的,我的方法是復(fù)制該表,然后再修改復(fù)制后的表結(jié)構(gòu))
  因為這個大字段,在最常用的列表顯示中是根本不需要關(guān)心的,僅當用戶需要查看某一記錄的具體信息時,才需要調(diào)入該字段信息。因此分表后,顯著提高了分頁性能。

在我現(xiàn)在開發(fā)的所有的系統(tǒng)中,我都采用了上述的方式,這樣做屬于未雨綢繆,一旦系統(tǒng)部署后再修改,可能就來不及了。

補充:近日公司的另一套CMS系統(tǒng),已經(jīng)出現(xiàn) 了上述問題。clob字段直接置于業(yè)務(wù)表中,現(xiàn)業(yè)務(wù)表記錄已達20余萬,查詢的速度非常緩慢,被迫采用各種方式來解決。如果當初設(shè)計時就考慮到這方面就不會有這樣的問題了。
PS:解決方案之一是,可以在Pojo中加入構(gòu)造函數(shù),參數(shù)中包含除clob字段外的所有其它字段,通過select new Pojo(field1,field2,.....) from Pojo的方式來處理。但要注意,fieldx不能為集合類型,只能為基本數(shù)據(jù)類型或Po類型。如public Pojo(Long id,String name,User usr,Date createDate){}