欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
分頁(yè) & QueryKey & 定長(cháng)預取

數據庫分頁(yè)查詢(xún)一般分為兩步,

(1)根據查詢(xún)條件,count 記錄總數

(2)根據當前頁(yè)的數據范圍(起始位置offset, 每頁(yè)數據個(gè)數span),從符合查詢(xún)條件的記錄集 取出對應范圍的數據。

一、根據范圍取數據的方法

如果單純用JDBCResultSet中取出一個(gè)指定范圍(offset, span)的數據,可以采用這樣的方法。

ps = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

ps.setMaxRows(offset + span);

rs = ps.executeQuery();

rs.absolute(offset);

while(rs.next())...

 

數據量大的時(shí)候,頁(yè)數很多,offset很大,這種方法不太適合。這時(shí)候,需要使用各數據庫的native SQL特性。

我們來(lái)看Hibernate dialect package的類(lèi),支持了各種數據庫的getLimitString方法。這里舉MysqlOracle的例子。假設查詢(xún)語(yǔ)句為

Select * from message where forum_id = ? and created_time > ? order by created_time desc

 

那么,Mysql limit SQL

Select * from message where forum_id = ? and created_time > ? order by created_time desc

limit ?, ?

后面的兩個(gè)limit ?, ? 分別為 offset, span。

 

Oraclelimit SQL

select * from ( select row_.*, rownum rownum_ from (

select * from message where forum_id = ? and created_time > ? order by created_time desc

) row_ where rownum <= ?) where rownum_ > ?

后面的兩個(gè)limit ?, ? 分別為 offset + span, span。

 

二、緩存 & QueryKey

count語(yǔ)句可以根據查詢(xún)語(yǔ)句自動(dòng)生成,比如

Select count(*) from (

Select * from message where forum_id = ? and created_time > ? order by created_time desc

)

 

這樣的自動(dòng)count語(yǔ)句有些浪費,用了子查詢(xún)不說(shuō),還保留了沒(méi)有必要的order by。最好還是另外提供一個(gè)count語(yǔ)句。

Select count(*) from message where forum_id = ? and created_time > ?

 

在多頁(yè)翻動(dòng)的情況下,這個(gè)count語(yǔ)句要被反復執行。為了提高效率,我把這個(gè)count結果保存在全局緩存中,不僅本Session用戶(hù)可以重復使用,其他用戶(hù)在根據同樣條件翻找message的時(shí)候,也可以重復使用這個(gè)結果。

 

我在持久層中使用通用的QueryKey做為緩存鍵值。

QueryKey分成三個(gè)部分,SQL, Parameters, Range。比如:

Query Key:

SQL : Select count(*) from message where forum_id = ? and created_time > ?

Parameters : [buaawhl, time long value]

Range: (0, 1)

 

這個(gè)QueryKey的效率很關(guān)鍵。主要是hashCodeequals兩個(gè)方法的效率。

我們知道,當key放在MapHash數據結構中,首先hashCode,然后用equals比較hashCode后面的一串key。

舉個(gè)例子。Key1key2 hashCode一樣,都和key3hashCode不一樣。

[ 101 ] -> key1 -> key2

[ 666 ] -> key3

 

可以看到,hashCode,equals,這兩個(gè)方法都是每次查找緩存都要調用的方法。尤其是equals方法更是重中之重,很可能需要被調用多次。

hashCode的優(yōu)化實(shí)現相對來(lái)說(shuō)比較簡(jiǎn)單,只要根據QueryKey中各部分的不同,盡量實(shí)現hashCode取值的擴散化,降低hashCode的重復率就可以了。

關(guān)鍵是equals的實(shí)現方案。這里有個(gè)原則,越小的結構越先比較,可以提高比較速度。

QueryKey中的parametersrange比較好辦。每次equals比較的時(shí)候,先比較range,如果不相等,返回false; 如果相等,再比較Parameters,如果有一個(gè)parameter value不相等,返回false。這樣,我們可以用很短的時(shí)間開(kāi)銷(xiāo) 過(guò)濾掉一大批不相等的QueryKey。

但是parametersrange都相等的時(shí)候,我們還是無(wú)可避免的要比較SQL。Stringequals方法如下:

// from jdk src

//這個(gè)方法沒(méi)有比較hashCode,直接比較長(cháng)度和字符

    public boolean equals(Object anObject) {

        if (this == anObject) {

            return true;

        }

        if (anObject instanceof String) {

            String anotherString = (String)anObject;

            int n = count;

            if (n == anotherString.count) {

                char v1[] = value;

                char v2[] = anotherString.value;

                int i = offset;

                int j = anotherString.offset;

                while (n-- != 0) {

                    if (v1[i++] != v2[j++])

                        return false;

                }

                return true;

            }

        }

        return false;

    }

我們看到,兩個(gè)相同的長(cháng)String具有不同的reference,那么比較起來(lái)是相當消耗時(shí)間的。所以說(shuō),字符串比較,不怕不同,就怕相同。大部分情況下,不同的String的長(cháng)度不同,或者前幾個(gè)字符串開(kāi)始就不相同,很快就能夠得出比較結果。

當然也有這種情況,兩個(gè)SQL String都很長(cháng),而且長(cháng)度相等,而且前面大部分字符相同的時(shí)候,到了后面才有字符的不同。比如,

Select * from message where forum_id = ? and created_time > ? order by created_time desc

Select * from message where forum_id = ? and created_time > ? order by updated_time desc

這兩個(gè)String的長(cháng)度相等,前面大部分也相等,只有走到cre upd 的時(shí)候,才能比較出不相同。如果兩個(gè)字符串內容一樣,那更是要走到頭,才能判斷出兩個(gè)字符串完全一樣了。

 

我的第一個(gè)做法就是,盡量使用static final String做為QueryKeySQL。這樣兩個(gè)SQLreference如果相等,那么可以迅速判斷出兩個(gè)SQL相同。

這個(gè)做法只能處理事先定義好的SQL語(yǔ)句,但實(shí)際需求中,存在很多需要動(dòng)態(tài)拼接SQL的情況,不可能做到所有相同的SQL具有相同的reference。

我又采取了第二個(gè)做法:分而治之,把一個(gè)SQL String拆分成多個(gè)SQL常量的數組;泛化SQL的類(lèi)型,SQL不限制為String類(lèi)型,也可以是String[]類(lèi)型。

比如。

String[] sql1 = {

“Select * from message where forum_id = ?”,

“ and created_time > ?”,

“ order by ”,

“created_time”,

“desc”

};

String[] sql2 = {

“Select * from message where forum_id = ?”,

“ and created_time > ?”,

“ order by ”,

“created_time”,

“desc”

};

String[] sql3 = {

“Select * from message where forum_id = ?”,

“ and created_time > ?”,

“ order by ”,

“updated_time”,

“desc”

};

 

這個(gè)時(shí)候,比較sql1sql2和sql3的效率就會(huì )大大提高,雖然sql1 sql2兩個(gè)數組的長(cháng)度相等,還是要一個(gè)元素一個(gè)元素的比較,但由于里面大量用到了String常量,相同的String常量具有相同的reference,所以5步下來(lái),就可以判斷出sql1sql2數組的元素是完全相等的;4步下來(lái),加上第一個(gè)字符的比較,就可以判斷sql1sql3的第4個(gè)元素是不相等的。

 

我們看到,做法1和做法2,能夠提高SQL的比較效率,大部分情況下,也許比parameters的比較還快。

三、定長(cháng)預取

多用戶(hù)訪(fǎng)問(wèn)同一頁(yè)面的可能性比較大的情況下,比如,論壇的某些熱門(mén)話(huà)題,很可能被多人同時(shí)翻閱。這時(shí)候,如果把根據范圍取出的數據對象List也按照QueryKey存入緩存中,那么就可以大大提高響應速度,減輕數據服務(wù)器負擔,當然,你的Web Server的內存負擔也大大增加了。:-)

我們進(jìn)一步考慮下面兩種情況:

1. 用戶(hù)自定義頁(yè)面記錄數

一般來(lái)說(shuō),用戶(hù)可以自定義自己的每頁(yè)顯示記錄個(gè)數,比如,有些用戶(hù)喜歡每頁(yè)20條,有的喜歡每頁(yè)10條。

假設用戶(hù)A翻到一個(gè)論壇的第一頁(yè),顯示1 – 20條信息;用戶(hù)B翻到同一個(gè)論壇的第一頁(yè),顯示1 – 10條信息。這個(gè)時(shí)候,緩存的命中率是很低的。用戶(hù)A和用戶(hù)B無(wú)法共享緩存信息。因為他們的range(span)總是不同,QueryKey永遠不可能相同。

 

2. 記錄很多、每頁(yè)記錄數過(guò)少

假設一個(gè)論壇里面有1000條信息,每頁(yè)顯示10條,那么共有100頁(yè)。如果用戶(hù)一頁(yè)一頁(yè)的翻動(dòng),每次程序發(fā)出一個(gè)span大小為10Query請求,取出10條記錄,根據QueryKey緩存起來(lái)。由于頁(yè)面記錄數過(guò)少,每次數據庫查詢(xún)的效率很低,緩存命中率也很低。

 

為了提高緩存命中率,并且順便實(shí)現數據預取功能,我們可以采取 同一定長(cháng)Span的方案。比如,還是上面的例子,我們在程序中設定統一Span大小為100。

當用戶(hù)A請求1 – 10的記錄的時(shí)候,程序判斷這個(gè)落在 1 – 100的范圍內,那么用range (1, 100)獲取100條記錄,把前面的10條返回給用戶(hù)。當用戶(hù)A翻了一頁(yè),請求11 – 20的記錄的時(shí)候,程序判斷還是落在 1 – 100的范圍內,而且已經(jīng)存在于緩存中,那么直接把對應的11 – 20條返回給用戶(hù)A就可以。

當用戶(hù)B 請求1 – 20的記錄的時(shí)候,程序判斷這個(gè)落在 1 – 100的范圍內,而且已經(jīng)存在于緩存中,那么直接把對應的1 – 20條返回給用戶(hù)B就可以。

 

可以看到,這種定長(cháng)預取方案能夠大大提高數據庫查詢(xún)的效率和緩存的命中率。

 

 


[點(diǎn)擊此處收藏本文]
發(fā)表于 2005年01月08日 2:56 PM

shaokun305 發(fā)表于2005-02-22 9:04 AM  
Ping Back來(lái)自:blog.csdn.net

buaawhl 發(fā)表于2005-05-31 8:37 AM  
Ping Back來(lái)自:blog.csdn.net

lovefanx 發(fā)表于2005-06-01 11:40 PM  
你說(shuō)的這個(gè)把sql串變成字符串數組來(lái)比較的方法,還是頭回聽(tīng)說(shuō),確實(shí)很長(cháng)見(jiàn)識:)

buaawhl 發(fā)表于2005-06-02 11:41 AM  

thanks. :-)
隨著(zhù)對Hibernate, JDO的逐步深入了解,還有對象數據庫Cache‘ 和 Objectivity的了解,Lightor的緩存策略還在發(fā)展中。
(1)為了進(jìn)一步提高命中率,減少不必要的緩存清空,或者減少緩存清空的粒度,
(2) 為了讓相關(guān)的數據庫對象、頁(yè)面資源對象都放在一起,共同清空。
Lightor引入了多級緩存機制。

帶來(lái)的好處是,性能提高;Cluster支持良好;命中率高。
帶來(lái)的壞處是,Cache控制代碼對 業(yè)務(wù)邏輯代碼的侵入性高,編程復雜。所以,要控制好,一般把Cache 控制代碼局限在DAO層以?xún)?,不擴散到DAO層以外。當然,有的時(shí)候,為了更好的性能/命中率,Cache 控制代碼有可能需要進(jìn)入業(yè)務(wù)邏輯,這個(gè)就一定要做好代碼的包裝:提供一個(gè)沒(méi)有緩存控制的業(yè)務(wù)方法;提供一個(gè)有緩存控制的包裝方法。對于一些通用的緩存控制,可以采用AOP實(shí)現。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
高效分布式數據庫緩存經(jīng)典解決方案
Primary key 與Unique Key
Q:SQL該怎么用? · 語(yǔ)雀
敖丙工作以來(lái)總結的大廠(chǎng)SQL調優(yōu)姿勢
sequence 的測試
/usr/local/bin/php -f index.php
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久