mysql查詢優化之100萬條數據的一張表優化方案

1.兩種查詢引擎查詢速度(myIsam 引擎 )

InnoDB 中不保存表的具體行數,也就是說,執行select count(*) from table時,InnoDB要掃描一遍整個表來計算有多少行。

MyISAM隻要簡單的讀出保存好的行數即可。

註意的是,當count(*)語句包含 where條件時,兩種表的操作有些不同,InnoDB類型的表用count(*)或者count(主鍵),加上where col 條件。其中col列是表的主鍵之外的其他具有唯一約束索引的列。這樣查詢時速度會很快。就是可以避免全表掃描。

總結:

mysql 在300萬條數據(myisam引擎)情況下使用 count(*) 進行數據總數查詢包含條件(正確設置索引)運行時間正常。對於經常進行讀取的數據我們建議使用myIsam引擎。

2.百萬數據下mysql分頁問題

在開發過程中我們經常會使用分頁,核心技術是使用limit進行數據的讀取,在使用limit進行分頁的測試過程中,得到以下數據:

select * from news order by id desc limit 0,10
耗時0.003秒
select * from news order by id desc limit 10000,10
耗時0.058秒
select * from news order by id desc limit 100000,10 
耗時0.575秒
select * from news order by id desc limit 1000000,10
耗時7.28秒

我們驚訝的發現mysql在數據量大的情況下分頁起點越大查詢速度越慢,100萬條起的查詢速度已經需要7秒鐘。這是一個我們無法接受的數值!

改進方案 1

select * from news 
where id > (select id from news order by id desc limit 1000000, 1)
order by id desc 
limit 0,10

查詢時間 0.365秒,提升效率是非常明顯的!!原理是什麼呢???

我們使用條件對id進行瞭篩選,在子查詢 (select id from news order by id desc limit 1000000, 1) 中我們隻查詢瞭id這一個字段比起select * 或 select 多個字段 節省瞭大量的查詢開銷!

改進方案2

適合id連續的系統,速度極快!

select * from news 
where id between 1000000 and 1000010 
order by id desc

不適合帶有條件的、id不連續的查詢。速度非常快!

3. 百萬數據下mysql條件查詢、分頁查詢的註意事項

接上一節,我們加上查詢條件:

select id from news 
where cate = 1
order by id desc 
limit 500000 ,10 
查詢時間 20 秒

好恐怖的速度!!利用第一節知識進行優化:

select * from news
where cate = 1 and id > (select id from news where cate = 1 order by id desc limit 500000,1 ) 
order by id desc 
limit 0,10 
查詢時間 15 秒

優化效果不明顯,條件帶來的影響還是很大!在這樣的情況下無論我們怎麼去優化sql語句就無法解決運行效率問題。那麼換個思路:建立一個索引表,隻記錄文章的id、分類信息,我們將文章內容這個大字段分割出去。

表 news2 [ 文章表 引擎 myisam 字符集 utf-8 ]

id int 11 主鍵自動增加

cate int 11 索引

在寫入數據時將2張表同步,查詢是則可以使用news2 來進行條件查詢:

select * from news
where cate = 1 and id > (select id from news2 where cate = 1 order by id desc limit 500000,1 ) 
order by id desc 
limit 0,10

註意條件 id > 後面使用瞭news2 這張表!

運行時間 1.23秒,我們可以看到運行時間縮減瞭近20倍!!數據在10萬左右是查詢時間可以保持在0.5秒左右,是一個逐步接近我們能夠容忍的值!

但是1秒對於服務器來說依然是一個不能接受的值!!還有什麼可以優化的辦法嗎??我們嘗試瞭一個偉大的變化:

將 news2 的存儲引擎改變為innodb,執行結果是驚人的!

select * from news
where cate = 1 and id > (select id from news2 where cate = 1 order by id desc limit 500000,1 ) 
order by id desc 
limit 0,10

