摘要: 本文內容主要來源於互聯網上主流文章,只是按照我的理解稍做整合,後面附有參考連接。html
本文內容主要來源於互聯網上主流文章,只是按照我的理解稍做整合,後面附有參考連接。node
本文以MySQL數據庫爲研究對象,討論與數據庫索引相關的一些話題。特別須要說明的是,MySQL支持諸多存儲引擎,而各類存儲引擎對索引的支持也各不相同,所以MySQL數據庫支持多種索引類型,如BTree索引,哈希索引,全文索引等等。爲了不混亂,本文將只關注於BTree索引,由於這是日常使用MySQL時主要打交道的索引,至於哈希索引和全文索引本文暫不討論。mysql
爲何這裏要講查詢算法和數據結構呢?由於之因此要創建索引,其實就是爲了構建一種數據結構,能夠在上面應用一種高效的查詢算法,最終提升數據的查詢速度。正則表達式
MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。提取句子主幹,就能夠獲得索引的本質:索引是數據結構。算法
咱們知道,數據庫查詢是數據庫的最主要功能之一。咱們都但願查詢數據的速度能儘量的快,所以數據庫系統的設計者會從查詢算法的角度進行優化。那麼有哪些查詢算法可使查詢速度變得更快呢?sql
最基本的查詢算法固然是順序查找(linear search),也就是對比每一個元素的方法,不過這種算法在數據量很大時效率是極低的。
數據結構:有序或無序隊列
複雜度:O(n)
實例代碼:數據庫
//順序查找 int SequenceSearch(int a[], int value, int n) { int i; for(i=0; i<n; i++) if(a[i]==value) return i; return -1; }
比順序查找更快的查詢方法應該就是二分查找了,二分查找的原理是查找過程從數組的中間元素開始,若是中間元素正好是要查找的元素,則搜素過程結束;若是某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,並且跟開始同樣從中間元素開始比較。若是在某一步驟數組爲空,則表明找不到。
數據結構:有序數組
複雜度:O(logn)
實例代碼:數組
//二分查找,遞歸版本 int BinarySearch2(int a[], int value, int low, int high) { int mid = low+(high-low)/2; if(a[mid]==value) return mid; if(a[mid]>value) return BinarySearch2(a, value, low, mid-1); if(a[mid]<value) return BinarySearch2(a, value, mid+1, high); }
二叉排序樹的特色是:markdown
搜索的原理:數據結構
數據結構:二叉排序樹
時間複雜度: O(log2N)
其原理是首先根據key值和哈希函數建立一個哈希表(散列表),燃耗根據鍵值,經過散列函數,定位數據元素位置。
數據結構:哈希表
時間複雜度:幾乎是O(1)
,取決於產生衝突的多少。
分塊查找又稱索引順序查找,它是順序查找的一種改進方法。其算法思想是將n個數據元素」按塊有序」劃分爲m塊(m ≤ n)。每一塊中的結點沒必要有序,但塊與塊之間必須」按塊有序」;即第1塊中任一元素的關鍵字都必須小於第2塊中任一元素的關鍵字;而第2塊中任一元素又都必須小於第3塊中的任一元素,依次類推。
算法流程:
這種搜索算法每一次比較都使搜索範圍縮小一半。它們的查詢速度就有了很大的提高,複雜度爲。若是稍微分析一下會發現,每種查找算法都只能應用於特定的數據結構之上,例如二分查找要求被檢索數據有序,而二叉樹查找只能應用於二叉查找樹上,可是數據自己的組織結構不可能徹底知足各類數據結構(例如,理論上不可能同時將兩列都按順序進行組織),因此,在數據以外,數據庫系統還維護着知足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就能夠在這些數據結構上實現高級查找算法。這種數據結構,就是索引。
上面講到了二叉樹,它的搜索時間複雜度爲O(log2N)
,因此它的搜索效率和樹的深度有關,若是要提升查詢速度,那麼就要下降樹的深度。要下降樹的深度,很天然的方法就是採用多叉樹,再結合平衡二叉樹的思想,咱們能夠構建一個平衡多叉樹結構,而後就能夠在上面構建平衡多路查找算法,提升大數據量下的搜索效率。
B樹(Balance Tree)又叫作B- 樹(其實B-是由B-tree翻譯過來,因此B-樹和B樹是一個概念) ,它就是一種平衡多路查找樹。下圖就是一個典型的B樹:
從上圖中咱們能夠大體看到B樹的一些特色,爲了更好的描述B樹,咱們定義記錄爲一個二元組[key, data]
,key爲記錄的鍵值,data表示其它數據(上圖中只有key,沒有畫出data數據 )。下面是對B樹的一個詳細定義:
1. 有一個根節點,根節點只有一個記錄和兩個孩子或者根節點爲空; 2. 每一個節點記錄中的key和指針相互間隔,指針指向孩子節點; 3. d是表示樹的寬度,除葉子節點以外,其它每一個節點有[d/2,d-1]條記錄,而且些記錄中的key都是從左到右按大小排列的,有[d/2+1,d]個孩子; 4. 在一個節點中,第n個子樹中的全部key,小於這個節點中第n個key,大於第n-1個key,好比上圖中B節點的第2個子節點E中的全部key都小於B中的第2個key 9,大於第1個key 3; 5. 全部的葉子節點必須在同一層次,也就是它們具備相同的深度;
因爲B-Tree的特性,在B-Tree中按key檢索數據的算法很是直觀:首先從根節點進行二分查找,若是找到則返回對應節點的data,不然對相應區間的指針指向的節點遞歸進行查找,直到找到節點或找到null指針,前者查找成功,後者查找失敗。B-Tree上查找算法的僞代碼以下:
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);
關於B-Tree有一系列有趣的性質,例如一個度爲d的B-Tree,設其索引N個key,則其樹高h的上限爲logd((N+1)/2)
,檢索一個key,其查找節點個數的漸進複雜度爲O(logdN)
。從這點能夠看出,B-Tree是一個很是有效率的索引數據結構。
另外,因爲插入刪除新的數據記錄會破壞B-Tree的性質,所以在插入刪除時,須要對樹進行一個分裂、合併、轉移等操做以保持B-Tree性質,本文不打算完整討論B-Tree這些內容,由於已經有許多資料詳細說明了B-Tree的數學性質及插入刪除算法,有興趣的朋友能夠查閱其它文獻進行詳細研究。
其實B-Tree有許多變種,其中最多見的是B+Tree,好比MySQL就廣泛使用B+Tree實現其索引結構。B-Tree相比,B+Tree有如下不一樣點:
下面是一個簡單的B+Tree示意。
因爲並非全部節點都具備相同的域,所以B+Tree中葉節點和內節點通常大小不一樣。這點與B-Tree不一樣,雖然B-Tree中不一樣節點存放的key和指針可能數量不一致,可是每一個節點的域和上限是一致的,因此在實現中B-Tree每每對每一個節點申請同等大小的空間。通常來講,B+Tree比B-Tree更適合實現外存儲索引結構,具體緣由與外存儲器原理及計算機存取原理有關,將在下面討論。
通常在數據庫系統或文件系統中使用的B+Tree結構都在經典B+Tree的基礎上進行了優化,增長了順序訪問指針。
如圖所示,在B+Tree的每一個葉子節點增長一個指向相鄰葉子節點的指針,就造成了帶有順序訪問指針的B+Tree。作這個優化的目的是爲了提升區間訪問的性能,例如圖4中若是要查詢key爲從18到49的全部數據記錄,當找到18後,只需順着節點和指針順序遍歷就能夠一次性訪問到全部數據節點,極大提到了區間查詢效率。
這一節對B-Tree和B+Tree進行了一個簡單的介紹,下一節結合存儲器存取原理介紹爲何目前B+Tree是數據庫系統實現索引的首選數據結構。
上文說過,二叉樹、紅黑樹等數據結構也能夠用來實現索引,可是文件系統及數據庫系統廣泛採用B-/+Tree做爲索引結構,這一節將結合計算機組成原理相關知識討論B-/+Tree做爲索引的理論基礎。
在計算機系統中通常包含兩種類型的存儲,計算機主存(RAM)和外部存儲器(如硬盤、CD、SSD等)。在設計索引算法和存儲結構時,咱們必需要考慮到這兩種類型的存儲特色。主存的讀取速度快,相對於主存,外部磁盤的數據讀取速率要比主從慢好幾個數量級,具體它們之間的差異後面會詳細介紹。 上面講的全部查詢算法都是假設數據存儲在計算機主存中的,計算機主存通常比較小,實際數據庫中數據都是存儲到外部存儲器的。
通常來講,索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程當中就要產生磁盤I/O消耗,相對於內存存取,I/O存取的消耗要高几個數量級,因此評價一個數據結構做爲索引的優劣最重要的指標就是在查找過程當中磁盤I/O操做次數的漸進複雜度。換句話說,索引的結構組織要儘可能減小查找過程當中磁盤I/O的存取次數。下面詳細介紹內存和磁盤存取原理,而後再結合這些原理分析B-/+Tree做爲索引的效率。
目前計算機使用的主存基本都是隨機讀寫存儲器(RAM),現代RAM的結構和存取原理比較複雜,這裏本文拋卻具體差異,抽象出一個十分簡單的存取模型來講明RAM的工做原理。
從抽象角度看,主存是一系列的存儲單元組成的矩陣,每一個存儲單元存儲固定大小的數據。每一個存儲單元有惟一的地址,現代主存的編址規則比較複雜,這裏將其簡化成一個二維地址:經過一個行地址和一個列地址能夠惟必定位到一個存儲單元。上圖展現了一個4 x 4的主存模型。
主存的存取過程以下:
當系統須要讀取主存時,則將地址信號放到地址總線上傳給主存,主存讀到地址信號後,解析信號並定位到指定存儲單元,而後將此存儲單元數據放到數據總線上,供其它部件讀取。寫主存的過程相似,系統將要寫入單元地址和數據分別放在地址總線和數據總線上,主存讀取兩個總線的內容,作相應的寫操做。
這裏能夠看出,主存存取的時間僅與存取次數呈線性關係,由於不存在機械操做,兩次存取的數據的「距離」不會對時間有任何影響,例如,先取A0再取A1和先取A0再取D3的時間消耗是同樣的。
上文說過,索引通常以文件形式存儲在磁盤上,索引檢索須要磁盤I/O操做。與主存不一樣,磁盤I/O存在機械運動耗費,所以磁盤I/O的時間消耗是巨大的。
磁盤讀取數據靠的是機械運動,當須要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即肯定要讀的數據在哪一個磁道,哪一個扇區。爲了讀取這個扇區的數據,須要將磁頭放到這個扇區上方,爲了實現這一點,磁頭須要移動對準相應磁道,這個過程叫作尋道,所耗費時間叫作尋道時間,而後磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫作旋轉時間,最後即是對讀取數據的傳輸。 因此每次讀取數據花費的時間能夠分爲尋道時間、旋轉延遲、傳輸時間三個部分。其中:
那麼訪問一次磁盤的時間,即一次磁盤IO的時間約等於5+4.17 = 9ms左右,聽起來還挺不錯的,但要知道一臺500 -MIPS的機器每秒能夠執行5億條指令,由於指令依靠的是電的性質,換句話說執行一次IO的時間能夠執行40萬條指令,數據庫動輒十萬百萬乃至千萬級數據,每次9毫秒的時間,顯然是個災難。
因爲存儲介質的特性,磁盤自己存取就比主存慢不少,再加上機械運動耗費,磁盤的存取速度每每是主存的幾百分分之一,所以爲了提升效率,要儘可能減小磁盤I/O。爲了達到這個目的,磁盤每每不是嚴格按需讀取,而是每次都會預讀,即便只須要一個字節,磁盤也會從這個位置開始,順序向後讀取必定長度的數據放入內存。這樣作的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也一般會立刻被使用。程序運行期間所須要的數據一般比較集中。
因爲磁盤順序讀取的效率很高(不須要尋道時間,只需不多的旋轉時間),所以對於具備局部性的程序來講,預讀能夠提升I/O效率。預讀的長度通常爲頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操做系統每每將主存和磁盤存儲區分割爲連續的大小相等的塊,每一個存儲塊稱爲一頁(在許多操做系統中,頁得大小一般爲4k),主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,而後異常返回,程序繼續運行。
到這裏終於能夠分析爲什麼數據庫索引採用B-/+Tree存儲結構了。上文說過數據庫索引是存儲到磁盤的而咱們又通常以使用磁盤I/O次數來評價索引結構的優劣。先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多須要訪問h-1
個節點(根節點常駐內存)。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣每一個節點只須要一次I/O就能夠徹底載入。爲了達到這個目的,在實際實現B-Tree還須要使用以下技巧:每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。
B-Tree中一次檢索最多須要h-1
次I/O(根節點常駐內存),漸進複雜度爲O(h)=O(logdN)
。通常實際應用中,出度d是很是大的數字,一般超過100,所以h很是小(一般不超過3)。
綜上所述,若是咱們採用B-Tree存儲結構,搜索時I/O次數通常不會超過3次,因此用B-Tree做爲索引結構效率是很是高的。
從上面介紹咱們知道,B樹的搜索複雜度爲O(h)=O(logdN)
,因此樹的出度d越大,深度h就越小,I/O的次數就越少。B+Tree偏偏能夠增長出度d的寬度,由於每一個節點大小爲一個頁大小,因此出度的上限取決於節點內key和data的大小:
dmax=floor(pagesize/(keysize+datasize+pointsize))//floor表示向下取整
因爲B+Tree內節點去掉了data域,所以能夠擁有更大的出度,從而擁有更好的性能。
B-樹和B+樹查找過程基本一致。如上圖所示,若是要查找數據項29,那麼首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,在內存中用二分查找肯定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間由於很是短(相比磁盤的IO)能夠忽略不計,經過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,經過指針加載磁盤塊8到內存,發生第三次IO,同時內存中作二分查找找到29,結束查詢,總計三次IO。真實的狀況是,3層的b+樹能夠表示上百萬的數據,若是上百萬的數據查找只須要三次IO,性能提升將是巨大的,若是沒有索引,每一個數據項都要發生一次IO,那麼總共須要百萬次的IO,顯然成本很是很是高。
這一章從理論角度討論了與索引相關的數據結構與算法問題,下一章將討論B+Tree是如何具體實現爲MySQL中索引,同時將結合MyISAM和InnDB存儲引擎介紹非彙集索引和彙集索引兩種不一樣的索引實現形式。
在MySQL中,索引屬於存儲引擎級別的概念,不一樣存儲引擎對索引的實現方式是不一樣的,本文主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。
MyISAM引擎使用B+Tree做爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖:
這裏設表一共有三列,假設咱們以Col1爲主鍵,則上圖是一個MyISAM表的主索引(Primary key)示意。能夠看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是惟一的,而輔助索引的key能夠重複。若是咱們在Col2上創建一個輔助索引,則此索引的結構以下圖所示:
一樣也是一顆B+Tree,data域保存數據記錄的地址。所以,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,則取出其data域的值,而後以data域的值爲地址,讀取相應數據記錄。
MyISAM的索引方式也叫作「非彙集」的,之因此這麼稱呼是爲了與InnoDB的彙集索引區分。
雖然InnoDB也使用B+Tree做爲索引結構,但具體實現方式卻與MyISAM大相徑庭。
第一個重大區別是InnoDB的數據文件自己就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件自己就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以InnoDB表數據文件自己就是主索引。
上圖是InnoDB主索引(同時也是數據文件)的示意圖,能夠看到葉節點包含了完整的數據記錄。這種索引叫作彙集索引。由於InnoDB的數據文件自己要按主鍵彙集,因此InnoDB要求表必須有主鍵(MyISAM能夠沒有),若是沒有顯式指定,則MySQL系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段做爲主鍵,這個字段長度爲6個字節,類型爲長整形。
第二個與MyISAM索引的不一樣是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的全部輔助索引都引用主鍵做爲data域。例如,下圖爲定義在Col3上的一個輔助索引:
這裏以英文字符的ASCII碼做爲比較準則。彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。
瞭解不一樣存儲引擎的索引實現方式對於正確使用和優化索引都很是有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲何不建議使用過長的字段做爲主鍵,由於全部輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。再例如,用非單調的字段做爲主鍵在InnoDB中不是個好主意,由於InnoDB數據文件自己是一顆B+Tree,非單調的主鍵會形成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段做爲主鍵則是一個很好的選擇。
下一章將具體討論這些與索引有關的優化策略。
MySQL的優化主要分爲結構優化(Scheme optimization)和查詢優化(Query optimization)。本章討論的高性能索引策略主要屬於結構優化範疇。本章的內容徹底基於上文的理論基礎,實際上一旦理解了索引背後的機制,那麼選擇高性能的策略就變成了純粹的推理,而且能夠理解這些策略背後的邏輯。
首先介紹一下聯合索引。聯合索引其實很簡單,相對於通常索引只有一個字段,聯合索引能夠爲多個字段建立一個索引。它的原理也很簡單,好比,咱們在(a,b,c)字段上建立一個聯合索引,則索引記錄會首先按照A字段排序,而後再按照B字段排序而後再是C字段,所以,聯合索引的特色就是:
當第一個字段值相等的時候,第二個字段又是有序的,好比下表中當A=2時全部B的值是有序排列的,依次類推,當同一個B值得全部C字段是有序排列的
| A | B | C |
| 1 | 2 | 3 |
| 1 | 4 | 2 |
| 1 | 1 | 4 |
| 2 | 3 | 5 |
| 2 | 4 | 4 |
| 2 | 4 | 6 |
| 2 | 5 | 5 |
其實聯合索引的查找就跟查字典是同樣的,先根據第一個字母查,而後再根據第二個字母查,或者只根據第一個字母查,可是不能跳過第一個字母從第二個字母開始查。這就是所謂的最左前綴原理。
咱們再來詳細介紹一下聯合索引的查詢。仍是上面例子,咱們在(a,b,c)
字段上建了一個聯合索引,因此這個索引是先按a 再按b 再按c進行排列的,因此:
如下的查詢方式均可以用到索引
select * from table where a=1; select * from table where a=1 and b=2; select * from table where a=1 and b=2 and c=3;
上面三個查詢按照 (a ), (a,b ),(a,b,c )
的順序均可以利用到索引,這就是最左前綴匹配。
若是查詢語句是:
select * from table where a=1 and c=3; 那麼只會用到索引a。
若是查詢語句是:
select * from table where b=2 and c=3; 由於沒有用到最左前綴a,因此這個查詢是用戶到索引的。
若是用到了最左前綴,可是順序顛倒會用到索引碼?
好比:
select * from table where b=2 and a=1; select * from table where b=2 and a=1 and c=3;
若是用到了最左前綴而只是顛倒了順序,也是能夠用到索引的,由於mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃。但咱們仍是最好按照索引順序來查詢,這樣查詢優化器就不用從新編譯了。
除了聯合索引以外,對mysql來講其實還有一種前綴索引。前綴索引就是用列的前綴代替整個列做爲索引key,當前綴長度合適時,能夠作到既使得前綴索引的選擇性接近全列索引,同時由於索引key變短而減小了索引文件的大小和維護開銷。
通常來講如下狀況可使用前綴索引:
一些文章中也提到:
MySQL 前綴索引能有效減少索引文件的大小,提升索引的速度。可是前綴索引也有它的壞處:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前綴索引,也不能把它們用做覆蓋索引(Covering Index)。
SELECT * FROM
houdunwangWHERE
unameLIKE'後盾%' -- 走索引
SELECT * FROM
houdunwangWHERE
unameLIKE "%後盾%" -- 不走索引
CREATE TABLE
a(
achar(10));
EXPLAIN SELECT * FROM
aWHERE
a="1"
– 走索引 a
WHERE a
=1 – 不走索引 參考文章:
http://blog.csdn.net/suifeng3051/article/details/49530299?locationNum=1
http://tech.meituan.com/mysql-index.html
https://yq.aliyun.com/articles/39841
http://blog.csdn.net/lovelion/article/details/8462814
轉載:https://yq.aliyun.com/articles/65126
建索引原則:
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)的索引,那麼只須要修改原來的索引便可