五月综合缴情婷婷六月,色94色欧美sute亚洲线路二,日韩制服国产精品一区,色噜噜一区二区三区,香港三级午夜理伦三级三

您現(xiàn)在的位置: 365建站網(wǎng) > 365文章 > 什么是索引及MySQL索引原理和慢查詢優(yōu)化

什么是索引及MySQL索引原理和慢查詢優(yōu)化

文章來源:365jz.com     點擊數(shù):286    更新時間:2018-01-08 11:03   參與評論

一、索引

 

MySQL索引的建立對于MySQL的高效運行是很重要的,索引可以大大提高MySQL的檢索速度。

打個比方,如果合理的設計且使用索引的MySQL是一輛蘭博基尼的話,那么沒有設計和使用索引的MySQL就是一個人力三輪車。

索引分單列索引和組合索引。單列索引,即一個索引只包含單個列,一個表可以有多個單列索引,但這不是組合索引。組合索引,即一個索引包含多個列。

創(chuàng)建索引時,你需要確保該索引是應用在 SQL 查詢語句的條件(一般作為 WHERE 子句的條件)。

實際上,索引也是一張表,該表保存了主鍵與索引字段,并指向實體表的記錄。

上面都在說使用索引的好處,但過多的使用索引將會造成濫用。因此索引也會有它的缺點:雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對表進行INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要保存數(shù)據(jù),還要保存一下索引文件。

建立索引會占用磁盤空間的索引文件。

多數(shù)情況下,我們知道索引能夠提高查詢效率,但應該如何建立索引?索引的順序如何?許多人卻只知道大概。其實理解這些概念并不難,而且索引的原理遠沒有想象的那么復雜。
 

索引目的

索引的目的在于提高查詢效率,可以類比字典,如果要查“mysql”這個單詞,我們肯定需要定位到m字母,然后從下往下找到y(tǒng)字母,再找到剩下的sql。如果沒有索引,那么你可能需要把所有單詞看一遍才能找到你想要的,如果我想找到m開頭的單詞呢?或者ze開頭的單詞呢?是不是覺得如果沒有索引,這個事情根本無法完成?

索引原理

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

數(shù)據(jù)庫也是一樣,但顯然要復雜許多,因為不僅面臨著等值查詢,還有范圍查詢(>、<、between、in)、模糊查詢(like)、并集查詢(or)等等。數(shù)據(jù)庫應該選擇怎么樣的方式來應對所有的問題呢?我們回想字典的例子,能不能把數(shù)據(jù)分成段,然后分段查詢呢?最簡單的如果1000條數(shù)據(jù),1到100分成第一段,101到200分成第二段,201到300分成第三段&hellip;&hellip;這樣查第250條數(shù)據(jù),只要找第三段就可以了,一下子去除了90%的無效數(shù)據(jù)。但如果是1千萬的記錄呢,分成幾段比較好?稍有算法基礎的同學會想到搜索樹,其平均復雜度是lgN,具有不錯的查詢性能。但這里我們忽略了一個關鍵的問題,復雜度模型是基于每次相同的操作成本來考慮的,數(shù)據(jù)庫實現(xiàn)比較復雜,數(shù)據(jù)保存在磁盤上,而為了提高性能,每次又可以把部分數(shù)據(jù)讀入內存來計算,因為我們知道訪問磁盤的成本大概是訪問內存的十萬倍左右,所以簡單的搜索樹難以滿足復雜的應用場景。

磁盤IO與預讀

前面提到了訪問磁盤,那么這里先簡單介紹一下磁盤IO和預讀,磁盤讀取數(shù)據(jù)靠的是機械運動,每次讀取數(shù)據(jù)花費的時間可以分為尋道時間、旋轉延遲、傳輸時間三個部分,尋道時間指的是磁臂移動到指定磁道所需要的時間,主流磁盤一般在5ms以下;旋轉延遲就是我們經(jīng)常聽說的磁盤轉速,比如一個磁盤7200轉,表示每分鐘能轉7200次,也就是說1秒鐘能轉120次,旋轉延遲就是1/120/2 = 4.17ms;傳輸時間指的是從磁盤讀出或將數(shù)據(jù)寫入磁盤的時間,一般在零點幾毫秒,相對于前兩個時間可以忽略不計。那么訪問一次磁盤的時間,即一次磁盤IO的時間約等于5+4.17 = 9ms左右,聽起來還挺不錯的,但要知道一臺500 -MIPS的機器每秒可以執(zhí)行5億條指令,因為指令依靠的是電的性質,換句話說執(zhí)行一次IO的時間可以執(zhí)行40萬條指令,數(shù)據(jù)庫動輒十萬百萬乃至千萬級數(shù)據(jù),每次9毫秒的時間,顯然是個災難。下圖是計算機硬件延遲的對比圖,供大家參考:

考慮到磁盤IO是非常高昂的操作,計算機操作系統(tǒng)做了一些優(yōu)化,當一次IO時,不光把當前磁盤地址的數(shù)據(jù),而是把相鄰的數(shù)據(jù)也都讀取到內存緩沖區(qū)內,因為局部預讀性原理告訴我們,當計算機訪問一個地址的數(shù)據(jù)的時候,與其相鄰的數(shù)據(jù)也會很快被訪問到。每一次IO讀取的數(shù)據(jù)我們稱之為一頁(page)。具體一頁有多大數(shù)據(jù)跟操作系統(tǒng)有關,一般為4k或8k,也就是我們讀取一頁內的數(shù)據(jù)時候,實際上才發(fā)生了一次IO,這個理論對于索引的數(shù)據(jù)結構設計非常有幫助。

索引的數(shù)據(jù)結構

前面講了生活中索引的例子,索引的基本原理,數(shù)據(jù)庫的復雜性,又講了操作系統(tǒng)的相關知識,目的就是讓大家了解,任何一種數(shù)據(jù)結構都不是憑空產(chǎn)生的,一定會有它的背景和使用場景,我們現(xiàn)在總結一下,我們需要這種數(shù)據(jù)結構能夠做些什么,其實很簡單,那就是:每次查找數(shù)據(jù)時把磁盤IO次數(shù)控制在一個很小的數(shù)量級,最好是常數(shù)數(shù)量級。那么我們就想到如果一個高度可控的多路搜索樹是否能滿足需求呢?就這樣,b+樹應運而生。

詳解b+樹

如上圖,是一顆b+樹,關于b+樹的定義可以參見B+樹,這里只說一些重點,淺藍色的塊我們稱之為一個磁盤塊,可以看到每個磁盤塊包含幾個數(shù)據(jù)項(深藍色所示)和指針(黃色所示),如磁盤塊1包含數(shù)據(jù)項17和35,包含指針P1、P2、P3,P1表示小于17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大于35的磁盤塊。真實的數(shù)據(jù)存在于葉子節(jié)點即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非葉子節(jié)點只不存儲真實的數(shù)據(jù),只存儲指引搜索方向的數(shù)據(jù)項,如17、35并不真實存在于數(shù)據(jù)表中。

b+樹的查找過程

如圖所示,如果要查找數(shù)據(jù)項29,那么首先會把磁盤塊1由磁盤加載到內存,此時發(fā)生一次IO,在內存中用二分查找確定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間因為非常短(相比磁盤的IO)可以忽略不計,通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發(fā)生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,通過指針加載磁盤塊8到內存,發(fā)生第三次IO,同時內存中做二分查找找到29,結束查詢,總計三次IO。真實的情況是,3層的b+樹可以表示上百萬的數(shù)據(jù),如果上百萬的數(shù)據(jù)查找只需要三次IO,性能提高將是巨大的,如果沒有索引,每個數(shù)據(jù)項都要發(fā)生一次IO,那么總共需要百萬次的IO,顯然成本非常非常高。

b+樹性質

