【測試】兩種資料庫,四種分頁演算法的效率比較
分頁演算法本身沒有什麼快慢之分,對反應速度起到決定作用的是——能否有效地利用索引!
演算法 |
評價 |
缺點 |
適用的資料庫 |
---|---|---|---|
max |
效率最高的 |
只能有一個排序欄位 |
Excel、Access、SQL Server 2000(2005) |
顛倒Top |
適用範圍最廣的 |
最後一頁多資料 |
Excel、Access、SQL Server 2000(2005) |
表變數 |
最大失所望的 |
太多了 |
SQL Server 2000(2005) |
Row_Number |
只適合SQL Server2005 |
只能用在一種資料庫 |
SQL Server 2005 |
一、max
缺點就是隻能有一個排序欄位,而且排序欄位的值不能有重複值,或者說可以有重複值,但是不能在一頁的最上面、最後重複。優點就是很容易設定索引,按那個欄位排序就把那個欄位設定上索引就可以了,設定索引後,分頁速度會大大提高,記錄越多越明顯,因為利用了索引。索引的作用是什麼?簡單地說就是,先站排,再找個頭最高的。假設說要找到一個班級裡面個頭最高的,那要怎麼辦呢,先讓同學按照大小個站排,然後誰是最高的就一目瞭然了。設定好了索引就相當於已經排好了隊,剩下的就快多了。
二、顛倒Top
這個是顛顛倒倒的一種優化,必須有一個主鍵,且不能是複合主鍵。由於大部分情況都比較好設定索引,也可以利用索引,所以分頁效果也是可以的。
三、表變數
由於一直在使用顛倒top法和定位法,所以一直對錶變數不感冒,由於是吳旗娃的分頁控制元件推薦的一種演算法,這次升級的時候還是去研究了一下。仔細看了一下,缺點還真是多。由於表變數的思路是,要把主鍵和自增欄位放在表變數裡面,然後利用自增欄位來分頁。就像上面的例子,要先站排,然後報數,然後根據報數來提取資料。這樣就帶來了很多的缺點:
1、表必須有一個主鍵,且不能是複合主鍵。複合主鍵的話,就不好提取資料了。 2、主鍵的型別會影響分頁演算法的編寫。int的和datetime的,在定義表變數的時候欄位型別是不一樣的。 3、如果只按照主鍵排序的話,那麼主鍵的索引就完全被浪費掉了。原先已經排好隊了,卻又讓她們重新排一遍。 4、越往後翻頁,需要往表變數裡存放的資料也就越多,放的多倒無所謂,其他的演算法也會放一些資料到“臨時表”裡面,但是問題在於,自增欄位沒有索引,加的資料越多速度也就自然越慢。 5、只能在SQL Server 2000和SQL Server2005裡面使用,而且在2005裡面速度明顯沒有Row_Number快,吳旗娃的那個網站推薦的分頁演算法,都分成了兩個版本,SQL2000的和SQL2005的,對於後者推薦使用Row_Number。
說了這麼多的缺點,不會一點優點都沒有吧,有點恐怕就是編寫起來比較容易吧,便於理解。顛倒Top就不好理解。所以說這個是最大失所望的分頁演算法。
四、Row_Number
這個我只會用,內部原理也不太理解,大多數情況效率也還行,但是有一次發現他不能利用索引,鬱悶。
光說不練,恐怕大家也不太相信,尤其說了表變數那麼的壞話,那麼下面就用測試來證明一下,看看各個分頁演算法的表現。要說測試也真麻煩,資料庫至少就有三個,分頁演算法有四個,資料上呢又有單欄位排序、多欄位排序,單表分頁、多變關聯分頁,少量資料和海量資料,能否利索引。
3*4*2*2*2*2 = 192 。這麼多是不是有點暈,先簡化一下吧,這裡先用SQL Server2000 測試一下單表海量資料的情況。
三種分頁演算法、單表、海量資料(二百萬吧,少了對比不出來效果)。
先介紹一下測試環境
資料庫: SQL Server2000
IDE:VS2008。
CPU:AMD3000+
記憶體:DDR2 1G
硬碟:串列埠160G
三種分頁演算法:Max、表變數、顛倒top。
我們使用Northwind資料庫,Products表進行測試,自帶的資料不夠,我們來複制點資料吧,用insert into select 的方式,新增2523136條記錄,一頁顯示15條記錄,一共有168210頁 。那麼我們就記錄一下第一次載入、前幾頁、前100頁、第1000頁、第10000頁、最後幾頁和最後一頁的用時和記憶體佔用情況。
這是單欄位排序的測試結果,按照 ProductID 排序。 單位:毫秒。
分頁演算法 |
第一次 |
前幾頁 |
前100頁 |
1000頁 |
10000頁 |
100000頁 |
最後幾頁 |
最後一頁 |
---|---|---|---|---|---|---|---|---|
MaxMin |
953/40M |
400/40M |
0-15/50M |
0-15/50M |
78-93/68M |
3.62s/210M 765/210M |
2.80s/310M 1.28s/310M |
5.62s/310M 1.28s/310M |
顛倒Top |
875/40M |
400/40M |
0-15/50M |
15-31/50M |
281/69M 93-125/69M |
2.96s/210M 1.3s/210M |
3.78s/310M 1.7s/310M |
15(特殊處理) |
表變數 |
968 |
468 |
0-15/50M |
11.18s/55M 93-109/59M |
24.265s/76M 859/77M |
超時了 45.171s/230M 11.78s/240M 8.281s |
這個就不測試了 |
這個就不測試了 |
記錄說明:
1、如果一個格里面有兩個時間,那麼前面的表示大範圍跳頁(比如從100頁跳到1000頁)需要的時間,後面是顯示下一頁需要的時間。
2、前面的是執行時間,後面的是記憶體增量。
3、第一次執行的時候SQL Server需要載入一些資料到記憶體裡面,所以時間比較長。
4、第一頁的時候需要使用Count(*)來統計總記錄數,所以時間也有點長。而在訪問其他頁的時候就不用統計總記錄數了,所以時間會很快。
5、CPU的佔用率就不記錄了,基本上都佔滿了,看來AMD3000+有點弱了。
第一頁的SQL語句:
select top 15 * from Products order by ProductID
MaxMin的SQL語句: select top 15 * from Products where productid>= (SELECT max(productid ) from (select top 526 productid from Products order by productid ) as t ) order by productid
顛倒Top的SQL語句: select * from Products where productid in ( select top 15 productid from ( select top 420 productid from Products order by productid ) as t order by t.productid desc )order by productid
對最後一頁得分頁演算法作了特殊處理,目的是去掉bug,並不是為了提高速度。
顛倒Top的顯示最後一頁的SQL語句 select * from ( select top 1 * from Products order by productid desc ) as t order by t.productid
表變數的SQL語句: declare @tt table(id int identity(1,1),nid int) insert into @tt(nid) select top 2130 ProductID from Products order by ProductID select * from Products t1, @tt t2 where t1.ProductID =t2.nid and t2.id between 2116 and 2130
還真的是不行,從第一萬頁跳轉到第十萬頁的時候,很不幸,等待了45.171秒之後超時了,由於資料訪問函式庫在遇到異常的時候並不丟擲異常,所以能夠得到執行的時間,通過檢視錯誤日誌,得知“超時時間已到。在操作完成之前超時時間已過或伺服器未響應。” 再次重新整理才得到了資料。
在測試的時候表變數的分頁時間很不穩定,一會快一會慢的,大範圍跳頁的時候還總是超時。而顛倒Top就比較穩定。
這是多欄位排序的測試結果,按照 UnitPrice,ProductID desc 排序。設定了索引:UnitPrice,ProductID desc 單位:毫秒。
分頁演算法 |
第一次 |
前幾頁 |
100頁 |
1000頁 |
10000頁 |
100000頁 |
最後幾頁 |
最後一頁 |
---|---|---|---|---|---|---|---|---|
顛倒Top |
375/55M |
15-31/57M |
15-31/60M |
62-78/63M |
687/63M 486/64M |
5.18s/65M 4.3s/65M |
8.15s/66M 7-8s/66M |
15(特殊處理) |
表變數 |
968 |
15-31/58M |
1.31/59M 46/60M |
12.17s/60M 93-125/60M |
真實搞不懂,怎麼超時了 27.39s/76M 8-9s/77M 900/77M |
超時了 45.171s/100M 10.2s/100M 9.3s |
27.2s/100M 9.3s |
這個就不測試了 |
這是SQL Server 2005 的測試結果,多欄位排序,按照 UnitPrice,ProductID desc 排序。設定了索引:UnitPrice,ProductID desc 單位:毫秒。
分頁演算法 |
第一次 |
前幾頁 |
100頁 |
1000頁 |
10000頁 |
100000頁 |
最後幾頁 |
最後一頁 |
---|---|---|---|---|---|---|---|---|
顛倒Top |
375/55M |
15-46/57M |
15-46/100M |
78-93/110M |
531/117M 466/117M |
5-8s/118M |
7-10s/120M |
15(特殊處理) |
表變數 |
968 |
31-46/100M |
2.62/100M 0.4s 或1.6s/100M |
17.34s/110M 93-156/110M |
25.57s/76M 1.25s/77M |
超時了 22.82s/130M 15.2s/130M 9.3s |
27.2s/100M 9.3s |
這個就不測試了 |
Row_Number |
500 |
15/93M |
546 15-31 |
4.82s/200M 93-106/210M |
3.15/265M 3.96/263M |
比較慢,總超時,不想測了。 |
這裡增加了Row_Number演算法的測試,
Row_Number的SQL語句:
with t_pager as (select myIndex = ROW_NUMBER() OVER (ORDER BY UnitPrice,ProductID desc ),* from Products ) select * from t_pager where myIndex between 150031 and 150045
我都懷疑這個測試結果,如果測試結果沒有問題的話,那麼就說明“顛倒top”的效率是最高的,而且適用範圍也很廣。
但是我還是比較懷疑這個結果,難道 Row_Number會這麼慢?是不是拼接出來的SQL語句有什麼問題?或者那個環節出了問題?
===============================================================================
測試用的程式碼
先建立一個基類,定義一個GridView、一個QuickPager分頁控制元件和一個標籤,然後override OnInit()函式,再新增一個事件就ok了。因為是對同一個表進行分頁,所以分頁控制元件的屬性設定都是一樣的,只是分頁演算法的屬性不同,那麼我們就可以把相同的設定放在基類裡面,不同的放在具體的頁面裡。
QuickPager 分頁控制元件在基類裡面的使用方法
public class BaseList : System.Web.UI.Page
{
protected JYK.Controls.QuickPager myPager = null;
protected System.Web.UI.WebControls.GridView GV = null;
protected Label lbl = null;
protected DateTime dt1;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//設定分頁控制元件的屬性
myPager.ControlGrid = GV;
myPager.TableName = "Products";
myPager.TableShowColumns = "*";
myPager.TableIDColumns = "ProductID";
//myPager.TableOrderColumns = "ProductID";ProductName
myPager.TableOrderColumns = "UnitPrice,ProductID desc";
myPager.PageSize = 15;
//新增事件
myPager.GridBinded += new JYK.Controls.QuickPager.EventPageChange(myPager_GridBinded);
dt1 = DateTime.Now;
}
void myPager_GridBinded(object sender, JYK.Controls.Page.PageArgs e)
{
TimeSpan ts = DateTime.Now - dt1;
lbl.Text = "秒:" + ts.Seconds + ",毫秒:" + ts.Milliseconds;
lbl.Text += "<BR>" + myPager.GetPagerSQL;
}
void myPager_PageChanged(object sender, JYK.Controls.Page.PageArgs e)
{
}
}
然後建立一個aspx頁面,拖拽進來三個控制元件就可以了
<asp:GridView ID="GV" runat="server" Width="100%">
</asp:GridView>
<JYK:QuickPager ID="myPager" runat="server" />
<asp:Label ID="lbl" runat="server" Text=" "></asp:Label>
public partial class lst_Max : BaseList
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack )
myPager.SetSQLKind = JYK.Controls.Page.myPageSQLKind.Row_Number;
}
}
我們還可以順便對比一下SQL 2000 和 SQL 2005,好像在這種情況下05並沒有太多的優勢。Row_Number也沒有想象中的那麼好,也許是我寫的不對,也許四、五個排序欄位的時候才能體現出來優勢?不過不管怎麼說,更換分頁演算法對於QuickPager來說是很容易的事情,如果發現速度慢了,那麼就換成其他的分頁演算法試一試,再加個索引試一試,呵呵。
QuickPager 分頁控制元件 v2.0.0.8的下載地址: