国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數(shù)據(jù)庫 > MySQL > 正文

MySQL中分頁優(yōu)化的實例詳解

2024-07-24 13:07:11
字體:
來源:轉載
供稿:網(wǎng)友

這篇文章主要介紹了MySQL中分頁優(yōu)化的實例詳解,分頁優(yōu)化是MySQL優(yōu)化當中的重點,需要的朋友可以參考下

通常,我們會采用ORDER BY LIMIT start, offset 的方式來進行分頁查詢。例如下面這個SQL:

 

 
  1. SELECT * FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 100, 10; 

或者像下面這個不帶任何條件的分頁SQL:

 

 
  1. SELECT * FROM `t1` ORDER BY id DESC LIMIT 100, 10; 

一般而言,分頁SQL的耗時隨著 start 值的增加而急劇增加,我們來看下面這2個不同起始值的分頁SQL執(zhí)行耗時:

 

 
  1. yejr@imysql.com> SELECT * FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 500, 10; 
  2. … 
  3.  
  4. 10 rows in set (0.05 sec) 
  5.  
  6.  
  7. yejr@imysql.com> SELECT * FROM `t1` WHERE ftype=6 ORDER BY id DESC LIMIT 935500, 10; 
  8. … 
  9.  
  10. 10 rows in set (2.39 sec) 

