日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

MySQL索引背后的数据结构及算法原理(转)

發(fā)布時(shí)間:2023/12/20 数据库 62 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL索引背后的数据结构及算法原理(转) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

摘要

本文以MySQL數(shù)據(jù)庫為研究對(duì)象,討論與數(shù)據(jù)庫索引相關(guān)的一些話題。特別需要說明的是,MySQL支持諸多存儲(chǔ)引擎,而各種存儲(chǔ)引擎對(duì)索引的支持也各不相同,因此MySQL數(shù)據(jù)庫支持多種索引類型,如BTree索引,哈希索引,全文索引等等。為了避免混亂,本文將只關(guān)注于BTree索引,因?yàn)檫@是平常使用MySQL時(shí)主要打交道的索引,至于哈希索引和全文索引本文暫不討論。

文章主要內(nèi)容分為三個(gè)部分。

第一部分主要從數(shù)據(jù)結(jié)構(gòu)及算法理論層面討論MySQL數(shù)據(jù)庫索引的數(shù)理基礎(chǔ)。

第二部分結(jié)合MySQL數(shù)據(jù)庫中MyISAM和InnoDB數(shù)據(jù)存儲(chǔ)引擎中索引的架構(gòu)實(shí)現(xiàn)討論聚集索引、非聚集索引及覆蓋索引等話題。

第三部分根據(jù)上面的理論基礎(chǔ),討論MySQL中高性能使用索引的策略。

數(shù)據(jù)結(jié)構(gòu)及算法基礎(chǔ)

索引的本質(zhì)

MySQL官方對(duì)索引的定義為:索引(Index)是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。提取句子主干,就可以得到索引的本質(zhì):索引是數(shù)據(jù)結(jié)構(gòu)。

我們知道,數(shù)據(jù)庫查詢是數(shù)據(jù)庫的最主要功能之一。我們都希望查詢數(shù)據(jù)的速度能盡可能的快,因此數(shù)據(jù)庫系統(tǒng)的設(shè)計(jì)者會(huì)從查詢算法的角度進(jìn)行優(yōu)化。最基本的查詢算法當(dāng)然是順序查找(linear search),這種復(fù)雜度為O(n)的算法在數(shù)據(jù)量很大時(shí)顯然是糟糕的,好在計(jì)算機(jī)科學(xué)的發(fā)展提供了很多更優(yōu)秀的查找算法,例如二分查找(binary search)、二叉樹查找(binary tree search)等。如果稍微分析一下會(huì)發(fā)現(xiàn),每種查找算法都只能應(yīng)用于特定的數(shù)據(jù)結(jié)構(gòu)之上,例如二分查找要求被檢索數(shù)據(jù)有序,而二叉樹查找只能應(yīng)用于二叉查找樹上,但是數(shù)據(jù)本身的組織結(jié)構(gòu)不可能完全滿足各種數(shù)據(jù)結(jié)構(gòu)(例如,理論上不可能同時(shí)將兩列都按順序進(jìn)行組織),所以,在數(shù)據(jù)之外,數(shù)據(jù)庫系統(tǒng)還維護(hù)著滿足特定查找算法的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)以某種方式引用(指向)數(shù)據(jù),這樣就可以在這些數(shù)據(jù)結(jié)構(gòu)上實(shí)現(xiàn)高級(jí)查找算法。這種數(shù)據(jù)結(jié)構(gòu),就是索引

看一個(gè)例子:

圖1

圖1展示了一種可能的索引方式。左邊是數(shù)據(jù)表,一共有兩列七條記錄,最左邊的是數(shù)據(jù)記錄的物理地址(注意邏輯上相鄰的記錄在磁盤上也并不是一定物理相鄰的)。為了加快Col2的查找,可以維護(hù)一個(gè)右邊所示的二叉查找樹,每個(gè)節(jié)點(diǎn)分別包含索引鍵值和一個(gè)指向?qū)?yīng)數(shù)據(jù)記錄物理地址的指針,這樣就可以運(yùn)用二叉查找在O(log2n)的復(fù)雜度內(nèi)獲取到相應(yīng)數(shù)據(jù)。

雖然這是一個(gè)貨真價(jià)實(shí)的索引,但是實(shí)際的數(shù)據(jù)庫系統(tǒng)幾乎沒有使用二叉查找樹或其進(jìn)化品種紅黑樹(red-black tree)實(shí)現(xiàn)的,原因會(huì)在下文介紹。

B-Tree和B+Tree

目前大部分?jǐn)?shù)據(jù)庫系統(tǒng)及文件系統(tǒng)都采用B-Tree或其變種B+Tree作為索引結(jié)構(gòu),在本文的下一節(jié)會(huì)結(jié)合存儲(chǔ)器原理及計(jì)算機(jī)存取原理討論為什么B-Tree和B+Tree在被如此廣泛用于索引,這一節(jié)先單純從數(shù)據(jù)結(jié)構(gòu)角度描述它們。

B-Tree

