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

這張表的結構,表面上看起來,從數據庫設計角度和對應的JAVA類的設計來講,都是沒有問題的。
但實際上,這里面隱藏著兩個比較嚴重的問題!
一、不能完全跨數據庫

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

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

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

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

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