可以看到,隨著分頁數(shù)量的增加,SQL查詢耗時也有數(shù)十倍增加,顯然不科學。今天我們就來分析下,如何能優(yōu)化這個分頁方案。 一般滴,想要優(yōu)化分頁的終極方案就是:沒有分頁,哈哈哈~~~,不要說我講廢話,確實如此,可以把分頁算法交給Sphinx、Lucence等第三方解決方案,沒必要讓MySQL來做它不擅長的事情。 當然了,有小伙伴說,用第三方太麻煩了,我們就想用MySQL來做這個分頁,咋辦呢?莫急,且待我們慢慢分析,先看下表DDL、數(shù)據(jù)量、查詢SQL的執(zhí)行計劃等信息:

 

 
  1. yejr@imysql.com> SHOW CREATE TABLE `t1`; 
  2. CREATE TABLE `t1` ( 
  3. `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
  4. ... 
  5. `ftype` tinyint(3) unsigned NOT NULL
  6. ... 
  7. PRIMARY KEY (`id`) 
  8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
  9.  
  10. yejr@imysql.com> select count(*) from t1; 
  11. +----------+ 
  12. count(*) | 
  13. +----------+ 
  14. | 994584 | 
  15. +----------+ 
  16.  
  17. yejr@imysql.com> EXPLAIN SELECT * FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 500, 10/G 
  18. *************************** 1. row *************************** 
  19. id: 1 
  20. select_type: SIMPLE 
  21. table: t1 
  22. type: index 
  23. possible_keys: NULL 
  24. keyPRIMARY 
  25. key_len: 4 
  26. ref: NULL 
  27. rows: 510 
  28. Extra: Using where 
  29.  
  30. yejr@imysql.com> EXPLAIN SELECT * FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 935500, 10/G 
  31. *************************** 1. row *************************** 
  32. id: 1 
  33. select_type: SIMPLE 
  34. table: t1 
  35. type: index 
  36. possible_keys: NULL 
  37. keyPRIMARY 
  38. key_len: 4 
  39. ref: NULL 
  40. rows: 935510 
  41. Extra: Using where 

可以看到,雖然通過主鍵索引進行掃描了,但第二個SQL需要掃描的記錄數(shù)太大了,而且需要先掃描約935510條記錄,然后再根據(jù)排序結果取10條記錄,這肯定是非常慢了。 針對這種情況,我們的優(yōu)化思路就比較清晰了,有兩點:

1、盡可能從索引中直接獲取數(shù)據(jù),避免或減少直接掃描行數(shù)據(jù)的頻率

2、盡可能減少掃描的記錄數(shù),也就是先確定起始的范圍,再往后取N條記錄即可

據(jù)此,我們有兩種相應的改寫方法:子查詢、表連接,即下面這樣的:

#采用子查詢的方式優(yōu)化,在子查詢里先從索引獲取到最大id,然后倒序排,再取10行結果集

#注意這里采用了2次倒序排,因此在取LIMIT的start值時,比原來的值加了10,即935510,否則結果將和原來的不一致

 

 
  1. yejr@imysql.com> EXPLAIN SELECT * FROM (SELECT * FROM `t1` WHERE id > ( SELECT id FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 935510, 1) LIMIT 10) t ORDER BY id DESC/G 
  2. *************************** 1. row *************************** 
  3. id: 1 
  4. select_type: PRIMARY 
  5. table: <derived2> 
  6. type: ALL 
  7. possible_keys: NULL 
  8. keyNULL 
  9. key_len: NULL 
  10. ref: NULL 
  11. rows: 10 
  12. Extra: Using filesort 
  13. *************************** 2. row *************************** 
  14. id: 2 
  15. select_type: DERIVED 
  16. table: t1 
  17. type: ALL 
  18. possible_keys: PRIMARY 
  19. keyNULL 
  20. key_len: NULL 
  21. ref: NULL 
  22. rows: 973192 
  23. Extra: Using where 
  24. *************************** 3. row *************************** 
  25. id: 3 
  26. select_type: SUBQUERY 
  27. table: t1 
  28. type: index 
  29. possible_keys: NULL 
  30. keyPRIMARY 
  31. key_len: 4 
  32. ref: NULL 
  33. rows: 935511 
  34. Extra: Using where 

 

 
  1. #采用INNER JOIN優(yōu)化,JOIN子句里也優(yōu)先從索引獲取ID列表,然后直接關聯(lián)查詢獲得最終結果,這里不需要加10 
  2. yejr@imysql.com> EXPLAIN SELECT * FROM `t1` INNER JOIN ( SELECT id FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 935500,10) t2 USING (id)/G 
  3. *************************** 1. row *************************** 
  4. id: 1 
  5. select_type: PRIMARY 
  6. table: <derived2> 
  7. type: ALL 
  8. possible_keys: NULL 
  9. keyNULL 
  10. key_len: NULL 
  11. ref: NULL 
  12. rows: 935510 
  13. Extra: NULL 
  14. *************************** 2. row *************************** 
  15. id: 1 
  16. select_type: PRIMARY 
  17. table: t1 
  18. type: eq_ref 
  19. possible_keys: PRIMARY 
  20. keyPRIMARY 
  21. key_len: 4 
  22. ref: t2.id 
  23. rows: 1 
  24. Extra: NULL 
  25. *************************** 3. row *************************** 
  26. id: 2 
  27. select_type: DERIVED 
  28. table: t1 
  29. type: index 
  30. possible_keys: NULL 
  31. keyPRIMARY 
  32. key_len: 4 
  33. ref: NULL 
  34. rows: 973192 
  35. Extra: Using where 

然后我們來對比下這2個優(yōu)化后的新SQL執(zhí)行時間:

 

 
  1. yejr@imysql.com> SELECT * FROM (SELECT * FROM `t1` WHERE id > ( SELECT id FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 935510, 1) LIMIT 10) T ORDER BY id DESC
  2. ... 
  3. rows in set (1.86 sec) 
  4. #采用子查詢優(yōu)化,從profiling的結果來看,相比原來的那個SQL快了:28.2% 
  5.  
  6. yejr@imysql.com> SELECT * FROM `t1` INNER JOIN ( SELECT id FROM `t1` WHERE ftype=1 ORDER BY id DESC LIMIT 935500,10) t2 USING (id); 
  7. ... 
  8. 10 rows in set (1.83 sec) 
  9. #采用INNER JOIN優(yōu)化,從profiling的結果來看,相比原來的那個SQL快了:30.8% 

我們再來看一個不帶過濾條件的分頁SQL對比:

 

 
  1. #原始SQL 
  2. yejr@imysql.com> EXPLAIN SELECT * FROM `t1` ORDER BY id DESC LIMIT 935500, 10/G 
  3. *************************** 1. row *************************** 
  4. id: 1 
  5. select_type: SIMPLE 
  6. table: t1 
  7. type: index 
  8. possible_keys: NULL 
  9. keyPRIMARY 
  10. key_len: 4 
  11. ref: NULL 
  12. rows: 935510 
  13. Extra: NULL 
  14.  
  15. yejr@imysql.com> SELECT * FROM `t1` ORDER BY id DESC LIMIT 935500, 10; 
  16. ... 
  17. 10 rows in set (2.22 sec) 
  18.  
  19. #采用子查詢優(yōu)化 
  20. yejr@imysql.com> EXPLAIN SELECT * FROM (SELECT * FROM `t1` WHERE id > ( SELECT id FROM `t1` ORDER BY id DESC LIMIT 935510, 1) LIMIT 10) t ORDER BY id DESC
  21. *************************** 1. row *************************** 
  22. id: 1 
  23. select_type: PRIMARY 
  24. table: <derived2> 
  25. type: ALL 
  26. possible_keys: NULL 
  27. keyNULL 
  28. key_len: NULL 
  29. ref: NULL 
  30. rows: 10 
  31. Extra: Using filesort 
  32. *************************** 2. row *************************** 
  33. id: 2 
  34. select_type: DERIVED 
  35. table: t1 
  36. type: ALL 
  37. possible_keys: PRIMARY 
  38. keyNULL 
  39. key_len: NULL 
  40. ref: NULL 
  41. rows: 973192 
  42. Extra: Using where 
  43. *************************** 3. row *************************** 
  44. id: 3 
  45. select_type: SUBQUERY 
  46. table: t1 
  47. type: index 
  48. possible_keys: NULL 
  49. keyPRIMARY 
  50. key_len: 4 
  51. ref: NULL 
  52. rows: 935511 
  53. Extra: Using index 
  54.  
  55. yejr@imysql.com> SELECT * FROM (SELECT * FROM `t1` WHERE id > ( SELECT id FROM `t1` ORDER BY id DESC LIMIT 935510, 1) LIMIT 10) t ORDER BY id DESC
  56. … 
  57. 10 rows in set (2.01 sec) 
  58. #采用子查詢優(yōu)化,從profiling的結果來看,相比原來的那個SQL快了:10.6% 
  59.  
  60.  
  61. #采用INNER JOIN優(yōu)化 
  62. yejr@imysql.com> EXPLAIN SELECT * FROM `t1` INNER JOIN ( SELECT id FROM `t1`ORDER BY id DESC LIMIT 935500,10) t2 USING (id)/G 
  63. *************************** 1. row *************************** 
  64. id: 1 
  65. select_type: PRIMARY 
  66. table:  
  67. type: ALL 
  68. possible_keys: NULL 
  69. keyNULL 
  70. key_len: NULL 
  71. ref: NULL 
  72. rows: 935510 
  73. Extra: NULL 
  74. *************************** 2. row *************************** 
  75. id: 1 
  76. select_type: PRIMARY 
  77. table: t1 
  78. type: eq_ref 
  79. possible_keys: PRIMARY 
  80. keyPRIMARY 
  81. key_len: 4 
  82. ref: t1.id 
  83. rows: 1 
  84. Extra: NULL 
  85. *************************** 3. row *************************** 
  86. id: 2 
  87. select_type: DERIVED 
  88. table: t1 
  89. type: index 
  90. possible_keys: NULL 
  91. keyPRIMARY 
  92. key_len: 4 
  93. ref: NULL 
  94. rows: 973192 
  95. Extra: Using index 
  96.  
  97. yejr@imysql.com> SELECT * FROM `t1` INNER JOIN ( SELECT id FROM `t1`ORDER BY id DESC LIMIT 935500,10) t2 USING (id); 
  98. … 
  99. 10 rows in set (1.70 sec) 
  100. #采用INNER JOIN優(yōu)化,從profiling的結果來看,相比原來的那個SQL快了:30.2% 

至此,我們看到采用子查詢或者INNER JOIN進行優(yōu)化后,都有大幅度的提升,這個方法也同樣適用于較小的分頁,雖然LIMIT開始的 start 位置小了很多,SQL執(zhí)行時間也快了很多,但采用這種方法后,帶WHERE條件的分頁分別能提高查詢效率:24.9%、156.5%,不帶WHERE條件的分頁分別提高查詢效率:554.5%、11.7%,各位可以自行進行測試驗證。單從提升比例說,還是挺可觀的,確保這些優(yōu)化方法可以適用于各種分頁模式,就可以從一開始就是用。 我們來看下各種場景相應的提升比例是多少:

MySQL中分頁優(yōu)化的實例詳解

結論:這樣看就和明顯了,尤其是針對大分頁的情況,因此我們優(yōu)先推薦使用INNER JOIN方式優(yōu)化分頁算法。

上述每次測試都重啟mysqld實例,并且加了SQL_NO_CACHE,以保證每次都是直接數(shù)據(jù)文件或索引文件中讀取。如果數(shù)據(jù)經(jīng)過預熱后,查詢效率會一定程度提升,但但上述相應的效率提升比例還是基本一致的。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 江陵县| 德钦县| 曲靖市| 株洲县| 汉阴县| 沙湾县| 乌恰县| 阜康市| 高唐县| 铁岭市| 迁西县| 惠来县| 遵义县| 太白县| 湘乡市| 鹿邑县| 甘肃省| 板桥市| 平遥县| 凤城市| 芦溪县| 沙河市| 新余市| 晋州市| 台北县| 禹州市| 饶河县| 小金县| 大悟县| 旅游| 江永县| 阿拉善左旗| 梅河口市| 堆龙德庆县| 临江市| 潜江市| 西乌珠穆沁旗| 仙游县| 青浦区| 景东| 紫阳县|