日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

B+树索引实战:全值匹配查询

發(fā)布時(shí)間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 B+树索引实战:全值匹配查询 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

索引的代價(jià)

空間上的代價(jià)

一個(gè)索引都為對(duì)應(yīng)一棵B+樹(shù),樹(shù)中每一個(gè)節(jié)點(diǎn)都是一個(gè)數(shù)據(jù)頁(yè),一個(gè)頁(yè)默認(rèn)會(huì)占用16KB的存儲(chǔ)空間,所以一個(gè)索引也是會(huì)占用磁盤空間的。(了解更多Java知識(shí)可關(guān)注微信公眾號(hào)“老周扯IT”)

時(shí)間上的代價(jià)

索引是對(duì)數(shù)據(jù)的排序,那么當(dāng)對(duì)表中的數(shù)據(jù)進(jìn)行增、刪、改操作時(shí),都需要去維護(hù)修改內(nèi)容涉及到的B+樹(shù)索引。所以在進(jìn)行增、刪、改操作時(shí)可能需要額外的時(shí)間進(jìn)行一些記錄移動(dòng),頁(yè)面分裂、頁(yè)面回收等操作來(lái)維護(hù)好排序。

B+樹(shù)索引實(shí)戰(zhàn)

全值匹配

select * from t1 where b = 1 and c = 1 and d = 1;

查詢優(yōu)化器會(huì)分析這些查詢條件并且按照可以使用的索引中列的順序來(lái)決定先使用哪個(gè)查詢條件。

匹配左邊的列

select * from t1 where b = 1;

select * from t1 where b = 1 and c = 1;

下面這個(gè)sql是用不到索引的

select * from t1 where c = 1;

因?yàn)锽+樹(shù)先是按照b列的值排序的,在b列的值相同的情況下才使用c列進(jìn)行排序,也就是說(shuō)b列的值不同的記錄中c的值可能是無(wú)序的。而現(xiàn)在你跳過(guò)b列直接根據(jù)c的值去查找,這是做不到的。

匹配列前綴

如果只給出后綴或者中間的某個(gè)字符串,比如:

select * from t1 where b like '%101%';

這種是用不到索引的,因?yàn)樽址虚g有’101’的字符串并沒(méi)有排好序,所以只能全表掃描了。有時(shí)候我們有一些匹配某些字符串后綴的需求,比方說(shuō)某個(gè)表有一個(gè)url列,該列中存儲(chǔ)了許多url:

www.baidu.com

www.google.com

www.qq.com

假設(shè)已經(jīng)對(duì)該url列創(chuàng)建了索引,如果我們想查詢以com為后綴的網(wǎng)址的話可以這樣寫查詢條件:WHERE url LIKE ‘%com’,但是這樣的話無(wú)法使用該url列的索引。為了在查詢時(shí)用到這個(gè)索引而不至于全表掃描,我們可以把后綴查詢改寫成前綴查詢,不過(guò)我們就得把表中的數(shù)據(jù)全部逆序存儲(chǔ)一下,也就是說(shuō)我們可以這樣保存url列中的數(shù)據(jù):

moc.udiab.www

moc.elgoog.www

moc.qq.www

這樣再查找以com為后綴的網(wǎng)址時(shí)搜索條件便可以這么寫:WHERE url LIKE ‘moc%’,這樣就可以用到索引了。

匹配范圍值

select * from t1 where b > 1 and b < 20000;

由于B+樹(shù)中的數(shù)據(jù)頁(yè)和記錄是先按b列排序的,所以我們上邊的查詢過(guò)程其實(shí)是這樣的:

  • 找到b值為1的記錄。

  • 找到b值為20000的記錄。

  • 由于所有記錄都是由鏈表連起來(lái)的(記錄之間用單鏈表,數(shù)據(jù)頁(yè)之間用雙鏈表),所以他們之間的記錄都可以很容易的取出來(lái)

  • 找到這些記錄的主鍵值,再到聚簇索引中回表查找完整的記錄。

不過(guò)在使用聯(lián)合進(jìn)行范圍查找的時(shí)候需要注意,如果對(duì)多個(gè)列同時(shí)進(jìn)行范圍查找的話,只有對(duì)索引最左邊的那個(gè)列進(jìn)行范圍查找的時(shí)候才能用到B+樹(shù)索引,比如:

select * from t1 where b > 1 and c > 1;