隻需要 0.2秒,非常棒的速度。

4.mysql存儲引擎 myIsam和innodb的區別

MySQL有多種存儲引擎,MyISAM和InnoDB是其中常用的兩種。這裡介紹關於這兩種引擎的一些基本概念(非深入介紹)。

MyISAM存儲引擎,基於傳統的ISAM類型,支持全文搜索,但不是事務安全的,而且不支持外鍵。每張MyISAM表存放在三個文件中:frm 文件存放表格定義;數據文件是MYD (MYData);索引文件是MYI (MYIndex)。

InnoDB是事務型引擎,支持回滾、崩潰恢復能力、多版本並發控制、ACID事務,支持行級鎖定(InnoDB表的行鎖不是絕對的,如果在執行一個SQL語句時MySQL不能確定要掃描的范圍,InnoDB表同樣會鎖全表,如like操作時的SQL語句),以及提供與Oracle類型一致的不加鎖讀取方式。InnoDB存儲它的表和索引在一個表空間中,表空間可以包含數個文件。

核心區別

MyISAM是非事務安全型的,而InnoDB是事務安全型的。

MyISAM鎖的粒度是表級,而InnoDB支持行級鎖定。

MyISAM支持全文類型索引,而InnoDB不支持全文索引。

MyISAM相對簡單,所以在效率上要優於InnoDB,小型應用可以考慮使用MyISAM。

MyISAM表是保存成文件的形式,在跨平臺的數據轉移中使用MyISAM存儲會省去不少的麻煩。

InnoDB表比MyISAM表更安全,可以在保證數據不會丟失的情況下,切換非事務表到事務表(alter table tablename type=innodb)。

應用場景

MyISAM管理非事務表。它提供高速存儲和檢索,以及全文搜索能力。如果應用中需要執行大量的SELECT查詢,那麼MyISAM是更好的選擇。

InnoDB用於事務處理應用程序,具有眾多特性,包括ACID事務支持。如果應用中需要執行大量的INSERT或UPDATE操作,則應該使用InnoDB,這樣可以提高多用戶並發操作的性能。

Mysql的存儲引擎和索引

數據庫必須有索引,沒有索引則檢索過程變成瞭順序查找,O(n)的時間復雜度幾乎是不能忍受的。我們非常容易想象出一個隻有單關鍵字組成的表如何使用B+樹進行索引,隻要將關鍵字存儲到樹的節點即可。當數據庫一條記錄裡包含多個字段時,一棵B+樹就隻能存儲主鍵,如果檢索的是非主鍵字段,則主鍵索引失去作用,又變成順序查找瞭。這時應該在第二個要檢索的列上建立第二套索引。 這個索引由獨立的B+樹來組織。有兩種常見的方法可以解決多個B+樹訪問同一套表數據的問題,一種叫做聚簇索引(clustered index ),一種叫做非聚簇索引(secondary index)。這兩個名字雖然都叫做索引,但這並不是一種單獨的索引類型,而是一種數據存儲方式。對於聚簇索引存儲來說,行數據和主鍵B+樹存儲在一起,輔助鍵B+樹隻存儲輔助鍵和主鍵,主鍵和非主鍵B+樹幾乎是兩種類型的樹。對於非聚簇索引存儲來說,主鍵B+樹在葉子節點存儲指向真正數據行的指針,而非主鍵。

InnoDB使用的是聚簇索引,將主鍵組織到一棵B+樹中,而行數據就儲存在葉子節點上,若使用”where id = 14″這樣的條件查找主鍵,則按照B+樹的檢索算法即可查找到對應的葉節點,之後獲得行數據。若對Name列進行條件搜索,則需要兩個步驟:第一步在輔助索引B+樹中檢索Name,到達其葉子節點獲取對應的主鍵。第二步使用主鍵在主索引B+樹種再執行一次B+樹檢索操作,最終到達葉子節點即可獲取整行數據。

