關於mysql的索引原理與慢查詢優化

大多狀況下咱們都知道加索引能提升查詢效率,可是應該如何加索引呢?索引的順序如何呢?mysql

你們看一下下面的sql語句(在沒有看下面的優化的方法以前)應該如何優化加索引以及優化sql語句:算法

一、select count(*) from task where status=2 and operator_id=20839 and operate_time>1371169729  and operate_time<1371174603 and type=2;sql

二、一、select count(*) from task where from_unixtime(create_time) = ’2014-05-29’;數據庫

三、update test set name='zhangsan'  where sex=1 and age>20;(注意這種狀況加索引與不加索引的區別是什麼)數據結構

四、select distinct l.emp_id from log l inner join (select l2.id as emp_id,l2.id as cert_id from log2 l2  left join log3 l3 on l2.id = l3.emp_id where l2.is_deleted=0 ) l4 on ( l.ref_table='Employee' and l.ref_oid= l4.emp_id函數

 ) or (l.ref_table='EmpCertificate' and l.ref_oid= l2.cert_id) where l.last_upd_date >='2013-11-07 15:03:00' and l.last_upd_date<='2013-11-08 16:00:00'性能

下面說一下MySql索引原理大數據

索引目的

索引的目的在於提升查詢效率,能夠類比字典,若是要查「mysql」這個單詞,咱們確定須要定位到m字母,而後從下往下找到y字母,再找到剩下的sql。若是沒有索引,那麼你可能須要把全部單詞看一遍才能找到你想要的,若是我想找到m開頭的單詞呢?或者ze開頭的單詞呢?是否是以爲若是沒有索引,這個事情根本沒法完成?優化

索引原理

除了詞典,生活中隨處可見索引的例子,如火車站的車次表、圖書的目錄等。它們的原理都是同樣的,經過不斷的縮小想要得到數據的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是咱們老是經過同一種查找方式來鎖定數據。spa

數據庫也是同樣,但顯然要複雜許多,由於不只面臨着等值查詢,還有範圍查詢(>、<、between、in)、模糊查詢(like)、並集查詢(or)等等。數據庫應該選擇怎麼樣的方式來應對全部的問題呢?咱們回想字典的例子,能不能把數據分紅段,而後分段查詢呢?最簡單的若是1000條數據,1到100分紅第一段,101到200分紅第二段,201到300分紅第三段……這樣查第250條數據,只要找第三段就能夠了,一會兒去除了90%的無效數據。但若是是1千萬的記錄呢,分紅幾段比較好?稍有算法基礎的同窗會想到搜索樹,其平均複雜度是lgN,具備不錯的查詢性能。但這裏咱們忽略了一個關鍵的問題,複雜度模型是基於每次相同的操做成原本考慮的,數據庫實現比較複雜,數據保存在磁盤上,而爲了提升性能,每次又能夠把部分數據讀入內存來計算,由於咱們知道訪問磁盤的成本大概是訪問內存的十萬倍左右,因此簡單的搜索樹難以知足複雜的應用場景。

 

索引的數據結構

前面講了生活中索引的例子,索引的基本原理,數據庫的複雜性,又講了操做系統的相關知識,目的就是讓你們瞭解,任何一種數據結構都不是憑空產生的,必定會有它的背景和使用場景,咱們如今總結一下,咱們須要這種數據結構可以作些什麼,其實很簡單,那就是:每次查找數據時把磁盤IO次數控制在一個很小的數量級,最好是常數數量級。那麼咱們就想到若是一個高度可控的多路搜索樹是否能知足需求呢?就這樣,b+樹應運而生。

 

建索引的幾大原則

1.最左前綴匹配原則,很是重要的原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配,好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。

2.=和in能夠亂序,好比a = 1 and b = 2 and c = 3 創建(a,b,c)索引能夠任意順序,mysql的查詢優化器會幫你優化成索引能夠識別的形式

3.儘可能選擇區分度高的列做爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大咱們掃描的記錄數越少,惟一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0,那可能有人會問,這個比例有什麼經驗值嗎?使用場景不一樣,這個值也很難肯定,通常須要join的字段咱們都要求是0.1以上,即平均1條掃描10條記錄

4.索引列不能參與計算,保持列「乾淨」,好比from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,緣由很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,須要把全部元素都應用函數才能比較,顯然成本太大。因此語句應該寫成create_time = unix_timestamp(’2014-05-29’);

5.儘可能的擴展索引,不要新建索引。好比表中已經有a的索引,如今要加(a,b)的索引,那麼只須要修改原來的索引便可

 

回到上面的給出的mysql

一、根據最左匹配原則,第一條sql語句的索引應該是status、operator_id、type、operate_time的聯合索引;其中status、operator_id、type的順序能夠顛倒,因此我纔會說,把這個表的全部相關查詢都找到,會綜合分析;

二、根據索引列不能參與計算,保持列「乾淨」,第二條sql語句應該寫成create_time = unix_timestamp(’2014-05-29’);

三、update 語句不加索引是鎖表,加索引是鎖行 where後面的條件根據第一條同樣優化便可。

四、第4條sql語句就複雜了在沒有作如何優化以前結果是:53 rows in set (1.87 sec)

    經過explain

   簡述一下執行計劃,首先mysql根據idx_last_upd_date索引掃描log表得到379條記錄;而後查表掃描了63727條記錄,分爲兩部分,derived表示構造表,也就是不存在的表,能夠簡單理解成是一個語句造成的結果集,後面的數字表示語句的ID。derived2表示的是ID = 2的查詢構造了虛擬表,而且返回了63727條記錄。咱們再來看看ID = 2的語句究竟作了寫什麼返回了這麼大量的數據,首先全表掃描log2表13317條記錄,而後根據索引emp_certificate_empid關聯log3表,rows = 1表示,每一個關聯都只鎖定了一條記錄,效率比較高。得到後,再和log的379條記錄根據規則關聯。從執行過程上能夠看出返回了太多的數據,返回的數據絕大部分log都用不到,由於log只鎖定了379條記錄。

如何優化呢?能夠看到咱們在運行完後仍是要和log作join,那麼咱們能不能以前和log作join呢?仔細分析語句不難發現,其基本思想是若是log的ref_table是EmpCertificate就關聯log3表,若是ref_table是Employee就關聯log2表,咱們徹底能夠拆成兩部分,並用union鏈接起來,注意這裏用union,而不用union all是由於原語句有「distinct」來獲得惟一的記錄,而union剛好具有了這種功能。若是原語句中沒有distinct不須要去重,咱們就能夠直接使用union all了,由於使用union須要去重的動做,會影響SQL性能。

優化過的語句以下

select

   l2.id

from

   log l

inner join

   log2 l2

      on l.ref_table = 'Employee'

      and l.ref_oid = emp.id  

where

   l.last_upd_date >='2013-11-07 15:03:00'

   and l.last_upd_date<='2013-11-08 16:00:00'

   and l2.is_deleted = 0  

union

select

   l2.id

from

   log l

inner join

   log3 l3

      on l.ref_table = 'EmpCertificate'

      and l.ref_oid = l3.id  

inner join

   log2 l2

      on l2.id = l3.emp_id  

where

   l.last_upd_date >='2013-11-07 15:03:00'

   and l.last_upd_date<='2013-11-08 16:00:00'

   and l2.is_deleted = 0

相關文章
相關標籤/搜索