Timestamp 與 Date 變量綁定與Oracle的自動分區

很久沒有更新博客了,實際上是工做中遇到的不少問題在Google上都能找到答案,也就沒有記錄下來的必要了。今天主要想聊一下在實際的系統中遇到的Oracle數據庫的問題,但願對你們有一點點幫助就好。java

我首先描述一下我所遇到的場景:咱們的數據庫用的是Oracle 11g,我想你們立馬就對它的自動分區(Interval)有了基本的認識了,這是一個很是棒的功能,免除了在建表時弄一大堆建Range分區的代碼,也免除了之後對數據庫進行分區擴充的麻煩。固然利用JOB也是能夠完成分區擴展的,可是既然Oracle提供了這麼好的工具,何須跟本身過不去呢,實際上是我本身太懶不想寫。那麼我再說一下咱們具體遇到的問題,咱們有一張相似這樣的分區表:程序員

CREATE TABLE TICKET(
    TCT_ID    NUMBER,
    TCT_DATE    DATE,    
    TCT_CODE VARCHAR2(5),
    TCT_DEPARTURE VARCHAR2(5),
    TCT_DESTINATION VARCHAR2(5)
) partition by range
 (TCT_DATE)
    interval
 (NUMTODSINTERVAL(1,'DAY'))
 (partition
         PT_TCT_DAY_01
        values less than (TO_DATE('2014-07-27','YYYY-MM-DD'))
         nocompress);

看着挺複雜,其實就是一個簡單的自動分區表,分區鍵爲TCT_DATE(訂票日期)。那麼爲何要用自動分區表呢,其實就是由於數據太多了,好比有10億條,用一張表即便有再好的索引,查詢速度也會降下來的。這個分區表天天都會生成一個分區,那麼只要在這個表上建一個local index,這裏建了一個前綴本地索引:sql

CREATE INDEX IDX_COMBINE 
ON TICKET(TCT_DATE ,TCT_CODE ,TCT_DEPARTURE ,TCT_DESTINATION) LOCAL

就實現很不錯的查詢速度了,在10億條數據的狀況下,查詢速度基本能夠控制在10ms以內,這也基本是極限值了。咱們的項目在我接手以前已經運行了1年多了,一開始速度跟預期的同樣,查詢的速度基本也就在10ms以內,這兩天項目經理忽然跟我說咱們的系統插入極慢,讓我去調優一下。數據庫

接到任務,跟咱們的DBA要AWR,很快就發現系統的瓶頸出如今一條查詢語句上:編程

SELECT * FROM TICKET T 
WHERE T.DATE=:D1 
AND T.TCT_CODE=:D2 
AND T.TCT_DEPATURE=:D3 
AND T.TCT_DESTINATION=:D4;

乍一看以爲挺好,進行了變量綁定,可是查詢的速度飆升到200ms附近,同時物理讀很是高,邏輯讀也不是很低,而且db sequence file 讀的頻率也是很是高,這就說明兩點:oracle

  1. 這個查詢語句有可能進行了全表掃,沒有分區消除;less

  2. 這個查詢語句讀取了大量的索引數據,有可能遍歷了全部的分區的本地索引;工具

綜合這條sql的SQL AWR REPORT基本能夠得出一個結論,沒有實現分區消除spa

可是這個語句明明把分區條件都帶上了,是一個前綴索引,那爲何Oracle要進行全表掃,這個讓人很是的費解,我進行了一下的分析:code

1. 這個查詢語句效率底下的緣由就是由於沒有分區消除,遍歷了全部的索引,這也是爲何剛開始程序效率還不錯,可是隨着數據愈來愈多,程序愈來愈慢;

2. 可是爲何這裏明明有分區條件,但就是不走分區呢?映入腦海的第一個緣由就是傳進來的分區條件值沒法匹配;

3. 遵循以上的思考,我查看了一下該項目的源代碼,發現問題出如今這樣的一個語句上,這個錯誤十分隱蔽,實在是很難找:

ps.setTimestamp(1, new Timestamp(date.getTime()));

數據庫中TCT_DATE是DATE類型,該語句實際上就是往sql裏面注入java.sql.Date類型的值,也就是給TCT_DATE賦值,因爲咱們在用Java編程的時候通常比較習慣用java.util.Date,這個地方把date轉換成了Timestamp,好了問題就出現了,因爲TIMESTAMP是要精確到毫秒的,而DATE只精確到秒,這就形成這二者沒法匹配,形成的結果就是沒法進行分區消除;

4. 如何解決這個問題呢?我進行了一下的改造:

ps.setDate(1, new java.sql.Date(date.getTime()));

這裏利用了java.sql.Date,問題解決,查詢語句立馬進入10ms之內,看來是乖乖地走分區條件了!

只能感嘆千里之堤毀於蟻穴啊,後來者且行且仔細吧。其實這裏反映了一個以前的程序員對java.util.Date 和java.sql.Date的區別不是很清楚,此外對Timestap與Date 之間的關係也很模糊,其實這裏徹底能夠利用ojdbc中oracle.sql.Date來代替。這一點小小的盲點,帶來的後果也是很是嚴重的,之後學東西仍是要多消除這些盲點。

相關文章
相關標籤/搜索