JVM內(nèi)存模型及分區(qū)
Java虛擬機(jī)在程序執(zhí)行過程會把jvm的內(nèi)存分為若干個(gè)不同的數(shù)據(jù)區(qū)域來管理今瀑,這些區(qū)域有自己的用途鲤脏,以及創(chuàng)建和銷毀時(shí)間考阱。
jvm管理的內(nèi)存區(qū)域包括以下幾個(gè)區(qū)域:
棧區(qū):
棧分為java虛擬機(jī)棧和本地方法棧
重點(diǎn)是Java虛擬機(jī)棧亲族,它是線程私有的狠半,生命周期與線程相同。
每個(gè)方法執(zhí)行都會創(chuàng)建一個(gè)棧幀弃甥,用于存放局部變量表爽室,操作棧,動態(tài)鏈接淆攻,方法出口等阔墩。每個(gè)方法從被調(diào)用,直到被執(zhí)行完瓶珊。對應(yīng)著一個(gè)棧幀在虛擬機(jī)中從入棧到出棧的過程啸箫。
通常說的棧就是指局部變量表部分,存放編譯期間可知的8種基本數(shù)據(jù)類型伞芹,及對象引用和指令地址忘苛。局部變量表是在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí)丑瞧,這個(gè)棧中的局部變量分配內(nèi)存大小是確定的柑土。
會有兩種異常StackOverFlowError和 OutOfMemoneyError。當(dāng)線程請求棧深度大于虛擬機(jī)所允許的深度就會拋出StackOverFlowError錯(cuò)誤绊汹;虛擬機(jī)棧動態(tài)擴(kuò)展稽屏,當(dāng)擴(kuò)展無法申請到足夠的內(nèi)存空間時(shí)候,拋出OutOfMemoneyError西乖。
本地方法棧 為虛擬機(jī)使用到本地方法服務(wù)(native)
堆區(qū):
堆被所有線程共享區(qū)域狐榔,在虛擬機(jī)啟動時(shí)創(chuàng)建坛增,唯一目的存放對象實(shí)例。
堆區(qū)是gc的主要區(qū)域薄腻,通常情況下分為兩個(gè)區(qū)塊年輕代和年老代收捣。更細(xì)一點(diǎn)年輕代又分為Eden區(qū)最要放新創(chuàng)建對象,F(xiàn)rom survivor 和 To survivor 保存gc后幸存下的對象庵楷,默認(rèn)情況下各自占比 8:1:1罢艾。
不過很多文章介紹分為3個(gè)區(qū)塊,把方法區(qū)算著為永久代尽纽。這大概是基于Hotspot虛擬機(jī)劃分咐蚯, 然后比如IBM j9就不存在永久代概論。不管怎么分區(qū)弄贿,都是存放對象實(shí)例春锋。
會有異常OutOfMemoneyError
方法區(qū):
被所有線程共享區(qū)域,用于存放已被虛擬機(jī)加載的類信息差凹,常量期奔,靜態(tài)變量等數(shù)據(jù)。被Java虛擬機(jī)描述為堆的一個(gè)邏輯部分危尿。習(xí)慣是也叫它永久代(permanment generation)
垃圾回收很少光顧這個(gè)區(qū)域呐萌,不過也是需要回收的,主要針對常量池回收谊娇,類型卸載搁胆。
常量池用于存放編譯期生成的各種字節(jié)碼和符號引用,常量池具有一定的動態(tài)性邮绿,里面可以存放編譯期生成的常量;運(yùn)行期間的常量也可以添加進(jìn)入常量池中攀例,比如string的intern()方法船逮。
程序計(jì)數(shù)器:
當(dāng)前線程所執(zhí)行的行號指示器。通過改變計(jì)數(shù)器的值來確定下一條指令粤铭,比如循環(huán)挖胃,分支,跳轉(zhuǎn)梆惯,異常處理酱鸭,線程恢復(fù)等都是依賴計(jì)數(shù)器來完成。
Java虛擬機(jī)多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式實(shí)現(xiàn)的垛吗。為了線程切換能恢復(fù)到正確的位置凹髓,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,所以它是線程私有的怯屉。
唯一一塊Java虛擬機(jī)沒有規(guī)定任何OutofMemoryError的區(qū)塊
jvm分區(qū)大致就這個(gè)塊蔚舀,具體里面還有很多細(xì)節(jié)饵沧,及其各個(gè)模塊工作的算法都很復(fù)雜,這里只是對分區(qū)進(jìn)行簡單介紹赌躺,掌握一些基本的知識點(diǎn)狼牺。
GC可達(dá)性,哪些可以作為GCRoots
可達(dá)性分析算法的思想:從一個(gè)被稱為GC Roots的對象開始向下搜索礼患,如果一個(gè)對象到GC Roots沒有任何引用鏈相連時(shí)是钥,則說明此對象不可用。
在java中可以作為GC Roots的對象有以下幾種:
虛擬機(jī)棧中引用的對象缅叠、方法區(qū)類靜態(tài)屬性引用的對象悄泥、方法區(qū)常量池引用的對象、本地方法棧JNI引用的對象痪署。
雖然這些算法可以判定一個(gè)對象是否能被回收码泞,但是當(dāng)滿足上述條件時(shí),一個(gè)對象 不一定會被回收狼犯。當(dāng)一個(gè)對象不可達(dá)GC Roots時(shí)余寥,這個(gè)對象并不會馬上被回收,而是處于一個(gè)死緩的階段悯森,若要被真正的回收需要經(jīng)歷兩次標(biāo)記宋舷。如果對象在可達(dá)性分析中沒有與GC Roots的引用鏈,那么此時(shí)就會被第一次標(biāo)記并且進(jìn)行一次篩選瓢姻,篩選的條件是是否有必要執(zhí)行finalize()方法祝蝠。當(dāng)對象沒有覆蓋finalize()方法或者已經(jīng)被虛擬機(jī)調(diào)用過,那么就認(rèn)為是沒必要的幻碱。
如果該對象有必要執(zhí)行finalize()方法绎狭,那么這個(gè)對象將會放在一個(gè)稱為F-Queue的隊(duì)列中,虛擬機(jī)會觸發(fā)一個(gè)finalize()線程去執(zhí)行褥傍,此線程是低優(yōu)先級的儡嘶,并且虛擬機(jī)不會承諾一直等待它運(yùn)行完,這還是因?yàn)槿绻鹒inalize()執(zhí)行緩慢或者發(fā)生了死鎖恍风,那么就會造成F-Queue隊(duì)列一直等待蹦狂,造成了內(nèi)存回收系統(tǒng)的崩潰。GC對處于F-Queue中的對象進(jìn)行第二次被標(biāo)記朋贬,這時(shí)凯楔,該對象將被移除“即將回收”集合,等待回收锦募。
GC管理的主要區(qū)域是Java堆摆屯,一般情況下只針對堆進(jìn)行垃圾回收。
方法區(qū)御滩、棧和本地方法區(qū)不被GC所管理,因而選擇這些區(qū)域內(nèi)的對象作為GC roots,被GC roots引用的對象不被GC回收鸥拧。
詳細(xì):
GC Root常說的GC(Garbage Collector) roots党远,特指的是垃圾收集器(Garbage Collector)的對象,GC會收集那些不是GC roots且沒有被GC roots引用的對象富弦。
一個(gè)對象可以屬于多個(gè)root沟娱,GC root有幾下種:
Class - 由系統(tǒng)類加載器(system class loader)加載的對象,這些類是不能夠被回收的腕柜,他們可以以靜態(tài)字段的方式保存持有其它對象济似。我們需要注意的一點(diǎn)就是,通過用戶自定義的類加載器加載的類盏缤,除非相應(yīng)的java.lang.Class實(shí)例以其它的某種(或多種)方式成為roots砰蠢,否則它們并不是roots。
Thread - 活著的線程
Stack Local - Java方法的local變量或參數(shù)
JNI Local - JNI方法的local變量或參數(shù)
JNI Global - 全局JNI引用
Monitor Used - 用于同步的監(jiān)控對象
Held by JVM - 用于JVM特殊目的由GC保留的對象唉铜,但實(shí)際上這個(gè)與JVM的實(shí)現(xiàn)是有關(guān)的台舱。可能已知的一些類型是:系統(tǒng)類加載器潭流、一些JVM知道的重要的異常類竞惋、一些用于處理異常的預(yù)分配對象以及一些自定義的類加載器等。然而灰嫉,JVM并沒有為這些對象提供其它的信息拆宛,因此需要去確定哪些是屬于"JVM持有"的了。
常用JVM參數(shù)
JVM類加載機(jī)制(類加載器的雙親委派加載機(jī)制)
JVM類加載機(jī)制詳解(一)JVM類加載過程
JVM類加載機(jī)制詳解(二)類加載器與雙親委派模型
Java線程安全的集合
一讼撒、早期線程安全的集合
我們先從早期的線程安全的集合說起浑厚,它們是Vector和HashTable
1.Vector
Vector和ArrayList類似,是長度可變的數(shù)組根盒,與ArrayList不同的是钳幅,Vector是線程安全的,它給幾乎所有的public方法都加上了synchronized關(guān)鍵字炎滞。由于加鎖導(dǎo)致性能降低贡这,在不需要并發(fā)訪問同一對象時(shí),這種強(qiáng)制性的同步機(jī)制就顯得多余厂榛,所以現(xiàn)在Vector已被棄用
2.HashTable
HashTable和HashMap類似,不同點(diǎn)是HashTable是線程安全的丽惭,它給幾乎所有public方法都加上了synchronized關(guān)鍵字击奶,還有一個(gè)不同點(diǎn)是HashTable的K,V都不能是null责掏,但HashMap可以柜砾,它現(xiàn)在也因?yàn)樾阅茉虮粭売昧?/p>
二、Collections包裝方法
Vector和HashTable被棄用后换衬,它們被ArrayList和HashMap代替痰驱,但它們不是線程安全的证芭,所以Collections工具類中提供了相應(yīng)的包裝方法把它們包裝成線程安全的集合
List<E> synArrayList = Collections.synchronizedList(new ArrayList<E>());
Set<E> synHashSet = Collections.synchronizedSet(new HashSet<E>());
Map<K,V> synHashMap = Collections.synchronizedMap(new HashMap<K,V>());
Collections針對每種集合都聲明了一個(gè)線程安全的包裝類,在原集合的基礎(chǔ)上添加了鎖對象担映,集合中的每個(gè)方法都通過這個(gè)鎖對象實(shí)現(xiàn)同步
三废士、java.util.concurrent包中的集合
1.ConcurrentHashMap
ConcurrentHashMap和HashTable都是線程安全的集合,它們的不同主要是加鎖粒度上的不同蝇完。HashTable的加鎖方法是給每個(gè)方法加上synchronized關(guān)鍵字官硝,這樣鎖住的是整個(gè)Table對象。而ConcurrentHashMap是更細(xì)粒度的加鎖
在JDK1.8之前短蜕,ConcurrentHashMap加的是分段鎖氢架,也就是Segment鎖,每個(gè)Segment含有整個(gè)table的一部分朋魔,這樣不同分段之間的并發(fā)操作就互不影響
JDK1.8對此做了進(jìn)一步的改進(jìn)岖研,它取消了Segment字段,直接在table元素上加鎖警检,實(shí)現(xiàn)對每一行進(jìn)行加鎖孙援,進(jìn)一步減小了并發(fā)沖突的概率
2.CopyOnWriteArrayList和CopyOnWriteArraySet
它們是加了寫鎖的ArrayList和ArraySet,鎖住的是整個(gè)對象解滓,但讀操作可以并發(fā)執(zhí)行
3.除此之外還有ConcurrentSkipListMap赃磨、ConcurrentSkipListSet、ConcurrentLinkedQueue洼裤、ConcurrentLinkedDeque等邻辉,至于為什么沒有ConcurrentArrayList,原因是無法設(shè)計(jì)一個(gè)通用的而且可以規(guī)避ArrayList的并發(fā)瓶頸的線程安全的集合類腮鞍,只能鎖住整個(gè)list值骇,這用Collections里的包裝類就能辦到
常用線程池參數(shù)
Redis過期策略 實(shí)現(xiàn)原理
我們在使用redis時(shí),一般會設(shè)置一個(gè)過期時(shí)間移国,當(dāng)然也有不設(shè)置過期時(shí)間的吱瘩,也就是永久不過期。
當(dāng)我們設(shè)置了過期時(shí)間迹缀,redis是如何判斷是否過期使碾,以及根據(jù)什么策略來進(jìn)行刪除的。
1.redis設(shè)置過期時(shí)間:
expire key time(以秒為單位)--這是最常用的方式
setex(String key, int seconds, String value)--字符串獨(dú)有的方式
注:
除了字符串自己獨(dú)有設(shè)置過期時(shí)間的方法外祝懂,其他方法都需要依靠expire方法來設(shè)置時(shí)間
如果沒有設(shè)置時(shí)間票摇,那緩存就是永不過期
如果設(shè)置了過期時(shí)間,之后又想讓緩存永不過期砚蓬,使用persist key
2.三種過期策略:
a.定時(shí)刪除
含義:在設(shè)置key的過期時(shí)間的同時(shí)矢门,為該key創(chuàng)建一個(gè)定時(shí)器,讓定時(shí)器在key的過期時(shí)間來臨時(shí),對key進(jìn)行刪除
優(yōu)點(diǎn):保證內(nèi)存被盡快釋放
缺點(diǎn):若過期key很多祟剔,刪除這些key會占用很多的CPU時(shí)間隔躲,在CPU時(shí)間緊張的情況下,CPU不能把所有的時(shí)間用來做要緊的事兒物延,還需要去花時(shí)間刪除這些key.定時(shí)器的創(chuàng)建耗時(shí)宣旱,若為每一個(gè)設(shè)置過期時(shí)間的key創(chuàng)建一個(gè)定時(shí)器(將會有大量的定時(shí)器產(chǎn)生),性能影響嚴(yán)重
b.懶漢式刪除
含義:key過期的時(shí)候不刪除教届,每次通過key獲取值的時(shí)候去檢查是否過期响鹃,若過期,則刪除案训,返回null买置。
優(yōu)點(diǎn):刪除操作只發(fā)生在通過key取值的時(shí)候發(fā)生,而且只刪除當(dāng)前key强霎,所以對CPU時(shí)間的占用是比較少的忿项,而且此時(shí)的刪除是已經(jīng)到了非做不可的地步(如果此時(shí)還不刪除的話,我們就會獲取到了已經(jīng)過期的key了)
缺點(diǎn):若大量的key在超出超時(shí)時(shí)間后城舞,很久一段時(shí)間內(nèi)轩触,都沒有被獲取過,那么可能發(fā)生內(nèi)存泄露(無用的垃圾占用了大量的內(nèi)存)
c.定期刪除
含義:每隔一段時(shí)間執(zhí)行一次刪除過期key操作
優(yōu)點(diǎn):通過限制刪除操作的時(shí)長和頻率家夺,來減少刪除操作對CPU時(shí)間的占用(處理"定時(shí)刪除"的缺點(diǎn))脱柱,定期刪除過期key(處理"懶漢式刪除"的缺點(diǎn))
缺點(diǎn):在內(nèi)存友好方面,不如"定時(shí)刪除"(會造成一定的內(nèi)存占用拉馋,但是沒有懶漢式那么占用內(nèi)存)在CPU時(shí)間友好方面榨为,不如"懶漢式刪除"(會定期的去進(jìn)行比較和刪除操作,cpu方面不如懶漢式煌茴,但是比定時(shí)好)
難點(diǎn):合理設(shè)置刪除操作的執(zhí)行時(shí)長(每次刪除執(zhí)行多長時(shí)間)和執(zhí)行頻率(每隔多長時(shí)間做一次刪除)(這個(gè)要根據(jù)服務(wù)器運(yùn)行情況來定了)随闺,每次執(zhí)行時(shí)間太長,或者執(zhí)行頻率太高對cpu都是一種壓力蔓腐。
每次進(jìn)行定期刪除操作執(zhí)行之后矩乐,需要記錄遍歷循環(huán)到了哪個(gè)標(biāo)志位,以便下一次定期時(shí)間來時(shí)回论,從上次位置開始進(jìn)行循環(huán)遍歷
memcached只是用了惰性刪除散罕,而redis同時(shí)使用了惰性刪除與定期刪除,這也是二者的一個(gè)不同點(diǎn)(可以看做是redis優(yōu)于memcached的一點(diǎn))傀蓉;
對于懶漢式刪除而言笨使,并不是只有獲取key的時(shí)候才會檢查key是否過期,在某些設(shè)置key的方法上也會檢查(eg.setnx key2 value2:該方法類似于memcached的add方法僚害,如果設(shè)置的key2已經(jīng)存在,那么該方法返回false,什么都不做萨蚕;如果設(shè)置的key2不存在靶草,那么該方法設(shè)置緩存key2-value2。假設(shè)調(diào)用此方法的時(shí)候岳遥,發(fā)現(xiàn)redis中已經(jīng)存在了key2奕翔,但是該key2已經(jīng)過期了,如果此時(shí)不執(zhí)行刪除操作的話浩蓉,setnx方法將會直接返回false,也就是說此時(shí)并沒有重新設(shè)置key2-value2成功捻艳,所以對于一定要在setnx執(zhí)行之前驾窟,對key2進(jìn)行過期檢查)。
3.Redis采用的過期策略
懶漢式刪除+定期刪除
懶漢式刪除流程:
在進(jìn)行g(shù)et或setnx等操作時(shí)认轨,先檢查key是否過期绅络;
若過期,刪除key嘁字,然后執(zhí)行相應(yīng)操作恩急;
若沒過期,直接執(zhí)行相應(yīng)操作纪蜒;
定期刪除流程(簡單而言衷恭,對指定個(gè)數(shù)個(gè)庫的每一個(gè)庫隨機(jī)刪除小于等于指定個(gè)數(shù)個(gè)過期key):
遍歷每個(gè)數(shù)據(jù)庫(就是redis.conf中配置的"database"數(shù)量,默認(rèn)為16)
檢查當(dāng)前庫中的指定個(gè)數(shù)個(gè)key(默認(rèn)是每個(gè)庫檢查20個(gè)key纯续,注意相當(dāng)于該循環(huán)執(zhí)行20次随珠,循環(huán)體是下邊的描述)
如果當(dāng)前庫中沒有一個(gè)key設(shè)置了過期時(shí)間,直接執(zhí)行下一個(gè)庫的遍歷
隨機(jī)獲取一個(gè)設(shè)置了過期時(shí)間的key杆烁,檢查該key是否過期牙丽,如果過期,刪除key
判斷定期刪除操作是否已經(jīng)達(dá)到指定時(shí)長兔魂,若已經(jīng)達(dá)到烤芦,直接退出定期刪除。
對于定期刪除析校,在程序中有一個(gè)全局變量current_db來記錄下一個(gè)將要遍歷的庫构罗,假設(shè)有16個(gè)庫,我們這一次定期刪除遍歷了10個(gè)智玻,那此時(shí)的current_db就是11遂唧,下一次定期刪除就從第11個(gè)庫開始遍歷,假設(shè)current_db等于15了吊奢,那么之后遍歷就再從0號庫開始(此時(shí)current_db==0)
在實(shí)際中盖彭,如果我們要自己設(shè)計(jì)過期策略,在使用懶漢式刪除+定期刪除時(shí),控制時(shí)長和頻率這個(gè)尤為關(guān)鍵召边,需要結(jié)合服務(wù)器性能铺呵,已經(jīng)并發(fā)量等情況進(jìn)行調(diào)整,以致最佳隧熙。
Redis單線程速度快的原因
1片挂、完全基于內(nèi)存,絕大部分請求是純粹的內(nèi)存操作贞盯,非骋裟睿快速。數(shù)據(jù)存在內(nèi)存中躏敢,類似于HashMap闷愤,HashMap的優(yōu)勢就是查找和操作的時(shí)間復(fù)雜度都是O(1);
2父丰、數(shù)據(jù)結(jié)構(gòu)簡單肝谭,對數(shù)據(jù)操作也簡單,Redis中的數(shù)據(jù)結(jié)構(gòu)是專門進(jìn)行設(shè)計(jì)的蛾扇;
3攘烛、采用單線程,避免了不必要的上下文切換和競爭條件镀首,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU坟漱,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作更哄,沒有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗芋齿;
4、使用多路I/O復(fù)用模型成翩,非阻塞IO觅捆;
5、使用底層模型不同麻敌,它們之間底層實(shí)現(xiàn)方式以及與客戶端之間通信的應(yīng)用協(xié)議不一樣栅炒,Redis直接自己構(gòu)建了VM 機(jī)制 ,因?yàn)橐话愕南到y(tǒng)調(diào)用系統(tǒng)函數(shù)的話术羔,會浪費(fèi)一定的時(shí)間去移動和請求赢赊;
以上幾點(diǎn)都比較好理解,下邊我們針對多路 I/O 復(fù)用模型進(jìn)行簡單的探討:
(1)多路 I/O 復(fù)用模型
多路I/O復(fù)用模型是利用 select级历、poll释移、epoll 可以同時(shí)監(jiān)察多個(gè)流的 I/O 事件的能力,在空閑的時(shí)候寥殖,會把當(dāng)前線程阻塞掉玩讳,當(dāng)有一個(gè)或多個(gè)流有 I/O 事件時(shí)涩蜘,就從阻塞態(tài)中喚醒,于是程序就會輪詢一遍所有的流(epoll 是只輪詢那些真正發(fā)出了事件的流)熏纯,并且只依次順序的處理就緒的流皱坛,這種做法就避免了大量的無用操作。
這里“多路”指的是多個(gè)網(wǎng)絡(luò)連接豆巨,“復(fù)用”指的是復(fù)用同一個(gè)線程。采用多路 I/O 復(fù)用技術(shù)可以讓單個(gè)線程高效的處理多個(gè)連接請求(盡量減少網(wǎng)絡(luò) IO 的時(shí)間消耗)掐场,且 Redis 在內(nèi)存中操作數(shù)據(jù)的速度非惩樱快,也就是說內(nèi)存內(nèi)的操作不會成為影響Redis性能的瓶頸熊户,主要由以上幾點(diǎn)造就了 Redis 具有很高的吞吐量萍膛。
因?yàn)镽edis是基于內(nèi)存的操作,CPU不是Redis的瓶頸嚷堡,Redis的瓶頸最有可能是機(jī)器內(nèi)存的大小或者網(wǎng)絡(luò)帶寬蝗罗。既然單線程容易實(shí)現(xiàn),而且CPU不會成為瓶頸蝌戒,那就順理成章地采用單線程的方案了(畢竟采用多線程會有很多麻煩4堋)。
MySQL索引失效的問題
1.索引不存儲null值
更準(zhǔn)確的說北苟,單列索引不存儲null值桩匪,復(fù)合索引不存儲全為null的值。索引不能存儲Null友鼻,所以對這列采用is null條件時(shí)傻昙,因?yàn)樗饕细緵]Null值,不能利用到索引彩扔,只能全表掃描妆档。
為什么索引列不能存Null值?
將索引列值進(jìn)行建樹虫碉,其中必然涉及到諸多的比較操作贾惦。Null值的特殊性就在于參與的運(yùn)算大多取值為null。
這樣的話蔗衡,null值實(shí)際上是不能參與進(jìn)建索引的過程纤虽。也就是說,null值不會像其他取值一樣出現(xiàn)在索引樹的葉子節(jié)點(diǎn)上绞惦。
2.不適合鍵值較少的列(重復(fù)數(shù)據(jù)較多的列)
假如索引列TYPE有5個(gè)鍵值逼纸,如果有1萬條數(shù)據(jù),那么 WHERE TYPE = 1將訪問表中的2000個(gè)數(shù)據(jù)塊济蝉。再加上訪問索引塊杰刽,一共要訪問大于200個(gè)的數(shù)據(jù)塊菠发。
如果全表掃描,假設(shè)10條數(shù)據(jù)一個(gè)數(shù)據(jù)塊贺嫂,那么只需訪問1000個(gè)數(shù)據(jù)塊滓鸠,既然全表掃描訪問的數(shù)據(jù)塊
少一些,肯定就不會利用索引了第喳。
3.前導(dǎo)模糊查詢不能利用索引(like '%XX'或者like '%XX%')
假如有這樣一列code的值為'AAA','AAB','BAA','BAB' ,如果where code like '%AB'條件糜俗,由于前面是模糊的,所以不能利用索引的順序曲饱,必須一個(gè)個(gè)去找悠抹,看是否滿足條件。這樣會導(dǎo)致全索引掃描或者全表掃描扩淀。如果是這樣的條件where code like 'A % '楔敌,就可以查找CODE中A開頭的CODE的位置,當(dāng)碰到B開頭的數(shù)據(jù)時(shí)驻谆,就可以停止查找了卵凑,因?yàn)楹竺娴臄?shù)據(jù)一定不滿足要求。這樣就可以利用索引了胜臊。
4.索引失效的幾種情況
- 如果條件中有or勺卢,即使其中有條件帶索引也不會使用(這也是為什么盡量少用or的原因)
要想使用or,又想讓索引生效区端,只能將or條件中的每個(gè)列都加上索引 - 對于多列索引值漫,不是使用的第一部分,則不會使用索引
- like查詢以%開頭
- 如果列類型是字符串织盼,那一定要在條件中將數(shù)據(jù)使用引號引用起來,否則不使用索引
- 如果mysql估計(jì)使用全表掃描要比使用索引快,則不使用索引
5.MySQL主要提供2種方式的索引:B-Tree索引杨何,Hash索引
B樹索引具有范圍查找和前綴查找的能力,對于有N節(jié)點(diǎn)的B樹沥邻,檢索一條記錄的復(fù)雜度為O(LogN)危虱。相當(dāng)于二分查找。
哈希索引只能做等于查找唐全,但是無論多大的Hash表埃跷,查找復(fù)雜度都是O(1)。
顯然邮利,如果值的差異性大弥雹,并且以等值查找(=、 <延届、>剪勿、in)為主,Hash索引是更高效的選擇方庭,它有O(1)的查找復(fù)雜度厕吉。
如果值的差異性相對較差酱固,并且以范圍查找為主,B樹是更好的選擇头朱,它支持范圍查找运悲。