MyISM使用的是非聚簇索引,非聚簇索引的兩棵B+樹看上去沒什麼不同,節點的結構完全一致隻是存儲的內容不同而已,主鍵索引B+樹的節點存儲瞭主鍵,輔助鍵索引B+樹存儲瞭輔助鍵。表數據存儲在獨立的地方,這兩顆B+樹的葉子節點都使用一個地址指向真正的表數據,對於表數據來說,這兩個鍵沒有任何差別。由於索引樹是獨立的,通過輔助鍵檢索無需訪問主鍵的索引樹。

為瞭更形象說明這兩種索引的區別,我們假想一個表如下圖存儲瞭4行數據。其中Id作為主索引,Name作為輔助索引。圖示清晰的顯示瞭聚簇索引和非聚簇索引的差異。

我們重點關註聚簇索引,看上去聚簇索引的效率明顯要低於非聚簇索引,因為每次使用輔助索引檢索都要經過兩次B+樹查找,這不是多此一舉嗎?聚簇索引的優勢在哪?

1 由於行數據和葉子節點存儲在一起,這樣主鍵和行數據是一起被載入內存的,找到葉子節點就可以立刻將行數據返回瞭,如果按照主鍵Id來組織數據,獲得數據更快。

2 輔助索引使用主鍵作為”指針” 而不是使用地址值作為指針的好處是,減少瞭當出現行移動或者數據頁分裂時輔助索引的維護工作,使用主鍵值當作指針會讓輔助索引占用更多的空間,換來的好處是InnoDB在移動行時無須更新輔助索引中的這個”指針”。也就是說行的位置(實現中通過16K的Page來定位,後面會涉及)會隨著數據庫裡數據的修改而發生變化(前面的B+樹節點分裂以及Page的分裂),使用聚簇索引就可以保證不管這個主鍵B+樹的節點如何變化,輔助索引樹都不受影響。

所以在百萬級數據及更大數據情況下,mysql innoDB 的索引表現更加優秀!

5、MySQL性能優化的一些經驗

a.為查詢優化你的查詢

大多數的MySQL服務器都開啟瞭查詢緩存。這是提高性能最有效的方法之一,而且這是被MySQL的數據庫引擎處理的。當有很多相同的查詢被執行瞭多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果瞭。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓MySQL不使用緩存。

請看下面的示例:

// 查詢緩存不開啟

$r = mysql_query(“SELECT username FROM user WHERE signup_date >= CURDATE()”);

// 開啟查詢緩存

$today = date(“Y-m-d”);