上邊這個(gè)查詢可以分成兩個(gè)部分:

  • 通過(guò)條件b > 1來(lái)對(duì)b進(jìn)行范圍,查找的結(jié)果可能有多條b值不同的記錄,

  • 對(duì)這些b值不同的記錄繼續(xù)通過(guò)c > 1繼續(xù)過(guò)濾。

  • 這樣子對(duì)于聯(lián)合索引來(lái)說(shuō),只能用到b列的部分,而用不到c列的部分,因?yàn)橹挥衎值相同的情況下才能用c列的值進(jìn)行排序,而這個(gè)查詢中通過(guò)b進(jìn)行范圍查找的記錄中可能并不是按照c列進(jìn)行排序的,所以在搜索條件中繼續(xù)以c列進(jìn)行查找時(shí)是用不到這個(gè)B+樹(shù)索引的。

    精確匹配某一列并范圍匹配另外一列

    對(duì)于同一個(gè)聯(lián)合索引來(lái)說(shuō),雖然對(duì)多個(gè)列都進(jìn)行范圍查找時(shí)只能用到最左邊那個(gè)索引列,但是如果左邊的列是精確查找,則右邊的列可以進(jìn)行范圍查找,比方說(shuō)這樣:

    select * from t1 where b = 1 and c > 1;

    排序

    select * from t1 order by b, c, d;

    這個(gè)查詢的結(jié)果集需要先按照b值排序,如果記錄的b值相同,則需要按照c來(lái)排序,如果c的值相同,則需要按照d排序。因?yàn)檫@個(gè)B+樹(shù)索引本身就是按照上述規(guī)則排好序的,所以直接從索引中提取數(shù)據(jù),然后進(jìn)行回表操作取出該索引中不包含的列就好了。

    分組

    select b, c, d, count(*) from t1 group by b, c, d;

    這個(gè)查詢語(yǔ)句相當(dāng)于做了3次分組操作:

  • 先把記錄按照b值進(jìn)行分組,所有b值相同的記錄劃分為一組。

  • 將每個(gè)b值相同的分組里的記錄再按照c的值進(jìn)行分組,將title值相同的記錄放到一個(gè)分組里。

  • 再將上一步中產(chǎn)生的分組按照d的值分成更小的分組。

  • 如果沒(méi)有索引的話,這個(gè)分組過(guò)程全部需要在內(nèi)存里實(shí)現(xiàn),而如果有索引的話,正好這個(gè)分組順序又和B+樹(shù)中的索引列的順序是一致的,所以可以直接使用B+樹(shù)索引進(jìn)行分組。

    使用聯(lián)合索引進(jìn)行排序或分組的注意事項(xiàng)

    對(duì)于聯(lián)合索引有個(gè)問(wèn)題需要注意,ORDER BY的子句后邊的列的順序也必須按照索引列的順序給出,如果給出 order by c, b, d 的順序,那也是用不了B+樹(shù)索引的。

    同理, order by b order by b, c 這種匹配索引左邊的列的形式可以使用部分的B+樹(shù)索引。當(dāng)聯(lián)合索引左邊列的值為常量,也可以使用后邊的列進(jìn)行排序,比如這樣:

    select * from t1 where b = 1 order by c, d;

    這個(gè)查詢能使用聯(lián)合索引進(jìn)行排序是因?yàn)閎列的值相同的記錄是按照c, d排序的。

    不可以使用索引進(jìn)行排序或分組的幾種情況

    ASC、DESC混用

    對(duì)于使用聯(lián)合索引進(jìn)行排序的場(chǎng)景,我們要求各個(gè)排序列的排序順序是一致的,也就是要么各個(gè)列都是ASC規(guī)則排序,要么都是DESC規(guī)則排序。

    ORDER BY子句后的列如果不加ASC或者DESC默認(rèn)是按照ASC排序規(guī)則排序的,也就是升序排序的。

    select * from t1 order by b ASC, c DESC;

    這個(gè)查詢是用不到索引的。

    如何建立索引

    考慮索引選擇性

    索引的選擇性(Selectivity),是指不重復(fù)的索引值(也叫基數(shù),Cardinality)與表記錄數(shù)的比值:

    選擇性=基數(shù)/記錄數(shù)

    選擇性的取值范圍為(0, 1],選擇性越高的索引價(jià)值越大。如果選擇性等于1,就代表這個(gè)列的不重復(fù)值和表記錄數(shù)是一樣的,那么對(duì)這個(gè)列建立索引是非常合適的,如果選擇性非常小,那么就代表這個(gè)列的重復(fù)值是很多的,不適合建立索引。

    考慮前綴索引

    用列的前綴代替整個(gè)列作為索引key,當(dāng)前綴長(zhǎng)度合適時(shí),可以做到既使得前綴索引的選擇性接近全列索引,同時(shí)因?yàn)樗饕齥ey變短而減少了索引文件的大小和維護(hù)開(kāi)銷。

    使用mysql官網(wǎng)提供的示例數(shù)據(jù)庫(kù):https://dev.mysql.com/doc/employee/en/employees-installation.html

    github地址:https://github.com/datacharmer/test_db.git

    employees表只有一個(gè)索引,那么如果我們想按名字搜索一個(gè)人,就只能全表掃描了:

    EXPLAIN SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido';

    那么可以對(duì)或建立索引,看下兩個(gè)索引的選擇性:

    SELECT count(DISTINCT(first_name))/count(*) ASSelectivity FROM employees.employees; -- 0.0042 SELECTcount(DISTINCT(concat(first_name,last_name)))/count(*) AS Selectivity FROM employees.employees; -- 0.9313

    顯然選擇性太低,選擇性很好,但是first_name和last_name加起來(lái)長(zhǎng)度為30,有沒(méi)有兼顧長(zhǎng)度和選擇性的辦法?

    可以考慮用first_name和last_name的前幾個(gè)字符建立索引,例如,看看其選擇性:

    SELECT count(DISTINCT(concat(first_name, left(last_name, 3))))/count(*) AS Selectivity FROM employees.employees; -- 0.7879

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

    SELECT count(DISTINCT(concat(first_name, left(last_name, 4))))/count(*) AS Selectivity FROM employees.employees; -- 0.9007

    這時(shí)選擇性已經(jīng)很理想了,而這個(gè)索引的長(zhǎng)度只有18,比短了接近一半,建立前綴索引的方式為:

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

    前綴索引兼顧索引大小和查詢速度,但是其缺點(diǎn)是不能用于ORDER BY和GROUP BY操作,也不能用于覆蓋索引。

    總結(jié)

    索引列的類型盡量小

    利用索引字符串值的前綴

    主鍵自增

    定位并刪除表中的重復(fù)和冗余索引

    盡量使用覆蓋索引進(jìn)行查詢,避免回表帶來(lái)的性能損耗。

    文章分享到這就結(jié)束了,在這小編為大家整理了MySQL學(xué)習(xí)資料,大家自行下載即可

    總結(jié)

    以上是生活随笔為你收集整理的B+树索引实战:全值匹配查询的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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