1. mysql百萬數據分頁查詢4秒,求教怎麼優化
很多應用往往只展示最新或最熱門的幾條記錄,但為了舊記錄仍然可訪問,所以就需要個分頁的導航欄。然而,如何通過MySQL更好的實現分頁,始終是比較令人頭疼的問題。雖然沒有拿來就能用的解決辦法,但了解資料庫的底層或多或少有助於優化分頁查詢。
我們先從一個常用但性能很差的查詢來看一看。
SELECT *
FROM city
ORDER BY id DESC
LIMIT 0, 15
這個查詢耗時0.00sec。So,這個查詢有什麼問題呢?實際上,這個查詢語句和參數都沒有問題,因為它用到了下面表的主鍵,而且只讀取15條記錄。
CREATE TABLE city (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
city varchar(128) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
真正的問題在於offset(分頁偏移量)很大的時候,像下面這樣:
SELECT *
FROM city
ORDER BY id DESC
LIMIT 100000, 15;
上面的查詢在有2M行記錄時需要0.22sec,通過EXPLAIN查看SQL的執行計劃可以發現該SQL檢索了100015行,但最後只需要15行。大的分頁偏移量會增加使用的數據,MySQL會將大量最終不會使用的數據載入到內存中。就算我們假設大部分網站的用戶只訪問前幾頁數據,但少量的大的分頁偏移量的請求也會對整個系統造成危害。Facebook意識到了這一點,但Facebook並沒有為了每秒可以處理更多的請求而去優化資料庫,而是將重心放在將請求響應時間的方差變小。
對於分頁請求,還有一個信息也很重要,就是總共的記錄數。我們可以通過下面的查詢很容易的獲取總的記錄數。
SELECT COUNT(*)
FROM city;
然而,上面的SQL在採用InnoDB為存儲引擎時需要耗費9.28sec。一個不正確的優化是採用 SQL_CALC_FOUND_ROWS,SQL_CALC_FOUND_ROWS 可以在能夠在分頁查詢時事先准備好符合條件的記錄數,隨後只要執行一句 select FOUND_ROWS(); 就能獲得總記錄數。但是在大多數情況下,查詢語句簡短並不意味著性能的提高。不幸的是,這種分頁查詢方式在許多主流框架中都有用到,下面看看這個語句的查詢性能。
SELECT SQL_CALC_FOUND_ROWS *
FROM city
ORDER BY id DESC
LIMIT 100000, 15;
這個語句耗時20.02sec,是上一個的兩倍。事實證明使用 SQL_CALC_FOUND_ROWS 做分頁是很糟糕的想法。
下面來看看到底如何優化。文章分為兩部分,第一部分是如何獲取記錄的總數目,第二部分是獲取真正的記錄。
高效的計算行數
如果採用的引擎是MyISAM,可以直接執行COUNT(*)去獲取行數即可。相似的,在堆表中也會將行數存儲到表的元信息中。但如果引擎是InnoDB情況就會復雜一些,因為InnoDB不保存表的具體行數。
我們可以將行數緩存起來,然後可以通過一個守護進程定期更新或者用戶的某些操作導致緩存失效時,執行下面的語句:
SELECT COUNT(*)
FROM city
USE INDEX(PRIMARY);
獲取記錄
下面進入這篇文章最重要的部分,獲取分頁要展示的記錄。上面已經說過了,大的偏移量會影響性能,所以我們要重寫查詢語句。為了演示,我們創建一個新的表「news」,按照時事性排序(最新發布的在最前面),實現一個高性能的分頁。為了簡單,我們就假設最新發布的新聞的Id也是最大的。
CREATE TABLE news(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(128) NOT NULL
) ENGINE=InnoDB;
一個比較高效的方式是基於用戶展示的最後一個新聞Id。查詢下一頁的語句如下,需要傳入當前頁面展示的最後一個Id。
SELECT *
FROM news WHERE id < $last_id
ORDER BY id DESC
LIMIT $perpage
查詢上一頁的語句類似,只不過需要傳入當前頁的第一個Id,並且要逆序。
SELECT *
FROM news WHERE id > $last_id
ORDER BY id ASC
LIMIT $perpage
上面的查詢方式適合實現簡易的分頁,即不顯示具體的頁數導航,只顯示「上一頁」和「下一頁」,例如博客中頁腳顯示「上一頁」,「下一頁」的按鈕。但如果要實現真正的頁面導航還是很難的,下面看看另一種方式。
SELECT id
FROM (
SELECT id, ((@cnt:= @cnt + 1) + $perpage - 1) % $perpage cnt
FROM news
JOIN (SELECT @cnt:= 0)T
WHERE id < $last_id
ORDER BY id DESC
LIMIT $perpage * $buttons
)C
WHERE cnt = 0;
通過上面的語句可以為每一個分頁的按鈕計算出一個offset對應的id。這種方法還有一個好處。假設,網站上正在發布一片新的文章,那麼所有文章的位置都會往後移一位,所以如果用戶在發布文章時換頁,那麼他會看見一篇文章兩次。如果固定了每個按鈕的offset Id,這個問題就迎刃而解了。Mark Callaghan發表過一篇類似的博客,利用了組合索引和兩個位置變數,但是基本思想是一致的。
如果表中的記錄很少被刪除、修改,還可以將記錄對應的頁碼存儲到表中,並在該列上創建合適的索引。採用這種方式,當新增一個記錄的時候,需要執行下面的查詢重新生成對應的頁號。
SET p:= 0;
UPDATE news SET page=CEIL((p:= p + 1) / $perpage) ORDER BY id DESC;
當然,也可以新增一個專用於分頁的表,可以用個後台程序來維護。
UPDATE pagination T
JOIN (
SELECT id, CEIL((p:= p + 1) / $perpage) page
FROM news
ORDER BY id
)C
ON C.id = T.id
SET T.page = C.page;
現在想獲取任意一頁的元素就很簡單了:
SELECT *
FROM news A
JOIN pagination B ON A.id=B.ID
WHERE page=$offset;
還有另外一種與上種方法比較相似的方法來做分頁,這種方式比較試用於數據集相對小,並且沒有可用的索引的情況下—比如處理搜索結果時。在一個普通的伺服器上執行下面的查詢,當有2M條記錄時,要耗費2sec左右。這種方式比較簡單,創建一個用來存儲所有Id的臨時表即可(這也是最耗費性能的地方)。
CREATE TEMPORARY TABLE _tmp (KEY SORT(random))
SELECT id, FLOOR(RAND() * 0x8000000) random
FROM city;
ALTER TABLE _tmp ADD OFFSET INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, DROP INDEX SORT,ORDER BY random;
接下來就可以向下面一樣執行分頁查詢了。
SELECT *
FROM _tmp
WHERE OFFSET >= $offset
ORDER BY OFFSET
LIMIT $perpage;
簡單來說,對於分頁的優化就是。。。避免數據量大時掃描過多的記錄。
2. MySql 處理超大數據量 (分庫)
其實要看你怎麼查詢了
100W我記不清楚了但是1000W的話直接查詢效率肯定有問題的.
但是要是你把SELECT語句寫在存儲過程裡面,效率降低就很小了!
所以所有的對資料庫操作都盡量封裝在存儲過程裡面.
現在在做一個東西,昨天測了下2億行數據,沒什麼問題- -效率降低維持在可以接受的地步.
當然優秀的索引,緩存配置,調優之類的我就不說了.
分表,分庫,均衡負載等也是解決辦法
你上面說那個情況是可能的.
3. mysql 如何處理億級數據
1、數據表 collect ( id, title ,info ,vtype) 就這來4個欄位,自其中 title 用定長,info 用text, id 是逐漸,vtype是tinyint,vtype是索引。這是一個基本的新聞系統的簡單模型。現在往裡面填充數據,填充10萬篇新聞。
4. MySQL資料庫千萬級數據處理
資料庫主要抄就是兩個功能,襲一個是查詢,一個是儲存,而大數據必定會拖慢查詢,我們對於大數據,更多的是從業務邏輯進行拆分,比如:
當存儲一個人的歷史信息的時候,可以按照時間存儲,一定是最近的記錄最經常訪問,這就是我們常說的二八定律,最長訪問的數據僅僅佔有不到兩成的數據量。
上面說的就是分庫分表,這是一種解決數據量大的辦法。
內存資料庫,redis也是一種處理大數據的辦法,將常訪問的數據放到redis裡面,可以緩解資料庫的壓力,還是像上面說的,我們只要可以找到用戶經常訪問的數據,然後放到內存資料庫中,就可以大大減少mysql的壓力。
最後,技術一定是為解決問題而產生的,我們一定需要對業務進行分析,才能考慮使用什麼技術,拋開業務單存講技術,這是不正確的。
5. mysql資料庫有100萬+數據,查詢起來很慢了,如何優化
你好,你可以根據條件去添加索引,例如:
一、
所有mysql索引列類型都可以被索引,對來相關類使用索引可以提高select查詢性能,根據mysql索引數,可以是最大索引與最小索引,每種存儲引擎對每個表的至少支持16的索引。總索引長度為256位元組。
mysim和innodb存儲引擎的表默認創建索引都是btree索引,目前mysql還不支持函數索引,但支持前緣索引,對欄位前N個字元創建索引
二、mysql創建索引語法
Create[unioun|fulltext|spatial]indexindexname[usingindextype]ontablename(tablenamecol)
index_col_name:
col_name[(length)][asc|desc]
如果你創建索引時搞錯了,需要修改mysql索引我們可以用alert來修改索引,語法與createindex創建索引差不多,我們就不說了,可以查看相關手冊。
下面我們來看一個關於mysql創建索引實例教程。
mysql>createindexcitynameoncity(city(2));
QueryOk,600rowsaffected(0.26sec)
Records:600Duplicates:0Warings0:
我們現在來以city為條件進行查詢,如下面。
->explainselect*fromcitywherecity='www.111cn.net'G
id:1
......
possible_keys:cityname
key:cityname
好了,現在我們來看看mysql刪除索引等實例
Dropindexnameontablename
實例,我現在要刪除剛才創建city索引
>dropindexcitynameoncity;
Queryok,.....
不過通常對百萬級數據的查詢或者其他操作,都改換其他的大型的資料庫了,希望能幫到你,望採納。
6. 如何提高上百萬級記錄MySQL資料庫查詢速度
關於mysql處理百萬級以上的數據時如何提高其查詢速度的方法
最近一段時間由於工作需要,開始關注針對Mysql資料庫的select查詢語句的相關優化方法。
由於在參與的實際項目中發現當mysql表的數據量達到百萬級時,普通SQL查詢效率呈直線下降,而且如果where中的查詢條件較多時,其查詢速度簡直無法容忍。曾經測試對一個包含400多萬條記錄(有索引)的表執行一條條件查詢,其查詢時間竟然高達40幾秒,相信這么高的查詢延時,任何用戶都會抓狂。因此如何提高sql語句查詢效率,顯得十分重要。以下是網上流傳比較廣泛的30種SQL查詢語句優化方法:
1、應盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。
2、對查詢進行優化,應盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。
3、應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:
select id from t where num=0
4、盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20
可以這樣查詢:
select id from t where num=10
union all
select id from t where num=20
5、下面的查詢也將導致全表掃描:(不能前置百分號)
select id from t where name like 『%c%』
若要提高效率,可以考慮全文檢索。
6、in 和 not in 也要慎用,否則會導致全表掃描,如:
select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
7、如果在 where 子句中使用參數,也會導致全表掃描。因為SQL只有在運行時才會解析局部變數,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
8、應盡量避免在 where 子句中對欄位進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
應改為:
select id from t where num=100*2
9、應盡量避免在where子句中對欄位進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)=』abc』–name以abc開頭的id
select id from t where datediff(day,createdate,』2005-11-30′)=0–』2005-11-30′生成的id
應改為:
select id from t where name like 『abc%』
select id from t where createdate>=』2005-11-30′ and createdate<』2005-12-1′
10、不要在 where 子句中的「=」左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。
11、在使用索引欄位作為條件時,如果該索引是復合索引,那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使 用,並且應盡可能的讓欄位順序與索引順序相一致。
12、不要寫一些沒有意義的查詢,如需要生成一個空表結構:
select col1,col2 into #t from t where 1=0
這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:
create table #t(…)
13、很多時候用 exists 代替 in 是一個好的選擇:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
14、並不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重復時,SQL查詢可能不會去利用索引,如一表中有欄位 sex,male、female幾乎各一半,那麼即使在sex上建了索引也對查詢效率起不了作用。
15、索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。
16.應盡可能的避免更新 clustered 索引數據列,因為 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麼需要考慮是否應將該索引建為 clustered 索引。
17、盡量使用數字型欄位,若只含數值信息的欄位盡量不要設計為字元型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢和連接時會 逐個比較字元串中每一個字元,而對於數字型而言只需要比較一次就夠了。
18、盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長欄位存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的欄位內搜索效率顯然要高些。
19、任何地方都不要使用 select * from t ,用具體的欄位列表代替「*」,不要返回用不到的任何欄位。
20、盡量使用表變數來代替臨時表。如果表變數包含大量數據,請注意索引非常有限(只有主鍵索引)。
21、避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
22、臨時表並不是不可使用,適當地使用它們可以使某些常式更有效,例如,當需要重復引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使 用導出表。
23、在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然後insert。
24、如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。
25、盡量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那麼就應該考慮改寫。
26、使用基於游標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。
27、與臨時表一樣,游標並不是不可使用。對小型數據集使用 FAST_FORWARD 游標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括「合計」的常式通常要比使用游標執行的速度快。如果開發時 間允許,基於游標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。
28、在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。
29、盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
30、盡量避免大事務操作,提高系統並發能力。