[引用]http://www.javaeye.com/article/27917
在數(shù)據(jù)庫(kù)中,經(jīng)常需要用到大字段類(lèi)型,如oracle中l(wèi)ong/blob/clob,sqlserver中text/image,mysql中的text/longtext/clob/blob。
存儲(chǔ)的信息大概主要是兩類(lèi),一類(lèi)是長(zhǎng)文本,如大段的文字,普通的varchar最長(zhǎng)只能存儲(chǔ)4000個(gè)漢字,已經(jīng)不能滿(mǎn)足要求;另一類(lèi)是存儲(chǔ)二進(jìn)制信息,如上傳的文件等。
那么假如現(xiàn)在有一個(gè)表,記錄某人發(fā)布的文檔信息,字段包括:發(fā)布人,發(fā)布時(shí)間,文檔標(biāo)題,文檔內(nèi)容(實(shí)際中還會(huì)有其它字段),一般建表如下(sqlserver):
create table document(
id int identity(1,1) not null,
createuser_id int,
document_title varchar(255),
document_context text);
這張表的結(jié)構(gòu),表面上看起來(lái),從數(shù)據(jù)庫(kù)設(shè)計(jì)角度和對(duì)應(yīng)的JAVA類(lèi)的設(shè)計(jì)來(lái)講,都是沒(méi)有問(wèn)題的。
但實(shí)際上,這里面隱藏著兩個(gè)比較嚴(yán)重的問(wèn)題!
一、不能完全跨數(shù)據(jù)庫(kù)
why?問(wèn)題出在需要查重(distinct)的時(shí)候。
在需要查重時(shí),采用純jdbc技術(shù),則可以自定義要查重的字段,如select distinct id,createuser_id,document_title from document。而當(dāng)采用hibernate時(shí),若不想自已創(chuàng)建若干個(gè)新的Pojo或者使用Object[]方式來(lái)處理數(shù)據(jù),則只能使用select distinct d from document as d這樣的語(yǔ)句,而hibernate會(huì)將其解析為類(lèi)似:select distinct id,createuser_id,document_title,document_context from document。
問(wèn)題就出在這個(gè)document_context字段上!
對(duì)于oracle/mysql來(lái)講,hibernate生成的sql是可以執(zhí)行的。但對(duì)于sqlserver來(lái)講,是不允許在text/image列上進(jìn)行distinct查詢(xún)的!
因此系統(tǒng)在sqlserver上部署時(shí),當(dāng)需要查重時(shí)則會(huì)出錯(cuò)。
我也是在開(kāi)發(fā)一個(gè)系統(tǒng)即將完成的時(shí)候,在sqlserver上部署才發(fā)現(xiàn)的該問(wèn)題,最后修改了所有類(lèi)似的Pojo和表結(jié)構(gòu)以及DAO,相關(guān)的顯示層代碼也進(jìn)行了修改。工程量可謂不小。當(dāng)然如果你用不到查重語(yǔ)句,是一點(diǎn)不受影響的。
二、嚴(yán)重影響列表顯示和統(tǒng)計(jì)的效率
影響一張表的查詢(xún)速度的,除了行數(shù),還包括表所占的物理空間的大小。此表在數(shù)據(jù)量較小時(shí),在查詢(xún)方面感覺(jué)不到明顯的差異。但是如果document_context字段所存儲(chǔ)的數(shù)據(jù)都是大段文本或較大的文件時(shí),會(huì)導(dǎo)致表的物理空間迅速變大,該字段所占用的空間有可能達(dá)到整表所占空間的90%以上。在此基礎(chǔ)上,如果行數(shù)再增加到數(shù)十萬(wàn)、上百萬(wàn)級(jí)時(shí),整個(gè)表所占的空間將達(dá)到一個(gè)驚人的數(shù)字。
保守估計(jì),一條記錄占用的空間平均為10K的話(huà),一萬(wàn)條記錄將占用100M的空間,一百萬(wàn)條記錄將占用10G!在此表上的CRUD操作,亦將變慢,查詢(xún)的速度亦會(huì)受到非常大的影響 。當(dāng)然通過(guò)提高服務(wù)器本身的硬件性能和優(yōu)化索引,可以提高查詢(xún)速度,但面對(duì)無(wú)法預(yù)知的巨大洪水,單純加固堤壩是不保險(xiǎn)的。
解決的方式?
曾經(jīng)處理過(guò)公司內(nèi)的一個(gè)老系統(tǒng),表的行數(shù)達(dá)到十萬(wàn)左右,由于采用上面的設(shè)計(jì)方式,雖然已經(jīng)盡可能優(yōu)化了索引,但查詢(xún)分頁(yè)時(shí),仍然需要十秒左右。我單獨(dú)建了一個(gè)新表,將document_context這個(gè)字段移到新表中,在原表中加一個(gè)對(duì)應(yīng)的外鍵列,經(jīng)過(guò)處理后,分頁(yè)顯示響應(yīng)時(shí)間降到毫秒級(jí)以?xún)?nèi)。(二進(jìn)制數(shù)據(jù)的轉(zhuǎn)移是無(wú)法使用普通 的數(shù)據(jù)導(dǎo)入導(dǎo)出方式的,我的方法是復(fù)制該表,然后再修改復(fù)制后的表結(jié)構(gòu))
因?yàn)檫@個(gè)大字段,在最常用的列表顯示中是根本不需要關(guān)心的,僅當(dāng)用戶(hù)需要查看某一記錄的具體信息時(shí),才需要調(diào)入該字段信息。因此分表后,顯著提高了分頁(yè)性能。
在我現(xiàn)在開(kāi)發(fā)的所有的系統(tǒng)中,我都采用了上述的方式,這樣做屬于未雨綢繆,一旦系統(tǒng)部署后再修改,可能就來(lái)不及了。