1.通過上面的分析,我們知道IO次數(shù)取決于b+數(shù)的高度h,假設當前數(shù)據(jù)表的數(shù)據(jù)為N,每個磁盤塊的數(shù)據(jù)項的數(shù)量是m,則有h=㏒(m+1)N,當數(shù)據(jù)量N一定的情況下,m越大,h越?。欢鴐 = 磁盤塊的大小 / 數(shù)據(jù)項的大小,磁盤塊的大小也就是一個數(shù)據(jù)頁的大小,是固定的,如果數(shù)據(jù)項占的空間越小,數(shù)據(jù)項的數(shù)量越多,樹的高度越低。這就是為什么每個數(shù)據(jù)項,即索引字段要盡量的小,比如int占4字節(jié),要比bigint8字節(jié)少一半。這也是為什么b+樹要求把真實的數(shù)據(jù)放到葉子節(jié)點而不是內層節(jié)點,一旦放到內層節(jié)點,磁盤塊的數(shù)據(jù)項會大幅度下降,導致樹增高。當數(shù)據(jù)項等于1時將會退化成線性表。

2.當b+樹的數(shù)據(jù)項是復合的數(shù)據(jù)結構,比如(name,age,sex)的時候,b+數(shù)是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數(shù)據(jù)來檢索的時候,b+樹會優(yōu)先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最后得到檢索的數(shù)據(jù);但當(20,F)這樣的沒有name的數(shù)據(jù)來的時候,b+樹就不知道下一步該查哪個節(jié)點,因為建立搜索樹的時候name就是第一個比較因子,必須要先根據(jù)name來搜索才能知道下一步去哪里查詢。比如當(張三,F)這樣的數(shù)據(jù)來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等于張三的數(shù)據(jù)都找到,然后再匹配性別是F的數(shù)據(jù)了, 這個是非常重要的性質,即索引的最左匹配特性。

慢查詢優(yōu)化

關于MySQL索引原理是比較枯燥的東西,大家只需要有一個感性的認識,并不需要理解得非常透徹和深入。我們回頭來看看一開始我們說的慢查詢,了解完索引原理之后,大家是不是有什么想法呢?先總結一下索引的幾大基本原則

建索引的幾大原則

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的查詢優(yōu)化器會幫你優(yōu)化成索引可以識別的形式

3.盡量選擇區(qū)分度高的列作為索引,區(qū)分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數(shù)越少,唯一鍵的區(qū)分度是1,而一些狀態(tài)、性別字段可能在大數(shù)據(jù)面前區(qū)分度就是0,那可能有人會問,這個比例有什么經(jīng)驗值嗎?使用場景不同,這個值也很難確定,一般需要join的字段我們都要求是0.1以上,即平均1條掃描10條記錄

4.索引列不能參與計算,保持列&ldquo;干凈&rdquo;,比如from_unixtime(create_time) = &rsquo;2014-05-29&rsquo;就不能使用到索引,原因很簡單,b+樹中存的都是數(shù)據(jù)表中的字段值,但進行檢索時,需要把所有元素都應用函數(shù)才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(&rsquo;2014-05-29&rsquo;);

5.盡量的擴展索引,不要新建索引。比如表中已經(jīng)有a的索引,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可
 

查詢優(yōu)化神器 &ndash; explain命令

關于explain命令相信大家并不陌生,具體用法和字段含義可以參考官網(wǎng)explain-output,這里需要強調rows是核心指標,絕大部分rows小的語句執(zhí)行一定很快(有例外,下面會講到)。所以優(yōu)化語句基本上都是在優(yōu)化rows。

慢查詢優(yōu)化基本步驟

0.先運行看看是否真的很慢,注意設置SQL_NO_CACHE
1.where條件單表查,鎖定最小返回記錄表。這句話的意思是把查詢語句的where都應用到表中返回的記錄數(shù)最小的表開始查起,單表每個字段分別查詢,看哪個字段的區(qū)分度最高
2.explain查看執(zhí)行計劃,是否與1預期一致(從鎖定記錄較少的表開始查詢)
3.order by limit 形式的sql語句讓排序的表優(yōu)先查
4.了解業(yè)務方使用場景
5.加索引時參照建索引的幾大原則
6.觀察結果,不符合預期繼續(xù)從0分析
 

1. FULLTEXT

即為全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不過目前只有 CHAR、VARCHAR ,TEXT 列上可以創(chuàng)建全文索引。

全文索引并不是和MyISAM一起誕生的,它的出現(xiàn)是為了解決WHERE name LIKE &ldquo;%word%"這類針對文本的模糊查詢效率較低的問題。

3. BTREE

BTREE索引就是一種將索引值按一定的算法,存入一個樹形的數(shù)據(jù)結構中(二叉樹),每次查詢都是從樹的入口root開始,依次遍歷node,獲取leaf。這是MySQL里默認和最常用的索引類型。

三、索引種類

    唯一索引:加速查詢 + 列值唯一(可以有null)

    組合索引:多列值組成一個索引,專門用于組合搜索,其效率大于索引合并

    CREATE TABLE People (

       last_name varchar(50)    not null,

       first_name varchar(50)    not null,

       dob        date           not null,

       gender     enum('m', 'f') not null,

       key(last_name, first_name, dob)

    );

     其索引包含表中每一行的last_name、first_name和dob列。其結構大致如下:

    函數(shù)f( )

    f('Arjen') = 2323

    f('Baron') = 7437

    f('Peter') = 8784

    f('Vadim') = 2458

     

     Slots是有序的,但是記錄不是有序的。當你執(zhí)行
    mysql> SELECT lname FROM testhash WHERE fname='Peter';
    MySQL會計算&rsquo;Peter&rsquo;的hash值,然后通過它來查詢索引的行指針。因為f('Peter') = 8784,MySQL會在索引中查找8784,得到指向記錄3的指針。
    因為索引自己僅僅存儲很短的值,所以,索引非常緊湊。Hash值不取決于列的數(shù)據(jù)類型,一個TINYINT列的索引與一個長字符串列的索引一樣大。
     
    Hash索引有以下一些限制:
    (1)由于索引僅包含hash code和記錄指針,所以,MySQL不能通過使用索引避免讀取記錄。但是訪問內存中的記錄是非常迅速的,不會對性造成太大的影響。
    (2)不能使用hash索引排序。
    (3)Hash索引不支持鍵的部分匹配,因為是通過整個索引值來計算hash值的。
    (4)Hash索引只支持等值比較,例如使用=,IN( )和<=>。對于WHERE price>100并不能加速查詢。
    2.1.3、空間(R-Tree)索引
    MyISAM支持空間索引,主要用于地理空間數(shù)據(jù)類型,例如GEOMETRY。
    2.1.4、全文(Full-text)索引
    全文索引是MyISAM的一個特殊索引類型,主要用于全文檢索。
     

    3、高性能的索引策略
    3.1、聚簇索引(Clustered Indexes)
    聚簇索引保證關鍵字的值相近的元組存儲的物理位置也相同(所以字符串類型不宜建立聚簇索引,特別是隨機字符串,會使得系統(tǒng)進行大量的移動操作),且一個表只能有一個聚簇索引。因為由存儲引擎實現(xiàn)索引,所以,并不是所有的引擎都支持聚簇索引。目前,只有solidDB和InnoDB支持。
    聚簇索引的結構大致如下:

    CREATE TABLE layout_test (

       col1 int NOT NULL,

       col2 int NOT NULL,

       PRIMARY KEY(col1),

       KEY(col2)

    );

     假設主鍵的值位于1---10,000之間,且按隨機順序插入,然后用OPTIMIZE TABLE進行優(yōu)化。col2隨機賦予1---100之間的值,所以會存在許多重復的值。
    (1)    MyISAM的數(shù)據(jù)布局
    其布局十分簡單,MyISAM按照插入的順序在磁盤上存儲數(shù)據(jù),如下:

     注:MyISAM不支持聚簇索引,索引中每一個葉子節(jié)點僅僅包含行號(row number),且葉子節(jié)點按照col1的順序存儲。
    來看看col2的索引結構:

     注:聚簇索引中的每個葉子節(jié)點包含primary key的值,事務ID和回滾指針(rollback pointer)&mdash;&mdash;用于事務和MVCC,和余下的列(如col2)。

    相對于MyISAM,二級索引與聚簇索引有很大的不同。InnoDB的二級索引的葉子包含primary key的值,而不是行指針(row pointers),這減小了移動數(shù)據(jù)或者數(shù)據(jù)頁面分裂時維護二級索引的開銷,因為InnoDB不需要更新索引的行指針。其結構大致如下:

     

     3.1.2、按primary key的順序插入行(InnoDB)

    如果你用InnoDB,而且不需要特殊的聚簇索引,一個好的做法就是使用代理主鍵(surrogate key)&mdash;&mdash;獨立于你的應用中的數(shù)據(jù)。最簡單的做法就是使用一個AUTO_INCREMENT的列,這會保證記錄按照順序插入,而且能提高使用primary key進行連接的查詢的性能。應該盡量避免隨機的聚簇主鍵,例如,字符串主鍵就是一個不好的選擇,它使得插入操作變得隨機。

     

     3.2、覆蓋索引(Covering Indexes)
    如果索引包含滿足查詢的所有數(shù)據(jù),就稱為覆蓋索引。覆蓋索引是一種非常強大的工具,能大大提高查詢性能。只需要讀取索引而不用讀取數(shù)據(jù)有以下一些優(yōu)點:
    (1)索引項通常比記錄要小,所以MySQL訪問更少的數(shù)據(jù);
    (2)索引都按值的大小順序存儲,相對于隨機訪問記錄,需要更少的I/O;
    (3)大多數(shù)據(jù)引擎能更好的緩存索引。比如MyISAM只緩存索引。
    (4)覆蓋索引對于InnoDB表尤其有用,因為InnoDB使用聚集索引組織數(shù)據(jù),如果二級索引中包含查詢所需的數(shù)據(jù),就不再需要在聚集索引中查找了。
    覆蓋索引不能是任何索引,只有B-TREE索引存儲相應的值。而且不同的存儲引擎實現(xiàn)覆蓋索引的方式都不同,并不是所有存儲引擎都支持覆蓋索引(Memory和Falcon就不支持)。
    對于索引覆蓋查詢(index-covered query),使用EXPLAIN時,可以在Extra一列中看到&ldquo;Using index&rdquo;。例如,在sakila的inventory表中,有一個組合索引(store_id,film_id),對于只需要訪問這兩列的查詢,MySQL就可以使用索引,如下:

    *************************** 1. row ***************************

               id: 1

     select_type: SIMPLE

            table: inventory

             type: index

    possible_keys: NULL

              key: idx_store_id_film_id

          key_len: 3

              ref: NULL

             rows: 5007

            Extra: Using index

    1 row in set (0.17 sec)

    mysql> EXPLAIN SELECT store_id, film_id FROM sakila.inventory\G

    在大多數(shù)引擎中,只有當查詢語句所訪問的列是索引的一部分時,索引才會覆蓋。但是,InnoDB不限于此,InnoDB的二級索引在葉子節(jié)點中存儲了primary key的值。因此,sakila.actor表使用InnoDB,而且對于是last_name上有索引,所以,索引能覆蓋那些訪問actor_id的查詢,如:

     

        -> FROM sakila.actor WHERE last_name = 'HOPPER'\G

    *************************** 1. row ***************************

               id: 1

     select_type: SIMPLE

            table: actor

             type: ref

    possible_keys: idx_actor_last_name

              key: idx_actor_last_name

          key_len: 137

              ref: const

             rows: 2

            Extra: Using where; Using index

    mysql> EXPLAIN SELECT actor_id, last_name

     

    3.3、利用索引進行排序
    MySQL中,有兩種方式生成有序結果集:一是使用filesort,二是按索引順序掃描。利用索引進行排序操作是非??斓模铱梢岳猛凰饕瑫r進行查找和排序操作。當索引的順序與ORDER BY中的列順序相同且所有的列是同一方向(全部升序或者全部降序)時,可以使用索引來排序。如果查詢是連接多個表,僅當ORDER BY中的所有列都是第一個表的列時才會使用索引。其它情況都會使用filesort。

    password        varchar(16) NOT NULL DEFAULT '',

    PRIMARY KEY(actor_id),

     KEY     (name)

    ) ENGINE=InnoDB

    insert into actor(name,password) values('cat01','1234567');

    insert into actor(name,password) values('cat02','1234567');

    insert into actor(name,password) values('ddddd','1234567');

    insert into actor(name,password) values('aaaaa','1234567');

    name      varchar(16) NOT NULL DEFAULT '',

     

     

    *************************** 1. row ***************************

               id: 1

     select_type: SIMPLE

            table: actor

             type: index

    possible_keys: NULL

              key: PRIMARY

          key_len: 4

              ref: NULL

             rows: 4

            Extra: 

     

    mysql> explain select actor_id from actor order by password \G

    *************************** 1. row ***************************

               id: 1

     select_type: SIMPLE

            table: actor

             type: ALL

    possible_keys: NULL

              key: NULL

          key_len: NULL

              ref: NULL

             rows: 4

            Extra: 

     

    mysql> explain select actor_id from actor order by name \G

    *************************** 1. row ***************************

               id: 1

     select_type: SIMPLE

            table: actor

             type: index

    possible_keys: NULL

              key: name

          key_len: 18

              ref: NULL

             rows: 4

            Extra: 

    mysql> explain select actor_id from actor order by actor_id \G

     當MySQL不能使用索引進行排序時,就會利用自己的排序算法(快速排序算法)在內存(sort buffer)中對數(shù)據(jù)進行排序,如果內存裝載不下,它會將磁盤上的數(shù)據(jù)進行分塊,再對各個數(shù)據(jù)塊進行排序,然后將各個塊合并成有序的結果集(實際上就是外排序)。對于filesort,MySQL有兩種排序算法。

    (1)兩遍掃描算法(Two passes)
    實現(xiàn)方式是先將須要排序的字段和可以直接定位到相關行數(shù)據(jù)的指針信息取出,然后在設定的內存(通過參數(shù)sort_buffer_size設定)中進行排序,完成排序之后再次通過行指針信息取出所需的Columns。
    注:該算法是4.1之前采用的算法,它需要兩次訪問數(shù)據(jù),尤其是第二次讀取操作會導致大量的隨機I/O操作。另一方面,內存開銷較小。
    (3)    一次掃描算法(single pass)
    該算法一次性將所需的Columns全部取出,在內存中排序后直接將結果輸出。
    注:從 MySQL 4.1 版本開始使用該算法。它減少了I/O的次數(shù),效率較高,但是內存開銷也較大。如果我們將并不需要的Columns也取出來,就會極大地浪費排序過程所需要的內存。在 MySQL 4.1 之后的版本中,可以通過設置 max_length_for_sort_data 參數(shù)來控制 MySQL 選擇第一種排序算法還是第二種。當取出的所有大字段總大小大于 max_length_for_sort_data 的設置時,MySQL 就會選擇使用第一種排序算法,反之,則會選擇第二種。為了盡可能地提高排序性能,我們自然更希望使用第二種排序算法,所以在 Query 中僅僅取出需要的 Columns 是非常有必要的。

    當對連接操作進行排序時,如果ORDER BY僅僅引用第一個表的列,MySQL對該表進行filesort操作,然后進行連接處理,此時,EXPLAIN輸出&ldquo;Using filesort&rdquo;;否則,MySQL必須將查詢的結果集生成一個臨時表,在連接完成之后進行filesort操作,此時,EXPLAIN輸出&ldquo;Using temporary;Using filesort&rdquo;。

     

    3.4、索引與加鎖
    索引對于InnoDB非常重要,因為它可以讓查詢鎖更少的元組。這點十分重要,因為MySQL 5.0中,InnoDB直到事務提交時才會解鎖。有兩個方面的原因:首先,即使InnoDB行級鎖的開銷非常高效,內存開銷也較小,但不管怎么樣,還是存在開銷。其次,對不需要的元組的加鎖,會增加鎖的開銷,降低并發(fā)性。
    InnoDB僅對需要訪問的元組加鎖,而索引能夠減少InnoDB訪問的元組數(shù)。但是,只有在存儲引擎層過濾掉那些不需要的數(shù)據(jù)才能達到這種目的。一旦索引不允許InnoDB那樣做(即達不到過濾的目的),MySQL服務器只能對InnoDB返回的數(shù)據(jù)進行WHERE操作,此時,已經(jīng)無法避免對那些元組加鎖了:InnoDB已經(jīng)鎖住那些元組,服務器無法解鎖了。
    來看個例子:

    password        varchar(16) NOT NULL DEFAULT '',

    PRIMARY KEY(actor_id),

     KEY     (name)

    ) ENGINE=InnoDB

    insert into actor(name,password) values('cat01','1234567');

    insert into actor(name,password) values('cat02','1234567');

    insert into actor(name,password) values('ddddd','1234567');

    insert into actor(name,password) values('aaaaa','1234567');

    name      varchar(16) NOT NULL DEFAULT '',

    AND actor_id <> 1 FOR UPDATE;

    SELECT actor_id FROM actor WHERE actor_id < 4

     該查詢僅僅返回2---3的數(shù)據(jù),實際已經(jīng)對1---3的數(shù)據(jù)加上排它鎖了。InnoDB鎖住元組1是因為MySQL的查詢計劃僅使用索引進行范圍查詢(而沒有進行過濾操作,WHERE中第二個條件已經(jīng)無法使用索引了):

     

        -> WHERE actor_id < 4 AND actor_id <> 1 FOR UPDATE \G

    *************************** 1. row ***************************

               id: 1

     select_type: SIMPLE

            table: actor

             type: index

    possible_keys: PRIMARY

              key: PRIMARY

          key_len: 4

              ref: NULL

             rows: 4

            Extra: Using where; Using index

    1 row in set (0.00 sec)

     

    mysql>

    mysql> EXPLAIN SELECT actor_id FROM test.actor

     表明存儲引擎從索引的起始處開始,獲取所有的行,直到actor_id<4為假,服務器無法告訴InnoDB去掉元組1。

    為了證明row 1已經(jīng)被鎖住,我們另外建一個連接,執(zhí)行如下操作:

    SELECT actor_id FROM actor WHERE actor_id = 1 FOR UPDATE;

     該查詢會被掛起,直到第一個連接的事務提交釋放鎖時,才會執(zhí)行(這種行為對于基于語句的復制(statement-based replication)是必要的)。
    如上所示,當使用索引時,InnoDB會鎖住它不需要的元組。更糟糕的是,如果查詢不能使用索引,MySQL會進行全表掃描,并鎖住每一個元組,不管是否真正需要。


     

     

如對本文有疑問,請?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會為你解答??! 點擊進入論壇

發(fā)表評論 (286人查看0條評論)
請自覺遵守互聯(lián)網(wǎng)相關的政策法規(guī),嚴禁發(fā)布色情、暴力、反動的言論。
昵稱:
最新評論
------分隔線----------------------------

其它欄目

· 建站教程
· 365學習

業(yè)務咨詢

· 技術支持
· 服務時間:9:00-18:00
365建站網(wǎng)二維碼

Powered by 365建站網(wǎng) RSS地圖 HTML地圖

copyright © 2013-2024 版權所有 鄂ICP備17013400號