為了描述B-Tree,首先定義一條數(shù)據(jù)記錄為一個(gè)二元組[key, data],key為記錄的鍵值,對(duì)于不同數(shù)據(jù)記錄,key是互不相同的;data為數(shù)據(jù)記錄除key外的數(shù)據(jù)。那么B-Tree是滿足下列條件的數(shù)據(jù)結(jié)構(gòu):

  • d為大于1的一個(gè)正整數(shù),稱為B-Tree的度。
  • h為一個(gè)正整數(shù),稱為B-Tree的高度。
  • 每個(gè)非葉子節(jié)點(diǎn)由n-1個(gè)key和n個(gè)指針組成,其中d<=n<=2d。
  • 每個(gè)葉子節(jié)點(diǎn)最少包含一個(gè)key和兩個(gè)指針,最多包含2d-1個(gè)key和2d個(gè)指針,葉節(jié)點(diǎn)的指針均為null 。
  • 所有葉節(jié)點(diǎn)具有相同的深度,等于樹高h(yuǎn)。
  • key和指針互相間隔,節(jié)點(diǎn)兩端是指針。
  • 一個(gè)節(jié)點(diǎn)中的key從左到右非遞減排列。
  • 所有節(jié)點(diǎn)組成樹結(jié)構(gòu)。
  • 每個(gè)指針要么為null,要么指向另外一個(gè)節(jié)點(diǎn)。
  • 如果某個(gè)指針在節(jié)點(diǎn)node最左邊且不為null,則其指向節(jié)點(diǎn)的所有key小于v(key1),其中v(key1)為node的第一個(gè)key的值。
  • 如果某個(gè)指針在節(jié)點(diǎn)node最右邊且不為null,則其指向節(jié)點(diǎn)的所有key大于v(keym),其中v(keym)為node的最后一個(gè)key的值。
  • 如果某個(gè)指針在節(jié)點(diǎn)node的左右相鄰key分別是keyi和keyi+1且不為null,則其指向節(jié)點(diǎn)的所有key小于v(keyi+1)且大于v(keyi)。
  • 圖2是一個(gè)d=2的B-Tree示意圖。

    圖2

    由于B-Tree的特性,在B-Tree中按key檢索數(shù)據(jù)的算法非常直觀:首先從根節(jié)點(diǎn)進(jìn)行二分查找,如果找到則返回對(duì)應(yīng)節(jié)點(diǎn)的data,否則對(duì)相應(yīng)區(qū)間的指針指向的節(jié)點(diǎn)遞歸進(jìn)行查找,直到找到節(jié)點(diǎn)或找到null指針,前者查找成功,后者查找失敗。B-Tree上查找算法的偽代碼如下:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 BTree_Search(node, key) { ??? if(node == null) return null; ??? foreach(node.key) ??? { ??????? if(node.key[i] == key) return node.data[i]; ??????? if(node.key[i] > key) return BTree_Search(point[i]->node); ??? } ??? return BTree_Search(point[i+1]->node); } data = BTree_Search(root, my_key);

    關(guān)于B-Tree有一系列有趣的性質(zhì),例如一個(gè)度為d的B-Tree,設(shè)其索引N個(gè)key,則其樹高h(yuǎn)的上限為logd((N+1)/2),檢索一個(gè)key,其查找節(jié)點(diǎn)個(gè)數(shù)的漸進(jìn)復(fù)雜度為O(logdN)。從這點(diǎn)可以看出,B-Tree是一個(gè)非常有效率的索引數(shù)據(jù)結(jié)構(gòu)。

    另外,由于插入刪除新的數(shù)據(jù)記錄會(huì)破壞B-Tree的性質(zhì),因此在插入刪除時(shí),需要對(duì)樹進(jìn)行一個(gè)分裂、合并、轉(zhuǎn)移等操作以保持B-Tree性質(zhì),本文不打算完整討論B-Tree這些內(nèi)容,因?yàn)橐呀?jīng)有許多資料詳細(xì)說明了B-Tree的數(shù)學(xué)性質(zhì)及插入刪除算法,有興趣的朋友可以在本文末的參考文獻(xiàn)一欄找到相應(yīng)的資料進(jìn)行閱讀。

    B+Tree

    B-Tree有許多變種,其中最常見的是B+Tree,例如MySQL就普遍使用B+Tree實(shí)現(xiàn)其索引結(jié)構(gòu)。

    與B-Tree相比,B+Tree有以下不同點(diǎn):

  • 每個(gè)節(jié)點(diǎn)的指針上限為2d而不是2d+1。
  • 內(nèi)節(jié)點(diǎn)不存儲(chǔ)data,只存儲(chǔ)key;葉子節(jié)點(diǎn)不存儲(chǔ)指針。
  • 圖3是一個(gè)簡單的B+Tree示意。

    圖3

    由于并不是所有節(jié)點(diǎn)都具有相同的域,因此B+Tree中葉節(jié)點(diǎn)和內(nèi)節(jié)點(diǎn)一般大小不同。這點(diǎn)與B-Tree不同,雖然B-Tree中不同節(jié)點(diǎn)存放的key和指針可能數(shù)量不一致,但是每個(gè)節(jié)點(diǎn)的域和上限是一致的,所以在實(shí)現(xiàn)中B-Tree往往對(duì)每個(gè)節(jié)點(diǎn)申請(qǐng)同等大小的空間。

    一般來說,B+Tree比B-Tree更適合實(shí)現(xiàn)外存儲(chǔ)索引結(jié)構(gòu),具體原因與外存儲(chǔ)器原理及計(jì)算機(jī)存取原理有關(guān),將在下面討論。

    帶有順序訪問指針的B+Tree

    一般在數(shù)據(jù)庫系統(tǒng)或文件系統(tǒng)中使用的B+Tree結(jié)構(gòu)都在經(jīng)典B+Tree的基礎(chǔ)上進(jìn)行了優(yōu)化,增加了順序訪問指針。

    圖4

    如圖4所示,在B+Tree的每個(gè)葉子節(jié)點(diǎn)增加一個(gè)指向相鄰葉子節(jié)點(diǎn)的指針,就形成了帶有順序訪問指針的B+Tree。做這個(gè)優(yōu)化的目的是為了提高區(qū)間訪問的性能,例如圖4中如果要查詢key為從18到49的所有數(shù)據(jù)記錄,當(dāng)找到18后,只需順著節(jié)點(diǎn)和指針順序遍歷就可以一次性訪問到所有數(shù)據(jù)節(jié)點(diǎn),極大提到了區(qū)間查詢效率。

    這一節(jié)對(duì)B-Tree和B+Tree進(jìn)行了一個(gè)簡單的介紹,下一節(jié)結(jié)合存儲(chǔ)器存取原理介紹為什么目前B+Tree是數(shù)據(jù)庫系統(tǒng)實(shí)現(xiàn)索引的首選數(shù)據(jù)結(jié)構(gòu)。

    為什么使用B-Tree(B+Tree)

    上文說過,紅黑樹等數(shù)據(jù)結(jié)構(gòu)也可以用來實(shí)現(xiàn)索引,但是文件系統(tǒng)及數(shù)據(jù)庫系統(tǒng)普遍采用B-/+Tree作為索引結(jié)構(gòu),這一節(jié)將結(jié)合計(jì)算機(jī)組成原理相關(guān)知識(shí)討論B-/+Tree作為索引的理論基礎(chǔ)。

    一般來說,索引本身也很大,不可能全部存儲(chǔ)在內(nèi)存中,因此索引往往以索引文件的形式存儲(chǔ)的磁盤上。這樣的話,索引查找過程中就要產(chǎn)生磁盤I/O消耗,相對(duì)于內(nèi)存存取,I/O存取的消耗要高幾個(gè)數(shù)量級(jí),所以評(píng)價(jià)一個(gè)數(shù)據(jù)結(jié)構(gòu)作為索引的優(yōu)劣最重要的指標(biāo)就是在查找過程中磁盤I/O操作次數(shù)的漸進(jìn)復(fù)雜度。換句話說,索引的結(jié)構(gòu)組織要盡量減少查找過程中磁盤I/O的存取次數(shù)。下面先介紹內(nèi)存和磁盤存取原理,然后再結(jié)合這些原理分析B-/+Tree作為索引的效率。

    主存存取原理

    目前計(jì)算機(jī)使用的主存基本都是隨機(jī)讀寫存儲(chǔ)器(RAM),現(xiàn)代RAM的結(jié)構(gòu)和存取原理比較復(fù)雜,這里本文拋卻具體差別,抽象出一個(gè)十分簡單的存取模型來說明RAM的工作原理。

    圖5

    從抽象角度看,主存是一系列的存儲(chǔ)單元組成的矩陣,每個(gè)存儲(chǔ)單元存儲(chǔ)固定大小的數(shù)據(jù)。每個(gè)存儲(chǔ)單元有唯一的地址,現(xiàn)代主存的編址規(guī)則比較復(fù)雜,這里將其簡化成一個(gè)二維地址:通過一個(gè)行地址和一個(gè)列地址可以唯一定位到一個(gè)存儲(chǔ)單元。圖5展示了一個(gè)4 x 4的主存模型。

    主存的存取過程如下:

    當(dāng)系統(tǒng)需要讀取主存時(shí),則將地址信號(hào)放到地址總線上傳給主存,主存讀到地址信號(hào)后,解析信號(hào)并定位到指定存儲(chǔ)單元,然后將此存儲(chǔ)單元數(shù)據(jù)放到數(shù)據(jù)總線上,供其它部件讀取。

    寫主存的過程類似,系統(tǒng)將要寫入單元地址和數(shù)據(jù)分別放在地址總線和數(shù)據(jù)總線上,主存讀取兩個(gè)總線的內(nèi)容,做相應(yīng)的寫操作。

    這里可以看出,主存存取的時(shí)間僅與存取次數(shù)呈線性關(guān)系,因?yàn)椴淮嬖跈C(jī)械操作,兩次存取的數(shù)據(jù)的“距離”不會(huì)對(duì)時(shí)間有任何影響,例如,先取A0再取A1和先取A0再取D3的時(shí)間消耗是一樣的。

    磁盤存取原理

    上文說過,索引一般以文件形式存儲(chǔ)在磁盤上,索引檢索需要磁盤I/O操作。與主存不同,磁盤I/O存在機(jī)械運(yùn)動(dòng)耗費(fèi),因此磁盤I/O的時(shí)間消耗是巨大的。

    圖6是磁盤的整體結(jié)構(gòu)示意圖。

    圖6

    一個(gè)磁盤由大小相同且同軸的圓形盤片組成,磁盤可以轉(zhuǎn)動(dòng)(各個(gè)磁盤必須同步轉(zhuǎn)動(dòng))。在磁盤的一側(cè)有磁頭支架,磁頭支架固定了一組磁頭,每個(gè)磁頭負(fù)責(zé)存取一個(gè)磁盤的內(nèi)容。磁頭不能轉(zhuǎn)動(dòng),但是可以沿磁盤半徑方向運(yùn)動(dòng)(實(shí)際是斜切向運(yùn)動(dòng)),每個(gè)磁頭同一時(shí)刻也必須是同軸的,即從正上方向下看,所有磁頭任何時(shí)候都是重疊的(不過目前已經(jīng)有多磁頭獨(dú)立技術(shù),可不受此限制)。

    圖7是磁盤結(jié)構(gòu)的示意圖。

    圖7

    盤片被劃分成一系列同心環(huán),圓心是盤片中心,每個(gè)同心環(huán)叫做一個(gè)磁道,所有半徑相同的磁道組成一個(gè)柱面。磁道被沿半徑線劃分成一個(gè)個(gè)小的段,每個(gè)段叫做一個(gè)扇區(qū),每個(gè)扇區(qū)是磁盤的最小存儲(chǔ)單元。為了簡單起見,我們下面假設(shè)磁盤只有一個(gè)盤片和一個(gè)磁頭。

    當(dāng)需要從磁盤讀取數(shù)據(jù)時(shí),系統(tǒng)會(huì)將數(shù)據(jù)邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數(shù)據(jù)在哪個(gè)磁道,哪個(gè)扇區(qū)。為了讀取這個(gè)扇區(qū)的數(shù)據(jù),需要將磁頭放到這個(gè)扇區(qū)上方,為了實(shí)現(xiàn)這一點(diǎn),磁頭需要移動(dòng)對(duì)準(zhǔn)相應(yīng)磁道,這個(gè)過程叫做尋道,所耗費(fèi)時(shí)間叫做尋道時(shí)間,然后磁盤旋轉(zhuǎn)將目標(biāo)扇區(qū)旋轉(zhuǎn)到磁頭下,這個(gè)過程耗費(fèi)的時(shí)間叫做旋轉(zhuǎn)時(shí)間。

    局部性原理與磁盤預(yù)讀

    由于存儲(chǔ)介質(zhì)的特性,磁盤本身存取就比主存慢很多,再加上機(jī)械運(yùn)動(dòng)耗費(fèi),磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達(dá)到這個(gè)目的,磁盤往往不是嚴(yán)格按需讀取,而是每次都會(huì)預(yù)讀,即使只需要一個(gè)字節(jié),磁盤也會(huì)從這個(gè)位置開始,順序向后讀取一定長度的數(shù)據(jù)放入內(nèi)存。這樣做的理論依據(jù)是計(jì)算機(jī)科學(xué)中著名的局部性原理:

    當(dāng)一個(gè)數(shù)據(jù)被用到時(shí),其附近的數(shù)據(jù)也通常會(huì)馬上被使用。

    程序運(yùn)行期間所需要的數(shù)據(jù)通常比較集中。

    由于磁盤順序讀取的效率很高(不需要尋道時(shí)間,只需很少的旋轉(zhuǎn)時(shí)間),因此對(duì)于具有局部性的程序來說,預(yù)讀可以提高I/O效率。

    預(yù)讀的長度一般為頁(page)的整倍數(shù)。頁是計(jì)算機(jī)管理存儲(chǔ)器的邏輯塊,硬件及操作系統(tǒng)往往將主存和磁盤存儲(chǔ)區(qū)分割為連續(xù)的大小相等的塊,每個(gè)存儲(chǔ)塊稱為一頁(在許多操作系統(tǒng)中,頁得大小通常為4k),主存和磁盤以頁為單位交換數(shù)據(jù)。當(dāng)程序要讀取的數(shù)據(jù)不在主存中時(shí),會(huì)觸發(fā)一個(gè)缺頁異常,此時(shí)系統(tǒng)會(huì)向磁盤發(fā)出讀盤信號(hào),磁盤會(huì)找到數(shù)據(jù)的起始位置并向后連續(xù)讀取一頁或幾頁載入內(nèi)存中,然后異常返回,程序繼續(xù)運(yùn)行。

    B-/+Tree索引的性能分析

    到這里終于可以分析B-/+Tree索引的性能了。

    上文說過一般使用磁盤I/O次數(shù)評(píng)價(jià)索引結(jié)構(gòu)的優(yōu)劣。先從B-Tree分析,根據(jù)B-Tree的定義,可知檢索一次最多需要訪問h個(gè)節(jié)點(diǎn)。數(shù)據(jù)庫系統(tǒng)的設(shè)計(jì)者巧妙利用了磁盤預(yù)讀原理,將一個(gè)節(jié)點(diǎn)的大小設(shè)為等于一個(gè)頁,這樣每個(gè)節(jié)點(diǎn)只需要一次I/O就可以完全載入。為了達(dá)到這個(gè)目的,在實(shí)際實(shí)現(xiàn)B-Tree還需要使用如下技巧:

    每次新建節(jié)點(diǎn)時(shí),直接申請(qǐng)一個(gè)頁的空間,這樣就保證一個(gè)節(jié)點(diǎn)物理上也存儲(chǔ)在一個(gè)頁里,加之計(jì)算機(jī)存儲(chǔ)分配都是按頁對(duì)齊的,就實(shí)現(xiàn)了一個(gè)node只需一次I/O。

    B-Tree中一次檢索最多需要h-1次I/O(根節(jié)點(diǎn)常駐內(nèi)存),漸進(jìn)復(fù)雜度為O(h)=O(logdN)。一般實(shí)際應(yīng)用中,出度d是非常大的數(shù)字,通常超過100,因此h非常小(通常不超過3)。

    綜上所述,用B-Tree作為索引結(jié)構(gòu)效率是非常高的。

    而紅黑樹這種結(jié)構(gòu),h明顯要深的多。由于邏輯上很近的節(jié)點(diǎn)(父子)物理上可能很遠(yuǎn),無法利用局部性,所以紅黑樹的I/O漸進(jìn)復(fù)雜度也為O(h),效率明顯比B-Tree差很多。

    上文還說過,B+Tree更適合外存索引,原因和內(nèi)節(jié)點(diǎn)出度d有關(guān)。從上面分析可以看到,d越大索引的性能越好,而出度的上限取決于節(jié)點(diǎn)內(nèi)key和data的大小:

    dmax = floor(pagesize / (keysize + datasize + pointsize))?? (pagesize – dmax >= pointsize)

    dmax = floor(pagesize / (keysize + datasize + pointsize)) – 1?? (pagesize – dmax < pointsize)

    floor表示向下取整。由于B+Tree內(nèi)節(jié)點(diǎn)去掉了data域,因此可以擁有更大的出度,擁有更好的性能。

    這一章從理論角度討論了與索引相關(guān)的數(shù)據(jù)結(jié)構(gòu)與算法問題,下一章將討論B+Tree是如何具體實(shí)現(xiàn)為MySQL中索引,同時(shí)將結(jié)合MyISAM和InnDB存儲(chǔ)引擎介紹非聚集索引和聚集索引兩種不同的索引實(shí)現(xiàn)形式。

    MySQL索引實(shí)現(xiàn)

    在MySQL中,索引屬于存儲(chǔ)引擎級(jí)別的概念,不同存儲(chǔ)引擎對(duì)索引的實(shí)現(xiàn)方式是不同的,本文主要討論MyISAM和InnoDB兩個(gè)存儲(chǔ)引擎的索引實(shí)現(xiàn)方式。

    MyISAM索引實(shí)現(xiàn)

    MyISAM引擎使用B+Tree作為索引結(jié)構(gòu),葉節(jié)點(diǎn)的data域存放的是數(shù)據(jù)記錄的地址。下圖是MyISAM索引的原理圖:

    圖8

    這里設(shè)表一共有三列,假設(shè)我們以Col1為主鍵,則圖8是一個(gè)MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數(shù)據(jù)記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結(jié)構(gòu)上沒有任何區(qū)別,只是主索引要求key是唯一的,而輔助索引的key可以重復(fù)。如果我們?cè)贑ol2上建立一個(gè)輔助索引,則此索引的結(jié)構(gòu)如下圖所示:

    圖9

    同樣也是一顆B+Tree,data域保存數(shù)據(jù)記錄的地址。因此,MyISAM中索引檢索的算法為首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然后以data域的值為地址,讀取相應(yīng)數(shù)據(jù)記錄。

    MyISAM的索引方式也叫做“非聚集”的,之所以這么稱呼是為了與InnoDB的聚集索引區(qū)分。

    InnoDB索引實(shí)現(xiàn)

    雖然InnoDB也使用B+Tree作為索引結(jié)構(gòu),但具體實(shí)現(xiàn)方式卻與MyISAM截然不同。

    第一個(gè)重大區(qū)別是InnoDB的數(shù)據(jù)文件本身就是索引文件。從上文知道,MyISAM索引文件和數(shù)據(jù)文件是分離的,索引文件僅保存數(shù)據(jù)記錄的地址。而在InnoDB中,表數(shù)據(jù)文件本身就是按B+Tree組織的一個(gè)索引結(jié)構(gòu),這棵樹的葉節(jié)點(diǎn)data域保存了完整的數(shù)據(jù)記錄。這個(gè)索引的key是數(shù)據(jù)表的主鍵,因此InnoDB表數(shù)據(jù)文件本身就是主索引。

    圖10

    圖10是InnoDB主索引(同時(shí)也是數(shù)據(jù)文件)的示意圖,可以看到葉節(jié)點(diǎn)包含了完整的數(shù)據(jù)記錄。這種索引叫做聚集索引。因?yàn)镮nnoDB的數(shù)據(jù)文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統(tǒng)會(huì)自動(dòng)選擇一個(gè)可以唯一標(biāo)識(shí)數(shù)據(jù)記錄的列作為主鍵,如果不存在這種列,則MySQL自動(dòng)為InnoDB表生成一個(gè)隱含字段作為主鍵,這個(gè)字段長度為6個(gè)字節(jié),類型為長整形。

    第二個(gè)與MyISAM索引的不同是InnoDB的輔助索引data域存儲(chǔ)相應(yīng)記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作為data域。例如,圖11為定義在Col3上的一個(gè)輔助索引:

    圖11

    這里以英文字符的ASCII碼作為比較準(zhǔn)則。聚集索引這種實(shí)現(xiàn)方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然后用主鍵到主索引中檢索獲得記錄。

    了解不同存儲(chǔ)引擎的索引實(shí)現(xiàn)方式對(duì)于正確使用和優(yōu)化索引都非常有幫助,例如知道了InnoDB的索引實(shí)現(xiàn)后,就很容易明白為什么不建議使用過長的字段作為主鍵,因?yàn)樗休o助索引都引用主索引,過長的主索引會(huì)令輔助索引變得過大。再例如,用非單調(diào)的字段作為主鍵在InnoDB中不是個(gè)好主意,因?yàn)镮nnoDB數(shù)據(jù)文件本身是一顆B+Tree,非單調(diào)的主鍵會(huì)造成在插入新記錄時(shí)數(shù)據(jù)文件為了維持B+Tree的特性而頻繁的分裂調(diào)整,十分低效,而使用自增字段作為主鍵則是一個(gè)很好的選擇。

    下一章將具體討論這些與索引有關(guān)的優(yōu)化策略。

    索引使用策略及優(yōu)化

    MySQL的優(yōu)化主要分為結(jié)構(gòu)優(yōu)化(Scheme optimization)和查詢優(yōu)化(Query optimization)。本章討論的高性能索引策略主要屬于結(jié)構(gòu)優(yōu)化范疇。本章的內(nèi)容完全基于上文的理論基礎(chǔ),實(shí)際上一旦理解了索引背后的機(jī)制,那么選擇高性能的策略就變成了純粹的推理,并且可以理解這些策略背后的邏輯。

    示例數(shù)據(jù)庫

    為了討論索引策略,需要一個(gè)數(shù)據(jù)量不算小的數(shù)據(jù)庫作為示例。本文選用MySQL官方文檔中提供的示例數(shù)據(jù)庫之一:employees。這個(gè)數(shù)據(jù)庫關(guān)系復(fù)雜度適中,且數(shù)據(jù)量較大。下圖是這個(gè)數(shù)據(jù)庫的E-R關(guān)系圖(引用自MySQL官方手冊(cè)):

    圖12

    MySQL官方文檔中關(guān)于此數(shù)據(jù)庫的頁面為http://dev.mysql.com/doc/employee/en/employee.html。里面詳細(xì)介紹了此數(shù)據(jù)庫,并提供了下載地址和導(dǎo)入方法,如果有興趣導(dǎo)入此數(shù)據(jù)庫到自己的MySQL可以參考文中內(nèi)容。

    最左前綴原理與相關(guān)優(yōu)化

    高效使用索引的首要條件是知道什么樣的查詢會(huì)使用到索引,這個(gè)問題和B+Tree中的“最左前綴原理”有關(guān),下面通過例子說明最左前綴原理。

    這里先說一下聯(lián)合索引的概念。在上文中,我們都是假設(shè)索引只引用了單個(gè)的列,實(shí)際上,MySQL中的索引可以以一定順序引用多個(gè)列,這種索引叫做聯(lián)合索引,一般的,一個(gè)聯(lián)合索引是一個(gè)有序元組<a1, a2, …, an>,其中各個(gè)元素均為數(shù)據(jù)表的一列,實(shí)際上要嚴(yán)格定義索引需要用到關(guān)系代數(shù),但是這里我不想討論太多關(guān)系代數(shù)的話題,因?yàn)槟菢訒?huì)顯得很枯燥,所以這里就不再做嚴(yán)格定義。另外,單列索引可以看成聯(lián)合索引元素?cái)?shù)為1的特例。

    以employees.titles表為例,下面先查看其上都有哪些索引:

    1 2 3 4 5 6 7 8 9 SHOW INDEX FROM employees.titles; +--------+------------+----------+--------------+-------------+-----------+-------------+------+------------+ | Table? | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Null | Index_type | +--------+------------+----------+--------------+-------------+-----------+-------------+------+------------+ | titles |????????? 0 | PRIMARY? |??????????? 1 | emp_no????? | A???????? |??????? NULL |????? | BTREE????? | | titles |????????? 0 | PRIMARY? |??????????? 2 | title?????? | A???????? |??????? NULL |????? | BTREE????? | | titles |????????? 0 | PRIMARY? |??????????? 3 | from_date?? | A???????? |????? 443308 |????? | BTREE????? | | titles |????????? 1 | emp_no?? |??????????? 1 | emp_no????? | A???????? |????? 443308 |????? | BTREE????? | +--------+------------+----------+--------------+-------------+-----------+-------------+------+------------+

    從結(jié)果中可以到titles表的主索引為<emp_no, title, from_date>,還有一個(gè)輔助索引<emp_no>。為了避免多個(gè)索引使事情變復(fù)雜(MySQL的SQL優(yōu)化器在多索引時(shí)行為比較復(fù)雜),這里我們將輔助索引drop掉:

    1 ALTER TABLE employees.titles DROP INDEX emp_no;

    這樣就可以專心分析索引PRIMARY的行為了。

    情況一:全列匹配。

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title='Senior Engineer' AND from_date='1986-06-26'; +----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+ | id | select_type | table? | type? | possible_keys | key???? | key_len | ref?????????????? | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+ |? 1 | SIMPLE????? | titles | const | PRIMARY?????? | PRIMARY | 59????? | const,const,const |??? 1 |?????? | +----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+

    很明顯,當(dāng)按照索引中所有列進(jìn)行精確匹配(這里精確匹配指“=”或“IN”匹配)時(shí),索引可以被用到。這里有一點(diǎn)需要注意,理論上索引對(duì)順序是敏感的,但是由于MySQL的查詢優(yōu)化器會(huì)自動(dòng)調(diào)整where子句的條件順序以使用適合的索引,例如我們將where中的條件順序顛倒:

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE from_date='1986-06-26' AND emp_no='10001' AND title='Senior Engineer'; +----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+ | id | select_type | table? | type? | possible_keys | key???? | key_len | ref?????????????? | rows | Extra | +----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+ |? 1 | SIMPLE????? | titles | const | PRIMARY?????? | PRIMARY | 59????? | const,const,const |??? 1 |?????? | +----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+

    效果是一樣的。

    情況二:最左前綴匹配。

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001'; +----+-------------+--------+------+---------------+---------+---------+-------+------+-------+ | id | select_type | table? | type | possible_keys | key???? | key_len | ref?? | rows | Extra | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------+ |? 1 | SIMPLE????? | titles | ref? | PRIMARY?????? | PRIMARY | 4?????? | const |??? 1 |?????? | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------+

    當(dāng)查詢條件精確匹配索引的左邊連續(xù)一個(gè)或幾個(gè)列時(shí),如<emp_no>或<emp_no, title>,所以可以被用到,但是只能用到一部分,即條件所組成的最左前綴。上面的查詢從分析結(jié)果看用到了PRIMARY索引,但是key_len為4,說明只用到了索引的第一列前綴。

    情況三:查詢條件用到了索引中列的精確匹配,但是中間某個(gè)條件未提供。

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND from_date='1986-06-26'; +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table? | type | possible_keys | key???? | key_len | ref?? | rows | Extra?????? | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ |? 1 | SIMPLE????? | titles | ref? | PRIMARY?????? | PRIMARY | 4?????? | const |??? 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+

    此時(shí)索引使用情況和情況二相同,因?yàn)閠itle未提供,所以查詢只用到了索引的第一列,而后面的from_date雖然也在索引中,但是由于title不存在而無法和左前綴連接,因此需要對(duì)結(jié)果進(jìn)行掃描過濾from_date(這里由于emp_no唯一,所以不存在掃描)。如果想讓from_date也使用索引而不是where過濾,可以增加一個(gè)輔助索引<emp_no, from_date>,此時(shí)上面的查詢會(huì)使用這個(gè)索引。除此之外,還可以使用一種稱之為“隔離列”的優(yōu)化方法,將emp_no與from_date之間的“坑”填上。

    首先我們看下title一共有幾種不同的值:

    1 2 3 4 5 6 7 8 9 10 11 12 SELECT DISTINCT(title) FROM employees.titles; +--------------------+ | title????????????? | +--------------------+ | Senior Engineer??? | | Staff????????????? | | Engineer?????????? | | Senior Staff?????? | | Assistant Engineer | | Technique Leader?? | | Manager??????????? | +--------------------+

    只有7種。在這種成為“坑”的列值比較少的情況下,可以考慮用“IN”來填補(bǔ)這個(gè)“坑”從而形成最左前綴:

    1 2 3 4 5 6 7 8 9 EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title IN ('Senior Engineer', 'Staff', 'Engineer', 'Senior Staff', 'Assistant Engineer', 'Technique Leader', 'Manager') AND from_date='1986-06-26'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table? | type? | possible_keys | key???? | key_len | ref? | rows | Extra?????? | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ |? 1 | SIMPLE????? | titles | range | PRIMARY?????? | PRIMARY | 59????? | NULL |??? 7 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+

    這次key_len為59,說明索引被用全了,但是從type和rows看出IN實(shí)際上執(zhí)行了一個(gè)range查詢,這里檢查了7個(gè)key。看下兩種查詢的性能比較:

    1 2 3 4 5 6 7 SHOW PROFILES; +----------+------------+-------------------------------------------------------------------------------+ | Query_ID | Duration?? | Query???????????????????????????????????????????????????????????????????????? | +----------+------------+-------------------------------------------------------------------------------+ |?????? 10 | 0.00058000 | SELECT * FROM employees.titles WHERE emp_no='10001' AND from_date='1986-06-26'| |?????? 11 | 0.00052500 | SELECT * FROM employees.titles WHERE emp_no='10001' AND title IN ...????????? | +----------+------------+-------------------------------------------------------------------------------+

    “填坑”后性能提升了一點(diǎn)。如果經(jīng)過emp_no篩選后余下很多數(shù)據(jù),則后者性能優(yōu)勢會(huì)更加明顯。當(dāng)然,如果title的值很多,用填坑就不合適了,必須建立輔助索引。

    情況四:查詢條件沒有指定索引第一列。

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE from_date='1986-06-26'; +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+ | id | select_type | table? | type | possible_keys | key? | key_len | ref? | rows?? | Extra?????? | +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+ |? 1 | SIMPLE????? | titles | ALL? | NULL????????? | NULL | NULL??? | NULL | 443308 | Using where | +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

    由于不是最左前綴,索引這樣的查詢顯然用不到索引。

    情況五:匹配某列的前綴字符串。

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title LIKE 'Senior%'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table? | type? | possible_keys | key???? | key_len | ref? | rows | Extra?????? | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ |? 1 | SIMPLE????? | titles | range | PRIMARY?????? | PRIMARY | 56????? | NULL |??? 1 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+

    此時(shí)可以用到索引,但是如果通配符不是只出現(xiàn)在末尾,則無法使用索引。(原文表述有誤,如果通配符%不出現(xiàn)在開頭,則可以用到索引,但根據(jù)具體情況不同可能只會(huì)用其中一個(gè)前綴)

    情況六:范圍查詢。

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE emp_no < '10010' and title='Senior Engineer'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table? | type? | possible_keys | key???? | key_len | ref? | rows | Extra?????? | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ |? 1 | SIMPLE????? | titles | range | PRIMARY?????? | PRIMARY | 4?????? | NULL |?? 16 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+

    范圍列可以用到索引(必須是最左前綴),但是范圍列后面的列無法用到索引。同時(shí),索引最多用于一個(gè)范圍列,因此如果查詢條件中有兩個(gè)范圍列則無法全用到索引。

    1 2 3 4 5 6 7 8 9 EXPLAIN SELECT * FROM employees.titles WHERE emp_no < 10010' AND title='Senior Engineer' AND from_date BETWEEN '1986-01-01' AND '1986-12-31'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table? | type? | possible_keys | key???? | key_len | ref? | rows | Extra?????? | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ |? 1 | SIMPLE????? | titles | range | PRIMARY?????? | PRIMARY | 4?????? | NULL |?? 16 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+

    可以看到索引對(duì)第二個(gè)范圍索引無能為力。這里特別要說明MySQL一個(gè)有意思的地方,那就是僅用explain可能無法區(qū)分范圍索引和多值匹配,因?yàn)樵趖ype中這兩者都顯示為range。同時(shí),用了“between”并不意味著就是范圍查詢,例如下面的查詢:

    1 2 3 4 5 6 7 8 9 EXPLAIN SELECT * FROM employees.titles WHERE emp_no BETWEEN '10001' AND '10010' AND title='Senior Engineer' AND from_date BETWEEN '1986-01-01' AND '1986-12-31'; +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table? | type? | possible_keys | key???? | key_len | ref? | rows | Extra?????? | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+ |? 1 | SIMPLE????? | titles | range | PRIMARY?????? | PRIMARY | 59????? | NULL |?? 16 | Using where | +----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+

    看起來是用了兩個(gè)范圍查詢,但作用于emp_no上的“BETWEEN”實(shí)際上相當(dāng)于“IN”,也就是說emp_no實(shí)際是多值精確匹配。可以看到這個(gè)查詢用到了索引全部三個(gè)列。因此在MySQL中要謹(jǐn)慎地區(qū)分多值匹配和范圍匹配,否則會(huì)對(duì)MySQL的行為產(chǎn)生困惑。

    情況七:查詢條件中含有函數(shù)或表達(dá)式。

    很不幸,如果查詢條件中含有函數(shù)或表達(dá)式,則MySQL不會(huì)為這列使用索引(雖然某些在數(shù)學(xué)意義上可以使用)。例如:

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND left(title, 6)='Senior'; +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table? | type | possible_keys | key???? | key_len | ref?? | rows | Extra?????? | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+ |? 1 | SIMPLE????? | titles | ref? | PRIMARY?????? | PRIMARY | 4?????? | const |??? 1 | Using where | +----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+

    雖然這個(gè)查詢和情況五中功能相同,但是由于使用了函數(shù)left,則無法為title列應(yīng)用索引,而情況五中用LIKE則可以。再如:

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.titles WHERE emp_no - 1='10000'; +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+ | id | select_type | table? | type | possible_keys | key? | key_len | ref? | rows?? | Extra?????? | +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+ |? 1 | SIMPLE????? | titles | ALL? | NULL????????? | NULL | NULL??? | NULL | 443308 | Using where | +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

    顯然這個(gè)查詢等價(jià)于查詢emp_no為10001的函數(shù),但是由于查詢條件是一個(gè)表達(dá)式,MySQL無法為其使用索引。看來MySQL還沒有智能到自動(dòng)優(yōu)化常量表達(dá)式的程度,因此在寫查詢語句時(shí)盡量避免表達(dá)式出現(xiàn)在查詢中,而是先手工私下代數(shù)運(yùn)算,轉(zhuǎn)換為無表達(dá)式的查詢語句。

    索引選擇性與前綴索引

    既然索引可以加快查詢速度,那么是不是只要是查詢語句需要,就建上索引?答案是否定的。因?yàn)樗饕m然加快了查詢速度,但索引也是有代價(jià)的:索引文件本身要消耗存儲(chǔ)空間,同時(shí)索引會(huì)加重插入、刪除和修改記錄時(shí)的負(fù)擔(dān),另外,MySQL在運(yùn)行時(shí)也要消耗資源維護(hù)索引,因此索引并不是越多越好。一般兩種情況下不建議建索引。

    第一種情況是表記錄比較少,例如一兩千條甚至只有幾百條記錄的表,沒必要建索引,讓查詢做全表掃描就好了。至于多少條記錄才算多,這個(gè)個(gè)人有個(gè)人的看法,我個(gè)人的經(jīng)驗(yàn)是以2000作為分界線,記錄數(shù)不超過 2000可以考慮不建索引,超過2000條可以酌情考慮索引。

    另一種不建議建索引的情況是索引的選擇性較低。所謂索引的選擇性(Selectivity),是指不重復(fù)的索引值(也叫基數(shù),Cardinality)與表記錄數(shù)(#T)的比值:

    Index Selectivity = Cardinality / #T

    顯然選擇性的取值范圍為(0, 1],選擇性越高的索引價(jià)值越大,這是由B+Tree的性質(zhì)決定的。例如,上文用到的employees.titles表,如果title字段經(jīng)常被單獨(dú)查詢,是否需要建索引,我們看一下它的選擇性:

    1 2 3 4 5 6 SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles; +-------------+ | Selectivity | +-------------+ |????? 0.0000 | +-------------+

    title的選擇性不足0.0001(精確值為0.00001579),所以實(shí)在沒有什么必要為其單獨(dú)建索引。

    有一種與索引選擇性有關(guān)的索引優(yōu)化策略叫做前綴索引,就是用列的前綴代替整個(gè)列作為索引key,當(dāng)前綴長度合適時(shí),可以做到既使得前綴索引的選擇性接近全列索引,同時(shí)因?yàn)樗饕齥ey變短而減少了索引文件的大小和維護(hù)開銷。下面以employees.employees表為例介紹前綴索引的選擇和使用。

    從圖12可以看到employees表只有一個(gè)索引<emp_no>,那么如果我們想按名字搜索一個(gè)人,就只能全表掃描了:

    1 2 3 4 5 6 EXPLAIN SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido'; +----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+ | id | select_type | table???? | type | possible_keys | key? | key_len | ref? | rows?? | Extra?????? | +----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+ |? 1 | SIMPLE????? | employees | ALL? | NULL????????? | NULL | NULL??? | NULL | 300024 | Using where | +----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+

    如果頻繁按名字搜索員工,這樣顯然效率很低,因此我們可以考慮建索引。有兩種選擇,建<first_name>或<first_name, last_name>,看下兩個(gè)索引的選擇性:

    1 2 3 4 5 6 7 8 9 10 11 12 13 SELECT count(DISTINCT(first_name))/count(*) AS Selectivity FROM employees.employees; +-------------+ | Selectivity | +-------------+ |????? 0.0042 | +-------------+ SELECT count(DISTINCT(concat(first_name, last_name)))/count(*) AS Selectivity FROM employees.employees; +-------------+ | Selectivity | +-------------+ |????? 0.9313 | +-------------+

    <first_name>顯然選擇性太低,<first_name, last_name>選擇性很好,但是first_name和last_name加起來長度為30,有沒有兼顧長度和選擇性的辦法?可以考慮用first_name和last_name的前幾個(gè)字符建立索引,例如<first_name, left(last_name, 3)>,看看其選擇性:

    1 2 3 4 5 6 SELECT count(DISTINCT(concat(first_name, left(last_name, 3))))/count(*) AS Selectivity FROM employees.employees; +-------------+ | Selectivity | +-------------+ |????? 0.7879 | +-------------+

    選擇性還不錯(cuò),但離0.9313還是有點(diǎn)距離,那么把last_name前綴加到4:

    1 2 3 4 5 6 SELECT count(DISTINCT(concat(first_name, left(last_name, 4))))/count(*) AS Selectivity FROM employees.employees; +-------------+ | Selectivity | +-------------+ |????? 0.9007 | +-------------+

    這時(shí)選擇性已經(jīng)很理想了,而這個(gè)索引的長度只有18,比<first_name, last_name>短了接近一半,我們把這個(gè)前綴索引 建上:

    1 2 ALTER TABLE employees.employees ADD INDEX `first_name_last_name4` (first_name, last_name(4));

    此時(shí)再執(zhí)行一遍按名字查詢,比較分析一下與建索引前的結(jié)果:

    1 2 3 4 5 6 7 SHOW PROFILES; +----------+------------+---------------------------------------------------------------------------------+ | Query_ID | Duration?? | Query?????????????????????????????????????????????????????????????????????????? | +----------+------------+---------------------------------------------------------------------------------+ |?????? 87 | 0.11941700 | SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido' | |?????? 90 | 0.00092400 | SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido' | +----------+------------+---------------------------------------------------------------------------------+

    性能的提升是顯著的,查詢速度提高了120多倍。

    前綴索引兼顧索引大小和查詢速度,但是其缺點(diǎn)是不能用于ORDER BY和GROUP BY操作,也不能用于Covering index(即當(dāng)索引本身包含查詢所需全部數(shù)據(jù)時(shí),不再訪問數(shù)據(jù)文件本身)。

    InnoDB的主鍵選擇與插入優(yōu)化

    在使用InnoDB存儲(chǔ)引擎時(shí),如果沒有特別的需要,請(qǐng)永遠(yuǎn)使用一個(gè)與業(yè)務(wù)無關(guān)的自增字段作為主鍵。

    經(jīng)常看到有帖子或博客討論主鍵選擇問題,有人建議使用業(yè)務(wù)無關(guān)的自增主鍵,有人覺得沒有必要,完全可以使用如學(xué)號(hào)或身份證號(hào)這種唯一字段作為主鍵。不論支持哪種論點(diǎn),大多數(shù)論據(jù)都是業(yè)務(wù)層面的。如果從數(shù)據(jù)庫索引優(yōu)化角度看,使用InnoDB引擎而不使用自增主鍵絕對(duì)是一個(gè)糟糕的主意。

    上文討論過InnoDB的索引實(shí)現(xiàn),InnoDB使用聚集索引,數(shù)據(jù)記錄本身被存于主索引(一顆B+Tree)的葉子節(jié)點(diǎn)上。這就要求同一個(gè)葉子節(jié)點(diǎn)內(nèi)(大小為一個(gè)內(nèi)存頁或磁盤頁)的各條數(shù)據(jù)記錄按主鍵順序存放,因此每當(dāng)有一條新的記錄插入時(shí),MySQL會(huì)根據(jù)其主鍵將其插入適當(dāng)?shù)墓?jié)點(diǎn)和位置,如果頁面達(dá)到裝載因子(InnoDB默認(rèn)為15/16),則開辟一個(gè)新的頁(節(jié)點(diǎn))。

    如果表使用自增主鍵,那么每次插入新的記錄,記錄就會(huì)順序添加到當(dāng)前索引節(jié)點(diǎn)的后續(xù)位置,當(dāng)一頁寫滿,就會(huì)自動(dòng)開辟一個(gè)新的頁。如下圖所示:

    圖13

    這樣就會(huì)形成一個(gè)緊湊的索引結(jié)構(gòu),近似順序填滿。由于每次插入時(shí)也不需要移動(dòng)已有數(shù)據(jù),因此效率很高,也不會(huì)增加很多開銷在維護(hù)索引上。

    如果使用非自增主鍵(如果身份證號(hào)或?qū)W號(hào)等),由于每次插入主鍵的值近似于隨機(jī),因此每次新紀(jì)錄都要被插到現(xiàn)有索引頁得中間某個(gè)位置:

    圖14

    此時(shí)MySQL不得不為了將新記錄插到合適位置而移動(dòng)數(shù)據(jù),甚至目標(biāo)頁面可能已經(jīng)被回寫到磁盤上而從緩存中清掉,此時(shí)又要從磁盤上讀回來,這增加了很多開銷,同時(shí)頻繁的移動(dòng)、分頁操作造成了大量的碎片,得到了不夠緊湊的索引結(jié)構(gòu),后續(xù)不得不通過OPTIMIZE TABLE來重建表并優(yōu)化填充頁面。

    因此,只要可以,請(qǐng)盡量在InnoDB上采用自增字段做主鍵。

    后記

    這篇文章斷斷續(xù)續(xù)寫了半個(gè)月,主要內(nèi)容就是上面這些了。不可否認(rèn),這篇文章在一定程度上有紙上談兵之嫌,因?yàn)槲冶救藢?duì)MySQL的使用屬于菜鳥級(jí)別,更沒有太多數(shù)據(jù)庫調(diào)優(yōu)的經(jīng)驗(yàn),在這里大談數(shù)據(jù)庫索引調(diào)優(yōu)有點(diǎn)大言不慚。就當(dāng)是我個(gè)人的一篇學(xué)習(xí)筆記了。

    其實(shí)數(shù)據(jù)庫索引調(diào)優(yōu)是一項(xiàng)技術(shù)活,不能僅僅靠理論,因?yàn)閷?shí)際情況千變?nèi)f化,而且MySQL本身存在很復(fù)雜的機(jī)制,如查詢優(yōu)化策略和各種引擎的實(shí)現(xiàn)差異等都會(huì)使情況變得更加復(fù)雜。但同時(shí)這些理論是索引調(diào)優(yōu)的基礎(chǔ),只有在明白理論的基礎(chǔ)上,才能對(duì)調(diào)優(yōu)策略進(jìn)行合理推斷并了解其背后的機(jī)制,然后結(jié)合實(shí)踐中不斷的實(shí)驗(yàn)和摸索,從而真正達(dá)到高效使用MySQL索引的目的。

    另外,MySQL索引及其優(yōu)化涵蓋范圍非常廣,本文只是涉及到其中一部分。如與排序(ORDER BY)相關(guān)的索引優(yōu)化及覆蓋索引(Covering index)的話題本文并未涉及,同時(shí)除B-Tree索引外MySQL還根據(jù)不同引擎支持的哈希索引、全文索引等等本文也并未涉及。如果有機(jī)會(huì),希望再對(duì)本文未涉及的部分進(jìn)行補(bǔ)充吧。

    轉(zhuǎn)載于:https://www.cnblogs.com/javaspring/archive/2012/05/23/2656238.html

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的MySQL索引背后的数据结构及算法原理(转)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    免费性网站 | 四虎成人精品 | 狠狠干综合网 | 精品国产一区二区三区av性色 | 中文字幕av免费观看 | 在线观看免费高清视频大全追剧 | 麻豆影视在线观看 | 亚洲视频 一区 | 日韩精品视频免费 | 国产精成人品免费观看 | 69精品在线观看 | 午夜视频播放 | 人人爽人人爱 | 久久久久久久福利 | 一二区精品| 99re6热在线精品视频 | 狠狠网 | 97成人啪啪网 | 久久久亚洲国产精品麻豆综合天堂 | 亚洲在线视频免费观看 | 992tv在线成人免费观看 | 国产亚洲无 | 天天射天天| 色丁香色婷婷 | 69久久夜色精品国产69 | 日韩精品高清不卡 | 亚洲三级黄色 | 欧美不卡视频在线 | 亚洲成人999| 日韩美av在线 | 久久视了| 日韩视频a | 精品国产一区二区三区噜噜噜 | 久草在线视频国产 | 日韩免费在线观看视频 | 99精彩视频在线观看免费 | 探花视频在线观看免费 | 久久夜色精品国产欧美一区麻豆 | 日韩电影在线观看一区 | 久久在线免费观看 | 狠狠久久 | 日韩精品久久久久久久电影竹菊 | 欧美一级免费在线 | 911av视频| 国产在线精品一区二区三区 | 天天爱综合 | 青春草免费在线视频 | 日韩色爱 | 色999精品 | 在线视频 国产 日韩 | 免费a级大片 | 成人免费影院 | 国产成人精品免高潮在线观看 | 免费91在线 | 久久精品中文字幕少妇 | 国产精品美女视频网站 | 丝袜美女在线观看 | 色www精品视频在线观看 | 久久免费看a级毛毛片 | 欧美另类xxx | 91在线小视频 | 久久久久久久电影 | 亚洲成熟女人毛片在线 | a视频免费在线观看 | 日本精品一区二区三区在线播放视频 | 91免费版在线 | 99在线视频免费观看 | 天天干天天搞天天射 | 日韩精品中文字幕在线不卡尤物 | 天天插天天干天天操 | 亚洲精品456在线播放第一页 | 九九热精品视频在线播放 | 亚洲精品成人av在线 | 亚洲资源视频 | 最近能播放的中文字幕 | 久久调教视频 | a成人在线| 成人av电影在线播放 | 免费高清在线观看电视网站 | 日韩在线高清免费视频 | 久久a热6| 99在线国产 | 91丨九色丨国产女 | 亚洲网站在线看 | 手机成人av在线 | 一级国产视频 | 婷婷亚洲激情 | 久久夜色精品国产欧美一区麻豆 | 97av在线视频免费播放 | 久久丝袜视频 | 久久久资源 | 91一区啪爱嗯打偷拍欧美 | 欧美日韩后 | 91精品国产综合久久福利 | 欧美日韩在线精品一区二区 | 久久久午夜视频 | 射九九 | 久久国产精品一区二区三区四区 | 伊人国产在线播放 | 精品国产1区二区 | 欧洲一区二区在线观看 | 五月婷婷在线视频观看 | 天天拍天天草 | 一区二区av | 麻豆精品传媒视频 | av日韩av | 日一日操一操 | 女人魂免费观看 | 亚洲国内在线 | 日韩在线视频二区 | 美女在线免费观看视频 | 亚洲精品97 | 99精品偷拍视频一区二区三区 | 日韩精品在线视频免费观看 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 国产爽视频 | 九九视频免费观看视频精品 | 日日夜夜狠狠干 | 日日噜噜噜噜夜夜爽亚洲精品 | 首页中文字幕 | 日本黄色免费在线观看 | 夜夜夜草 | 日日干日日色 | 成年人黄色av | 8x成人在线 | 国产精品综合久久久久久 | 蜜臀av性久久久久蜜臀av | 在线视频中文字幕一区 | 午夜视频色| 国产精品18久久久久久vr | www亚洲国产 | 色99导航| 久久国产精品一二三区 | 97在线视频免费观看 | 欧美日韩在线第一页 | 中文字幕av在线播放 | 久久久免费看片 | 久久精精品视频 | 中国一级特黄毛片大片久久 | avav片| 奇米影视四色8888 | 久久伊99综合婷婷久久伊 | 亚洲久草视频 | 91精品国产自产老师啪 | 成人精品福利 | 日韩精品一区二区三区丰满 | 天天躁天天狠天天透 | 亚洲日本黄色 | 干狠狠| 亚洲久草网 | 色多多视频在线观看 | 激情欧美一区二区免费视频 | 国产视频在线观看一区 | 国产精品美女久久久久久 | 一级黄色免费 | 69av在线播放 | 久久永久免费 | 国产精选在线观看 | 国产91在线观看 | 中文av字幕在线观看 | 欧美一级日韩三级 | 国产成人一区二区精品非洲 | 婷婷丁香av | 国产精品久久艹 | 欧美日韩二区三区 | 国产精品午夜av | 欧美a级在线免费观看 | 国产精品黄色在线观看 | 色资源在线 | 精品一区久久 | 欧美色久 | 九九热99视频| 国产欧美精品一区aⅴ影院 99视频国产精品免费观看 | 狠狠色丁香久久婷婷综合_中 | 国产流白浆高潮在线观看 | 99在线视频精品 | www.久久色| 一区二精品 | 国产精彩视频一区 | 国产精品综合久久久久久 | 亚洲精品中文字幕在线观看 | 天天综合网 天天综合色 | 五月开心六月伊人色婷婷 | 中文字幕日本电影 | 九九久久久久久久久激情 | 国产成人精品免费在线观看 | 狠日日| 亚洲精品美女在线观看播放 | 中文字幕亚洲综合久久五月天色无吗'' | 天天干天天操人体 | 久久久久久综合 | 国产精品18久久久久白浆 | 亚洲免费av一区二区 | 99re亚洲国产精品 | 五月婷在线视频 | 美女视频a美女大全免费下载蜜臀 | 超碰97国产在线 | 国产精品爽爽久久久久久蜜臀 | 久久9精品 | 区一区二区三在线观看 | 久久综合毛片 | 成 人 黄 色 视频 免费观看 | 国产精品自产拍在线观看中文 | 69精品久久 | 成人黄色电影在线播放 | www.色五月| 久草网视频在线观看 | 免费激情网 | 一二区精品 | 最近免费在线观看 | 久久国产美女视频 | 91精品国产自产在线观看永久 | 国产精品免费观看国产网曝瓜 | 亚洲国产色一区 | 91看片在线看片 | 很黄很黄的网站免费的 | 一本一本久久a久久精品牛牛影视 | 四虎国产精品永久在线国在线 | 久久不射网站 | 最近中文字幕免费观看 | 亚洲毛片久久 | 九九精品久久 | 在线亚洲午夜片av大片 | 99精品久久只有精品 | 96视频在线 | 国产美女免费观看 | 自拍超碰在线 | 在线国产激情视频 | 国产精品久久久久久欧美 | 婷婷 中文字幕 | 国产精品久久久久久久久软件 | 久久国产二区 | 天天拍天天草 | 日韩va亚洲va欧美va久久 | 97干com| 伊人国产在线播放 | 国产美女精品 | 精品美女在线视频 | 狠狠干成人 | 成人精品在线 | 欧美日韩国产在线精品 | av九九九| 久久国语 | 日本久久电影 | 免费成人在线视频网站 | 久久视奸 | 91精品天码美女少妇 | 久久一区二区三区超碰国产精品 | 国内精品久久久久久久影视麻豆 | 国产日韩欧美在线播放 | 6080yy精品一区二区三区 | 久久国产手机看片 | 97成人精品视频在线播放 | 在线 高清 中文字幕 | 伊色综合久久之综合久久 | 久久人人爽人人爽人人片av软件 | 久久综合九色综合97_ 久久久 | 精品国产诱惑 | 一区二区毛片 | 国产亚州精品视频 | 日韩av专区 | 黄色影院在线免费观看 | 在线观看国产区 | 九九热在线精品视频 | 奇米影视777四色米奇影院 | 天天激情在线 | 成人av直播 | 成人作爱视频 | 91九色最新 | 麻豆视传媒官网免费观看 | 国产专区在线视频 | 美女一级毛片视频 | 中文字幕国产 | 精品主播网红福利资源观看 | 精品国产1区 | 日韩av免费在线电影 | 人人盈棋牌| 中文字幕在线播放日韩 | 在线国产激情视频 | 免费精品人在线二线三线 | 九七视频在线 | 成人v| 亚洲国产精品电影在线观看 | 久久国产精品一区二区三区四区 | 亚洲免费视频观看 | 特黄特黄的视频 | 久久成人国产精品一区二区 | 久久这里有 | www久久九 | 国产69熟 | 深爱激情五月婷婷 | 亚洲成人第一区 | 亚洲综合在线播放 | 国产精品久久 | 日韩欧美视频一区二区三区 | 中文字幕一区二区在线播放 | 久久精品这里都是精品 | 国产精品久久久久久久免费大片 | 夜夜澡人模人人添人人看 | 夜夜躁日日躁狠狠躁 | 在线观看免费成人av | 成人在线免费av | 国产精品激情偷乱一区二区∴ | 亚洲精品色 | 人操人| 91人人爱 | 亚洲人成网站精品片在线观看 | 国产日本在线播放 | 国产精品久99 | 亚洲精品小视频 | 国产高清成人av | 日日干综合 | 国产免费不卡 | 一区 在线 影院 | 永久免费毛片在线观看 | 国产精品一区久久久久 | 成人久久久久久久久久 | 人人看看人人 | av电影在线观看完整版一区二区 | 日韩免费看| 毛片随便看 | 4p变态网欧美系列 | 草久电影 | 精品麻豆入口免费 | 日韩在线观看的 | 精品国产a | 久久成人视屏 | 国产一在线精品一区在线观看 | 日韩高清一区在线 | 丁香六月久久综合狠狠色 | 99久久www免费| 成人在线播放av | 最新av在线网址 | 成人毛片一区二区三区 | a天堂免费 | 午夜成人免费电影 | 国产专区精品视频 | 国产精品永久久久久久久久久 | 国产精品青草综合久久久久99 | 国产成人av网站 | 婷婷久久婷婷 | 97在线看 | 亚洲成av片人久久久 | 久久五月网 | av在线永久免费观看 | 日韩欧美精品在线观看视频 | 日韩在线免费视频 | 99久久夜色精品国产亚洲 | 伊人五月婷 | 伊人伊成久久人综合网站 | 成人三级av | 中文av字幕在线观看 | 视频成人永久免费视频 | 亚洲视频 在线观看 | 中文字幕第一页在线视频 | 高清国产在线一区 | 国产精品久久久久久久久久久免费 | 久久精品香蕉 | 日韩在线免费高清视频 | 超碰在线91| 国产无遮挡猛进猛出免费软件 | 西西大胆免费视频 | 国产精品日韩久久久久 | 三级黄色大片在线观看 | 国产精品区二区三区日本 | 丁香在线观看完整电影视频 | 国产一级大片免费看 | 在线电影a | 亚洲 欧美日韩 国产 中文 | 在线国产黄色 | 91精品视频导航 | 婷婷色av| 久久精品激情 | 久久人人爽人人 | 成年人app网址| 国产精品永久在线 | 美女视频网站久久 | 国产区av在线 | 成年人在线电影 | 97在线观看免费观看高清 | 国产成人一区二区三区在线观看 | 婷婷丁香自拍 | 色香蕉视频 | 五月婷网站 | 五月婷婷影院 | 丁香花五月 | 狠狠色狠狠色合久久伊人 | 成 人 黄 色视频免费播放 | 久久久免费看视频 | 精品亚洲一区二区三区 | 久久免费在线 | 国产福利av在线 | 久久综合狠狠 | 九九热在线观看视频 | 久久久观看 | 日本三级全黄少妇三2023 | 日韩精品一区二区三区免费观看视频 | 日韩中文字幕电影 | 国产亚洲成人网 | 国产一区二区免费看 | 国产成人黄色片 | 黄色免费视频在线观看 | 欧美日韩中文在线 | 五月婷婷激情综合网 | 国语黄色片 | 狠狠狠狠干 | 久久成人一区二区 | 久久呀 | 一区二区三高清 | 亚州性色| 99久久99久久精品 | 狠狠色伊人亚洲综合网站野外 | 青青草国产成人99久久 | 九九热只有精品 | 成人av电影免费观看 | 亚州精品在线视频 | 日日摸日日爽 | 久久免费精品 | 成人一区二区三区中文字幕 | 天天视频亚洲 | 日韩动态视频 | 97精品在线 | 国产精品成人一区 | 国产午夜在线观看视频 | 国产精品毛片完整版 | 在线精品亚洲 | 国产高清第一页 | 粉嫩av一区二区三区免费 | 人人爽人人搞 | 天天操天天色综合 | av蜜桃在线 | 中文字幕在线电影 | 色噜噜日韩精品欧美一区二区 | 亚洲综合在 | 亚洲国产成人久久 | 97超碰影视 | 在线观看日本高清mv视频 | 欧美在线观看小视频 | 91天天操| 91免费视频黄| 亚洲韩国一区二区三区 | 久久午夜色播影院免费高清 | 亚洲欧美精品在线 | 欧美国产日韩激情 | 日韩在线在线 | av电影在线不卡 | 久久久免费观看视频 | 免费在线观看日韩 | 国产视频 久久久 | 激情综合国产 | 欧美另类xxx | 丁香综合激情 | 国产色道 | 夜夜躁天天躁很躁波 | 深爱激情综合网 | 激情六月婷婷久久 | av国产在线观看 | 97电影手机 | 久热香蕉视频 | 中文字幕av影院 | 欧美精品一区二区蜜臀亚洲 | 99久久婷婷国产 | 91色综合| 五月激情综合婷婷 | 国产一区电影在线观看 | 久久久久人人 | 欧美在线99 | 天天操狠狠操 | 亚洲国产精品成人va在线观看 | 国产精品久久久久久妇 | 国产精品一区二区吃奶在线观看 | 日韩一级理论片 | 久久综合精品国产一区二区三区 | 久一在线 | 中文字幕激情 | 在线观看精品国产 | 婷婷日韩| 久久久精品在线观看 | 久久国产精品电影 | 亚洲免费一级 | 免费日韩 精品中文字幕视频在线 | 国产美女网站视频 | 人人干人人艹 | 成人午夜黄色影院 | 日韩免费三区 | 天天拍夜夜拍 | 欧美激情视频免费看 | 成人av电影免费在线观看 | 三级小视频在线观看 | 欧美一区免费在线观看 | 福利视频入口 | 国产精品999久久久 久产久精国产品 | 国产精品美乳一区二区免费 | 国内精品久久久久国产 | 欧美色图狠狠干 | 亚洲午夜电影网 | 婷婷网在线 | 探花视频网站 | 中文字幕电影网 | 免费国产一区二区视频 | 成人久久久久久久久久 | 精品国产伦一区二区三区观看方式 | 欧美日韩中文国产一区发布 | 日韩网站在线看片你懂的 | 成年人在线观看免费视频 | 视频在线观看入口黄最新永久免费国产 | 国产色黄网站 | 综合精品久久久 | 中文在线免费一区三区 | 亚洲最新av在线 | 国产免费专区 | 一区在线免费观看 | 国产不卡在线看 | 亚洲97在线 | 国产一二三四在线观看视频 | 91亚洲永久精品 | 99精品视频一区二区 | 久久综合狠狠综合久久激情 | 日本最新一区二区三区 | 成人av在线直播 | 日韩欧美网站 | www.亚洲激情.com | 国产精品嫩草影院99网站 | 免费看黄在线 | 美女黄久久 | 欧洲成人免费 | 在线免费视频a | 91精品国产亚洲 | 日av免费 | 日日骑 | 欧美性黑人| 亚洲少妇xxxx | 成人超碰在线 | 亚洲男人天堂a | 99精品视频在线观看视频 | 91人人揉日日捏人人看 | 亚洲视频高清 | 亚洲一区二区视频在线 | 国内精品久久天天躁人人爽 | 日韩在线 一区二区 | 免费亚洲视频 | 日本aaaa级毛片在线看 | a级国产乱理论片在线观看 特级毛片在线观看 | 欧美韩日精品 | 亚洲国产色一区 | 久久免费的精品国产v∧ | 久久久久国产精品一区 | 日韩网站一区 | 一级免费av | 激情自拍av | 97免费在线观看视频 | 九九久久成人 | 在线视频观看国产 | 一级黄色a视频 | 99色免费视频 | 在线观看色视频 | 人人看人人草 | 96久久欧美麻豆网站 | 超碰在线亚洲 | 亚洲一级黄色av | 丝袜足交在线 | 久久免费看毛片 | 日本精品视频在线观看 | 99热只有精品在线观看 | 天天做日日做天天爽视频免费 | 日韩av一区在线观看 | 97涩涩视频 | 久久综合婷婷 | 久久伦理 | 天天干天天插伊人网 | 麻豆视传媒官网免费观看 | 国产999精品久久久久久 | 天天干夜夜擦 | www.超碰97.com | 亚洲一区欧美精品 | 中文字幕免费国产精品 | 欧美激情视频免费看 | av线上免费看 | 三上悠亚在线免费 | 中文乱码视频在线观看 | 精品免费在线视频 | 亚洲欧美日韩精品久久奇米一区 | 免费观看mv大片高清 | 国产亲近乱来精品 | 中文字幕在线观看免费高清完整版 | 日本大尺码专区mv | 91完整版观看 | 五月婷婷另类国产 | 婷婷综合视频 | 欧美伦理电影一区二区 | 天天干天天爽 | 国产一区91| 欧美精品一级视频 | 在线看福利av | 国产成人综合精品 | 亚洲一区久久久 | 日韩免费av片| 免费看毛片在线 | 亚洲精品中文字幕视频 | 免费三级影片 | 三级在线国产 | 久久五月天婷婷 | 精品一区91 | 在线观看岛国片 | 欧美在线观看视频 | 五月天电影免费在线观看一区 | 99视频播放| 日韩成人在线一区二区 | 亚洲人人网 | 久久神马影院 | 91桃色免费视频 | 一区二区三区视频 | 五月天欧美精品 | 九七人人干 | 三级黄色大片在线观看 | 婷婷网址 | 久久亚洲福利视频 | 亚洲精品久久在线 | 成人九九视频 | 精品国偷自产国产一区 | 一区二区三区久久精品 | 日产乱码一二三区别在线 | 十八岁以下禁止观看的1000个网站 | 在线观看视频国产 | 97av视频 | 中文字幕日韩伦理 | 日韩毛片在线一区二区毛片 | 国产精品女主播一区二区三区 | 三级视频国产 | 日韩av影视在线 | 久久狠狠一本精品综合网 | 久久免费在线 | 免费观看av网站 | 久久久久免费精品国产小说色大师 | 免费在线观看视频a | 欧洲成人av | 欧美日韩视频精品 | av中文字幕在线播放 | 国产资源在线视频 | 免费大片黄在线 | 欧美成人xxxxxxxx | 欧美二区在线播放 | 国产无遮挡又黄又爽在线观看 | 一区二区视频在线播放 | 久久免费视屏 | 97超碰国产精品女人人人爽 | 国内精品视频在线 | 国产黄a三级三级三级三级三级 | 香蕉免费| www免费视频com━ | 精品一二 | 国产黄色精品在线 | 国产精品免费视频观看 | 蜜桃视频精品 | 免费一级片观看 | 豆豆色资源网xfplay | www.97视频 | 欧美在线视频一区二区三区 | 国产精品久久久久久久久久免费 | 国产在线播放一区二区三区 | 日韩精品首页 | 日本精品视频在线观看 | 欧美日韩视频免费 | 99久久婷婷国产一区二区三区 | 欧美久久久久久久久久久 | 国产亚洲精品久久久久久久久久久久 | 麻豆影视在线免费观看 | 国产剧情在线一区 | 免费av影视| 国产精品毛片一区视频 | 亚洲一区免费在线 | 天堂av在线网址 | 91免费观看网站 | 91视频免费播放 | 天堂网一区 | 免费av观看网站 | 激情婷婷 | 国际精品久久久久 | 少妇激情久久 | 深爱激情婷婷网 | 手机在线日韩视频 | 久久综合亚洲鲁鲁五月久久 | 黄色成人影院 | 激情婷婷在线观看 | 久久久91精品国产一区二区精品 | 中文字幕精品一区二区精品 | 99久久精品免费看 | 超碰在线观看av.com | av天天澡天天爽天天av | 一区二区三区精品在线视频 | 黄色a在线观看 | 美女视频免费精品 | 99精品免费久久久久久日本 | 国产小视频在线观看 | 成年人网站免费观看 | 日韩av电影一区 | 精品一区二区免费视频 | 国产精品电影在线 | 欧美激情视频三区 | 99久久久久免费精品国产 | 国产又粗又猛又爽又黄的视频先 | 天天操天天射天天爱 | 激情综合亚洲精品 | 中文字幕av专区 | 久草在线免费新视频 | 少妇高潮冒白浆 | 最近能播放的中文字幕 | 日本三级香港三级人妇99 | 亚洲 综合 专区 | 中文字幕欧美激情 | 中文字幕在线播放第一页 | 亚洲a在线观看 | 国产精品1区 | 亚洲精品视频在线免费播放 | 人交video另类hd | 免费观看国产成人 | 在线观看成人毛片 | 久久天天躁狠狠躁亚洲综合公司 | 国产91影院 | 国内丰满少妇猛烈精品播 | 97在线观视频免费观看 | 狠狠色综合网站久久久久久久 | 一级黄色电影网站 | 国产青青青 | 中文字幕一区在线 | 九九三级毛片 | 99精品免费视频 | 一本一本久久aa综合精品 | 91福利免费 | 久久激情综合网 | 日韩v欧美v日本v亚洲v国产v | 91精品国产乱码久久 | 色网站在线免费观看 | 热久久免费国产视频 | 五月婷婷综合在线视频 | 超碰免费97| 午夜精品一区二区三区在线播放 | 综合激情av | 五月激情丁香图片 | 毛片a级片| www.亚洲精品视频 | 亚洲免费一级电影 | 亚洲综合狠狠干 | 国产精品丝袜久久久久久久不卡 | www.com.黄| 亚洲视频久久久 | 狠狠色丁香久久婷婷综合_中 | 免费a网站 | 日本精品久久久久中文字幕 | 亚洲一区二区精品视频 | 亚洲japanese制服美女 | 在线观看久久 | 91九色蝌蚪在线 | 日韩av手机在线看 | 97免费中文视频在线观看 | 99久久精品国产网站 | 狠狠干夜夜操天天爽 | 久久av中文字幕片 | www日韩在线观看 | 99精品视频在线观看 | 日韩av资源在线观看 | 在线观看国产永久免费视频 | 成人免费观看视频大全 | 亚洲天堂自拍视频 | a级黄色片视频 | 天天综合网久久 | 免费的成人av | 亚洲午夜不卡 | 久久1电影院 | 人人玩人人爽 | 久草精品网 | 久久精品一区二区三区国产主播 | 日日夜夜网 | 久草在线最新免费 | 天堂av影院 | 手机av在线不卡 | 日韩精品电影在线播放 | 精品美女久久久久久免费 | 日产乱码一二三区别在线 | 婷婷精品进入 | 久久99久久99精品中文字幕 | 成人午夜电影久久影院 | 日本99热| 日韩在线国产 | 国产精品久久久久久久午夜 | 国产一区二区在线免费视频 | 久久久不卡影院 | 午夜视频免费在线观看 | 四虎永久视频 | 99精品国产一区二区三区麻豆 | 性色xxxxhd| 98超碰在线观看 | 久久久久麻豆v国产 | 国产一区二区在线播放 | 黄色在线视频网址 | 性色av一区二区三区在线观看 | 国产精品乱码久久久 | 久草干 | 欧美精品中文 | av免费黄色| 91在线视频导航 | 在线影视 一区 二区 三区 | 狠狠久久婷婷 | 91精品视频免费看 | 欧美成人精品欧美一级乱黄 | 在线视频区 | 500部大龄熟乱视频使用方法 | 久精品视频 | 一区二区理论片 | 久久1电影院 | 中文字幕人成不卡一区 | 999色视频| 久久人人爽人人片 | 在线韩国电影免费观影完整版 | 亚洲天堂va| 91麻豆视频 | 午夜精品一区二区三区免费 | 久久免费视频观看 | 一区二区三区在线影院 | 亚洲视频在线免费观看 | 国产高清中文字幕 | 亚洲精品视频在线播放 | 国产专区在线播放 | 四虎免费在线观看 | a一片一级 | 国产精品免费观看在线 | 成年人视频在线观看免费 | 亚洲国产小视频在线观看 | 黄色影院在线播放 | 色婷婷在线播放 | 国产精品午夜在线观看 | 色在线国产 | 国产最新精品视频 | 91看成人 | 激情五月五月婷婷 | 怡红院成人在线 | 97在线免费 | 国产精品久久久久av | 欧美福利网站 | 欧美精品一区在线发布 | 国产v在线| 中文在线字幕观看电影 | 在线观看成人国产 | 亚洲经典视频 | 亚洲精品乱码久久久久久蜜桃不爽 | 欧美日韩国产色综合一二三四 | 欧美粗又大 | 国产在线探花 | 黄色在线观看免费 | 日日干网址| 日韩高清久久 | 国产午夜三级一区二区三桃花影视 | 成人av网页 | 丰满少妇对白在线偷拍 | 中文字幕日韩国产 | 91天堂在线观看 | 久久久午夜精品福利内容 | 国产精品久久久久久69 | 免费人人干 | 色网站在线观看 | 91精品国产91久久久久 | 国产亚洲综合精品 | 99高清视频有精品视频 | 日韩aa视频 | 在线观看午夜 | 久久超碰免费 | 日韩视频区 | 色综合天天综合网国产成人网 | 国产日韩视频在线观看 | www最近高清中文国语在线观看 | 日韩精品在线视频免费观看 | 丁香婷婷综合激情五月色 | 国产精品一区在线 | 亚洲国产精品500在线观看 | 国产成人三级在线观看 | 天天插天天操天天干 | 欧美精品久久久久久久久老牛影院 | 亚洲高清久久久 | 69国产精品成人在线播放 | 亚洲 欧美变态 另类 综合 | 一二三区高清 | 狠狠狠狠狠狠狠狠 | 激情久久久久久久久久久久久久久久 | 国产亚洲精品美女 | 亚洲乱码精品久久久久 | 天天干天天射天天操 | 综合色站导航 | 中文字幕一区二区三区久久 | 九九九在线观看 | 国产在线高清精品 | 97久久精品午夜一区二区 | 欧美另类z0zx| 人人澡人摸人人添学生av | 97色婷婷成人综合在线观看 | 九九亚洲视频 | 丁香花在线视频观看免费 | 狠狠干激情 | 国产97碰免费视频 | 免费精品| 久久国产精品二国产精品中国洋人 | 欧美国产精品一区二区 | 成人av电影在线观看 | 亚洲第一av在线播放 | 狠狠色综合网站久久久久久久 | 久久婷婷色 | 成人a毛片 | 97日日碰人人模人人澡分享吧 | 亚洲婷婷在线视频 | 丁香六月五月婷婷 | 视频福利在线观看 | 国产一区二区三区免费在线观看 | 久草99 | 日韩免费观看一区二区 | 制服丝袜一区二区 | 国产精品成人久久久 | 日韩高清一| 超级碰视频 | 中文资源在线播放 | 成人av免费 | 九九久久婷婷 | 狠狠干婷婷 | 美女视频一区 | 国产亚洲成人网 | 欧美日韩有码 | 免费视频91 | 亚洲综合五月 | 亚洲一区二区视频在线播放 | 在线观看国产v片 | 99久久er热在这里只有精品15 | 91成人午夜| 久久免费视频99 | 日韩三级在线观看 | 五月天久久婷 | 永久免费av在线播放 | 日韩电影一区二区三区 | 国产精品免费在线 | 高清有码中文字幕 | 亚洲最大在线视频 | 久久久91精品国产一区二区精品 | 色婷婷av国产精品 | 亚洲精品在线免费看 | 国产日女人 | 久久99国产精品二区护士 | 国产亚洲精品久久久久久电影 | 国产xx视频 | 精品国产激情 | 精品99视频| 国产亚洲成人精品 | 亚洲一区 av| 国产乱码精品一区二区三区介绍 | 精精国产xxxx视频在线播放 | 一区免费观看 | 久草电影免费在线观看 | 欧洲激情在线 | 中文av不卡 | 男女激情片在线观看 | 又黄又爽免费视频 | 日韩xxxbbb | 国产精品 日韩 欧美 | 啪啪动态视频 | 国产精品久久久久久久久久 | 久久99精品久久久久久清纯直播 | 四虎国产精品免费观看视频优播 | 国产精品免费久久久久久久久久中文 | 亚洲成人蜜桃 | 欧美最爽乱淫视频播放 | 成人欧美在线 | 成人h视频在线 | 色噜噜狠狠狠狠色综合 | 水蜜桃亚洲一二三四在线 | 97精品国产97久久久久久免费 | 91成人天堂久久成人 | 99这里只有精品视频 | 欧美在线视频精品 | 综合av在线 | 国产黄色大片免费看 | 在线观看免费版高清版 | 91看片淫黄大片91 | 亚洲美女视频在线 | 免费在线观看视频一区 | av免费片| 超碰免费av | 99理论片| 亚洲免费公开视频 | 国产在线a不卡 | 国产精品wwwwww | 国产少妇在线观看 | 91精品老司机久久一区啪 | 国产精品午夜免费福利视频 | 午夜视频在线观看一区二区三区 | 在线之家免费在线观看电影 | 免费久久网 | 亚洲精品视频在线观看免费视频 | 日日射av | 国产一区二区不卡视频 | 久久久久欠精品国产毛片国产毛生 | japanese黑人亚洲人4k | 四虎国产精品成人免费影视 |