$r = mysql_query(“SELECT username FROM user WHERE signup_date >= ‘$today'”);

上面兩條SQL語句的差別就是 CURDATE() ,MySQL的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的SQL函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替MySQL的函數,從而開啟緩存。

b.學會使用EXPLAIN

使用EXPLAIN關鍵字可以讓你知道MySQL是如何處理你的SQL語句的。

select id, title, cate from news where cate = 1

發現查詢緩慢,然後在cate字段上增加索引,則會加快查詢

c.當隻要一行數據時使用LIMIT 1

當你查詢表的有些時候隻需要一條數據,請使用 limit 1。

d.正確的使用索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索、拍下、條件,那麼,請為其建立索引吧。

e.不要ORDER BY RAND()

效率很低的一種隨機查詢。

f.避免SELECT *

從數據庫裡讀出越多的數據,那麼查詢就會變得越慢。並且,如果你的數據庫服務器和WEB服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。必須應該養成一個需要什麼就取什麼的好的習慣。

g.使用 ENUM 而不是 VARCHAR

ENUM 類型是非常快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。

如果你有一個字段,比如“性別”,“國傢”,“民族”,“狀態”或“部門”,你知道這些字段的取值是有限而且固定的,那麼,你應該使用 ENUM 而不是 VARCHAR。

h.使用 NOT NULL

除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的字段保持 NOT NULL。這看起來好像有點爭議,請往下看。

首先,問問你自己“Empty”和“NULL”有多大的區別(如果是INT,那就是0和NULL)?如果你覺得它們之間沒有什麼區別,那麼你就不要使用NULL。(你知道嗎?在 Oracle 裡,NULL 和 Empty 的字符串是一樣的!)

不要以為 NULL 不需要空間,其需要額外的空間,並且,在你進行比較的時候,你的程序會更復雜。 當然,這裡並不是說你就不能使用NULL瞭,現實情況是很復雜的,依然會有些情況下,你需要使用NULL值。

下面摘自MySQL自己的文檔

“NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”

i.IP地址存成 UNSIGNED INT

很多程序員都會創建一個 VARCHAR(15) 字段來存放字符串形式的IP而不是整形的IP。如果你用整形來存放,隻需要4個字節,並且你可以有定長的字段。而且,這會為你帶來查詢上的優勢,尤其是當你需要使用這樣的WHERE條件:IP between ip1 and ip2。

我們必需要使用UNSIGNED INT,因為 IP地址會使用整個32位的無符號整形

j.固定長度的表會更快

如果表中的所有字段都是“固定長度”的,整個表會被認為是 “static” 或 “fixed-length”。 例如,表中沒有如下類型的字段: VARCHAR,TEXT,BLOB。隻要你包括瞭其中一個這些字段,那麼這個表就不是“固定長度靜態表”瞭,這樣,MySQL 引擎會用另一種方法來處理。

固定長度的表會提高性能,因為MySQL搜尋得會更快一些,因為這些固定的長度是很容易計算下一個數據的偏移量的,所以讀取的自然也會很快。而如果字段不是定長的,那麼,每一次要找下一條的話,需要程序找到主鍵。

並且,固定長度的表也更容易被緩存和重建。不過,唯一的副作用是,固定長度的字段會浪費一些空間,因為定長的字段無論你用不用,他都是要分配那麼多的空間。

k.垂直分割

“垂直分割”是一種把數據庫中的表按列變成幾張表的方法,這樣可以降低表的復雜度和字段的數目,從而達到優化的目的。需要註意的是,這些被分出去的字段所形成的表,你不會經常性地去Join他們,不然的話,這樣的性能會比不分割時還要差,而且,會是極數級的下降。

l.拆分大的 DELETE 或 INSERT 語句

如果在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你需要非常小心,要避免你的操作讓你的整個網站停止相應。因為這兩個操作是會鎖表的,表一鎖住瞭,別的操作都進不來瞭。

Apache 會有很多的子進程或線程。所以,其工作起來相當有效率,而我們的服務器也不希望有太多的子進程,線程和數據庫鏈接,這是極大的占服務器資源的事情,尤其是內存。

如果你把你的表鎖上一段時間,比如30秒鐘,那麼對於一個有很高訪問量的站點來說,這30秒所積累的訪問進程/線程,數據庫鏈接,打開的文件數,可能不僅僅會讓你泊WEB服務Crash,還可能會讓你的整臺服務器馬上掛瞭。

m.越小的列會越快

對於大多數的數據庫引擎來說,硬盤操作可能是最重大的瓶頸。所以,把你的數據變得緊湊會對這種情況非常有幫助,因為這減少瞭對硬盤的訪問。

n.選擇正確的存儲引擎

在 MySQL 中有兩個存儲引擎 MyISAM 和 InnoDB,每個引擎都有利有弊。

MyISAM 適合於一些需要大量查詢的應用,但其對於有大量寫操作並不是很好。甚至你隻是需要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都無法操作直到讀操作完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。

InnoDB 的趨勢會是一個非常復雜的存儲引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支持“行鎖” ,於是在寫操作比較多的時候,會更優秀。並且,他還支持更多的高級應用,比如:事務。

到此這篇關於mysql查詢優化之100萬條數據的一張表優化方案的文章就介紹到這瞭,更多相關mysql查詢優化內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: