1、性能監(jiān)控
開(kāi)啟消耗:set profiling = 1;
查詢消耗:show profiles;
查詢細(xì)節(jié):show profile for query {2};
查詢 CPU:show profile cpu;
查詢所有:show profile all;
profile statement 會(huì)被逐漸替代掉。目前推薦的是performance schema。它以比較低的級(jí)別來(lái)監(jiān)控?cái)?shù)據(jù)庫(kù)系統(tǒng),統(tǒng)計(jì)運(yùn)行過(guò)程中的資源消耗、資源等待等情況,所以會(huì)對(duì)數(shù)據(jù)庫(kù)性能降低比較小。
所有schema:show databases;
所有系統(tǒng)表:show databasetables;
默認(rèn)開(kāi)啟:show varibales like 'performance_schema'; 修改在配置文件中修改。
具有如下特點(diǎn):
1、提供了一種在數(shù)據(jù)庫(kù)運(yùn)行時(shí)實(shí)時(shí)檢查server的內(nèi)部執(zhí)行情況的方法。performance_schema數(shù)據(jù)庫(kù)中的表使用performance_schema存儲(chǔ)引擎。該數(shù)據(jù)庫(kù)關(guān)注數(shù)據(jù)庫(kù)運(yùn)行過(guò)程中的性能相關(guān)數(shù)據(jù)。與information_schema不同,infor 是關(guān)注server運(yùn)行過(guò)程中的元數(shù)據(jù)信息。
2、performance_schema通過(guò)監(jiān)視server的事件來(lái)監(jiān)控server內(nèi)部運(yùn)行情況,事件就是server內(nèi)部活動(dòng)中所做的任何事情以及對(duì)應(yīng)的時(shí)間消耗。
常用sql:
1.哪類SQL執(zhí)行最多 selectDIGEST_TEXT,COUNT_STAR,FIRST...
2.哪類SQL的平均響應(yīng)時(shí)間最多 SELECTDIGEST_TEXT,AVG_TIMER...
3.哪類SQL排序記錄數(shù)最多 selectdigest_text,sum_sort_rows...
4.哪類SQL掃描記錄數(shù)最多 selectdigest_text,sum_rows...
5.哪類SQL使用臨時(shí)表最多 selectdigest_text,sum_created...
顯示connection 狀態(tài)及數(shù)量:show processlist
2、schema與數(shù)據(jù)類型優(yōu)化
1、數(shù)據(jù)類型的優(yōu)化
更小的通常更好
盡量使用可以正確存儲(chǔ)的最小數(shù)據(jù)類型,更小的數(shù)據(jù)類型意味著更少的內(nèi)存消耗和緩存存儲(chǔ)。
簡(jiǎn)單就好、
簡(jiǎn)單類型的數(shù)據(jù)操作通常需要更少的CPU周期,例如整形比字符串帶價(jià)更低,因?yàn)樽址托?duì)規(guī)則是字符比較比整形更復(fù)雜。
盡量避免null
在數(shù)據(jù)庫(kù)中null 也具有存儲(chǔ)空間,并且?guī)в衝ull 的列回友更復(fù)雜的索引維護(hù),索引比較也會(huì)復(fù)雜。
實(shí)際細(xì)則。
2、合理的使用范式和反范式
范式的目的是:減小數(shù)據(jù)的冗余性、提高效率
第一范式,每一列不可分割、第二范式:屬性必須完全依賴主鍵、第三范式:所有的非主屬性不依賴于其他的非主屬性
3、主鍵的選擇
(針對(duì)InnoDB引擎)我們實(shí)際生產(chǎn)環(huán)境可能會(huì)使用四類屬性作為主鍵:
(1). 自增序列;
(2). UUID()函數(shù)生成的隨機(jī)值;
(3). 用戶注冊(cè)的唯一性帳號(hào)名稱,字符串類型,一般長(zhǎng)度為:40個(gè)字符;
(4). 基于一套機(jī)制生成類似自增的值,比如序列生成器;
那么我們接下來(lái),再分析下這四類屬性各自作為表主鍵的優(yōu)缺點(diǎn):
(1). 自增序列:從小到大 或從大到小的順序模式增加新值;數(shù)據(jù)類型也利于進(jìn)行主鍵值比較;存儲(chǔ)空間占用也相對(duì)最小,一般設(shè)置為:4個(gè)字節(jié)的INT類型或 8個(gè)字節(jié)的BIGINT類型;若是想進(jìn)行數(shù)據(jù)水平拆分的話,也可以借助設(shè)置mysqld實(shí)例的2個(gè)參數(shù):auto_increment_increment 和 auto_increment_offset;另外,唯一缺點(diǎn)就是自增序列是一個(gè)表級(jí)別的全局鎖,在5.0系列大規(guī)模并發(fā)寫(xiě)的時(shí)候,因鎖釋放機(jī)制的問(wèn)題容易出現(xiàn)瓶頸,但是5.1系列做了改進(jìn),基本上不存在此問(wèn)題;
(2). UUID()函數(shù):值為隨機(jī)性+固定部分,其值產(chǎn)生是無(wú)序的,且同一臺(tái)服務(wù)器上產(chǎn)生的值相同部分為77.8%;產(chǎn)生的值字符個(gè)數(shù)為36,按utf8編碼計(jì)算,占用的存儲(chǔ)空間為36個(gè)字節(jié);對(duì)于數(shù)據(jù)水平拆分支持,無(wú)需特殊設(shè)置;
(3). 使用用戶注冊(cè)的帳號(hào)名稱,字符串類型,其值的產(chǎn)生依賴用戶輸入,為此數(shù)據(jù)基本上為無(wú)序增加,字符串的長(zhǎng)度也是不定的,只能通過(guò)前段技術(shù)控制最短最大長(zhǎng)度值的限制,對(duì)水平拆分支持,無(wú)需做特殊設(shè)置;
(4). 序列生成器的架構(gòu),類似自增序列,不過(guò)需要借助額外的開(kāi)發(fā)工作量,以及提供一個(gè)第三方的服務(wù),可以規(guī)避自增序列的字增全局鎖的問(wèn)題,提高并發(fā),對(duì)數(shù)據(jù)水平拆分可以更好地支持;
(5). 雙主復(fù)制架構(gòu)的概率性碰到的場(chǎng)景:主服務(wù)器的數(shù)據(jù)執(zhí)行成功,而沒(méi)有復(fù)制到在線備用服務(wù)器時(shí),出問(wèn)題的概率確實(shí)存在,其他類型的做法,也必須人工干涉解決,都無(wú)簡(jiǎn)單且合理的自動(dòng)化辦法,以上四種辦法都無(wú)法規(guī)避;
通過(guò)四種屬性值作為主鍵的優(yōu)缺點(diǎn)分析,以及對(duì)比前面我們闡述的主鍵需要的優(yōu)秀素質(zhì),若是不考慮水平拆分的問(wèn)題,帶來(lái)額外設(shè)置上的麻煩,則自增序列是最佳的主鍵字段選擇;用戶的注冊(cè)帳號(hào)本身要求唯一性且非空的場(chǎng)景下,則可以作為主鍵字段的選擇;若是考慮水平拆分的問(wèn)題,則采用自增序列生成器的架構(gòu),非常易用和可靠的實(shí)現(xiàn)方式,產(chǎn)生的值是最佳主鍵字段的選擇;
4、字符集的選擇
對(duì)數(shù)據(jù)庫(kù)來(lái)說(shuō),字符集更加重要,因?yàn)閿?shù)據(jù)庫(kù)存儲(chǔ)的數(shù)據(jù)大部分都是各種文字,字符集對(duì)數(shù)據(jù)庫(kù)的存儲(chǔ),處理性能,以及日后系統(tǒng)的移植,推廣都會(huì)有影響。
MySQL5.6目前支持幾十種字符集,包括UCS-2,UTF-16,UTF-16LE,UTF-32,UTF-8和utf8mb4等Unicode字符集。
根據(jù)應(yīng)用的需求,考慮以下幾方面的因素。
滿足應(yīng)用支持語(yǔ)言的需求,如果應(yīng)用要處理各種各樣的文字,或者將發(fā)布到使用不同語(yǔ)言的國(guó)家或地區(qū),就應(yīng)該選擇Unicode字符集。對(duì)MySQL來(lái)說(shuō),目前就是UTF-8
如果應(yīng)用中涉及已有數(shù)據(jù)的導(dǎo)入,就要充分考慮數(shù)據(jù)庫(kù)字符集對(duì)已有數(shù)據(jù)的兼容性。假如已有數(shù)據(jù)是GBK文字,如果選擇GB2312-80為數(shù)據(jù)庫(kù)字符集,就很有可能出現(xiàn)某些文字無(wú)法正確導(dǎo)入的問(wèn)題
如果數(shù)據(jù)庫(kù)只支持一般中文,數(shù)據(jù)量很大,性能要求也很高,那就應(yīng)該選擇雙字節(jié)長(zhǎng)編碼的中文字符集,比如GBK。因?yàn)椋鄬?duì)于UTF-8而言,GBK比較“小”,每個(gè)漢字只占2個(gè)字節(jié),而UTF-8漢字編碼需要3個(gè)字節(jié),這樣可以減少磁盤(pán)I/O,數(shù)據(jù)庫(kù)Cache以及網(wǎng)絡(luò)傳輸?shù)臅r(shí)間,從而提高性能。相反,如果應(yīng)用主要處理英文字符,僅有少量漢字?jǐn)?shù)據(jù),那么選擇UTF-8更好,因?yàn)镚BK,UCS-2,UTF-16的西文字符編碼都是2個(gè)字節(jié),會(huì)造成很多不必要的開(kāi)銷。
如果數(shù)據(jù)庫(kù)需要做大量的字符運(yùn)算,如比較,排序等,那么選擇定長(zhǎng)字符集可能更好,因?yàn)槎ㄩL(zhǎng)字符集的處理速度要比變長(zhǎng)字符集的處理速度快。
如果所有客戶端程序都支持相同的字符集,則應(yīng)該優(yōu)先選擇該字符集作為數(shù)據(jù)庫(kù)字符集,這樣可以避免因字符集轉(zhuǎn)換帶來(lái)的性能開(kāi)銷和數(shù)據(jù)損失
5、存儲(chǔ)引擎的選擇
6、適當(dāng)?shù)臄?shù)據(jù)冗余
互聯(lián)網(wǎng)數(shù)據(jù)量很大的業(yè)務(wù)場(chǎng)景,往往數(shù)據(jù)庫(kù)需要進(jìn)行水平切分來(lái)降低單庫(kù)數(shù)據(jù)量。水平切分會(huì)有一個(gè)partition key,通過(guò)partition key的查詢能夠直接定位到庫(kù),但是非partition key上的查詢可能就需要掃描多個(gè)庫(kù)了。
此時(shí)常見(jiàn)的架構(gòu)設(shè)計(jì)方案,是使用數(shù)據(jù)冗余這種反范式設(shè)計(jì)來(lái)滿足分庫(kù)后不同維度的查詢需求。
常見(jiàn)方案有:服務(wù)同步雙寫(xiě)、服務(wù)異步雙寫(xiě)、線下異步雙寫(xiě)。
7、適當(dāng)?shù)牟鸱?/b>
當(dāng)數(shù)據(jù)庫(kù)比較龐大,讀寫(xiě)操作特別是寫(xiě)入操作過(guò)于頻繁,很難由一臺(tái)服務(wù)器支撐的時(shí)候,我們就要考慮進(jìn)行數(shù)據(jù)庫(kù)的切分。所謂數(shù)據(jù)庫(kù)的切分,就是我們按照某些特定的條件,將一臺(tái)數(shù)據(jù)庫(kù)上的數(shù)據(jù)分散到多臺(tái)數(shù)據(jù)庫(kù)服務(wù)器上。因?yàn)槭褂枚嗯_(tái)服務(wù)器,所以當(dāng)一臺(tái)服務(wù)器宕機(jī)后,整個(gè)系統(tǒng)只有部分?jǐn)?shù)據(jù)不可用,而不是全部不可用。因此,數(shù)據(jù)庫(kù)切分不僅能夠用多臺(tái)服務(wù)器分擔(dān)數(shù)據(jù)庫(kù)的負(fù)載壓力,還可以提高系統(tǒng)的總體可用性。
1、垂直切分
垂直切分就是按照系統(tǒng)功能模塊,將每個(gè)模塊訪問(wèn)的數(shù)據(jù)表切分到不同的數(shù)據(jù)庫(kù)中。
2、水平切分
水平切分就是對(duì)數(shù)據(jù)量超大的數(shù)據(jù)表,按照其中數(shù)據(jù)的邏輯關(guān)系,根據(jù)某個(gè)字段的某種規(guī)則,將其中的數(shù)據(jù)切分到多個(gè)數(shù)據(jù)庫(kù)上。
3、整合切分結(jié)果
數(shù)據(jù)在經(jīng)過(guò)垂直和水平切分被存放在不同的數(shù)據(jù)庫(kù)服務(wù)器上之后,系統(tǒng)面臨的最大問(wèn)題就是如何來(lái)讓這些來(lái)自不同數(shù)據(jù)庫(kù)服務(wù)器上的數(shù)據(jù)得到較好的整合。解決這個(gè)問(wèn)題有兩種方式:
第一種:在系統(tǒng)的每個(gè)模塊中配置管理該模塊需要的一個(gè)或者幾個(gè)數(shù)據(jù)庫(kù)及其所在服務(wù)器的信息,數(shù)據(jù)在模塊中進(jìn)行整合;
第二種:通過(guò)中間代理層來(lái)統(tǒng)一管理所有的數(shù)據(jù)源,數(shù)據(jù)庫(kù)集群對(duì)系統(tǒng)應(yīng)用透明。
3、執(zhí)行計(jì)劃閱讀
1、mysql整個(gè)查詢的過(guò)程:
? 客戶端向 MySQL 服務(wù)器發(fā)送一條查詢請(qǐng)求
? 服務(wù)器首先檢查查詢緩存,如果命中緩存,則立刻返回存儲(chǔ)在緩存中的結(jié)果。否則進(jìn)入下一階段
? 服務(wù)器進(jìn)行 SQL 解析、預(yù)處理、再由優(yōu)化器生成對(duì)應(yīng)的執(zhí)行計(jì)劃
? MySQL 根據(jù)執(zhí)行計(jì)劃,調(diào)用存儲(chǔ)引擎的 API 來(lái)執(zhí)行查詢
? 將結(jié)果返回給客戶端,同時(shí)緩存查詢結(jié)果
注意:只有在8.0之前才有查詢緩存,8.0之后查詢緩存被去掉了
2、如何開(kāi)啟執(zhí)行計(jì)劃
explain?select?投影列?FROM?表名?WHERE?條件
3、分析示例
EXPLAIN?select?*?from?actor;
4、explain的 兩個(gè)變種
explain extended:會(huì)在 explain 的基礎(chǔ)上額外提供一些查詢優(yōu)化的信息。緊隨其后通過(guò) show warnings 命令可以得到優(yōu)化后的查詢語(yǔ)句,從而看出優(yōu)化器優(yōu)化了什么。額外還有 filtered 列,是一個(gè)半分比的值,rows *filtered/100 可以估算出將要和 explain 中前一個(gè)表進(jìn)行連接的行數(shù)(前一個(gè)表指 explain 中的id值比當(dāng)前表id值小的表)。
EXPLAIN?EXTENDED?select?*?from?actor?where?id=1;show?WARNINGS;
explain partitions:相比 explain 多了個(gè) partitions 字段,如果查詢是基于分區(qū)表的話,會(huì)顯示查詢將訪問(wèn)的分區(qū)。
5、explain中的列
id:
id 值相同時(shí)表示從上向下執(zhí)行,id 值相同被視為一組,如果是子查詢,id 值會(huì)遞增,id 值越高,優(yōu)先級(jí)越高,id為NULL最后執(zhí)行。
select_type:
simple:表示查詢中不包含子查詢或者 union
primary:當(dāng)查詢中包含任何復(fù)雜的子部分,最外層的查詢被標(biāo)記成 primary
derived:在 from 的列表中包含的子查詢被標(biāo)記成 derived
subquery:在 select 或 where 列表中包含了子查詢,則子查詢被標(biāo)記成 subquery
union:兩個(gè) select 查詢時(shí)前一個(gè)標(biāo)記為 PRIMARY,后一個(gè)標(biāo)記為 UNION。union 出現(xiàn)在 from 從句子查詢中,外層 select 標(biāo)記為 PIRMARY,union 中第一個(gè)查詢?yōu)?DERIVED,第二個(gè)子查詢標(biāo)記為 UNION
unionresult:從 union 表獲取結(jié)果的 select 被標(biāo)記成 union result。
table:
顯示這一行的數(shù)據(jù)是關(guān)于哪張表的。
當(dāng) from 子句中有子查詢時(shí),table列是 格式,表示當(dāng)前查詢依賴 id=N 的查詢,于是先執(zhí)行 id=N 的查詢。
當(dāng)有 union 時(shí),UNION RESULT 的 table 列的值為<union1,2>,1和2表示參與 union 的 select 行id。
type:
這是重要的列,顯示連接使用了何種類型。從最好到最差的連接類型為 system > const > eq_reg > ref > range > index > ALL。一般來(lái)說(shuō),得保證查詢達(dá)到range級(jí)別,最好達(dá)到ref。
NULL:mysql能夠在優(yōu)化階段分解查詢語(yǔ)句,在執(zhí)行階段用不著再訪問(wèn)表或索引。例如:在索引列中選取最小值,可以單獨(dú)查找索引來(lái)完成,不需要在執(zhí)行時(shí)訪問(wèn)表
explain select min(id) from film;
system:表中只有一行數(shù)據(jù)。屬于 const 的特例。如果物理表中就一行數(shù)據(jù)為 ALL
const :查詢結(jié)果最多有一個(gè)匹配行。因?yàn)橹挥幸恍校钥梢员灰暈槌A俊onst 查詢速度非常快,因?yàn)橹蛔x一次。一般情況下把主鍵或唯一索引作為唯一條件的查詢都是 const
explain select * from (select * from film where id = 1) tmp;
eq_ref:查詢時(shí)查詢外鍵表全部數(shù)據(jù)。且只能查詢主鍵列或關(guān)聯(lián)列。且外鍵表中外鍵列中數(shù)據(jù)不能有重復(fù)數(shù)據(jù),且這些數(shù)據(jù)都必須在主鍵表中有對(duì)應(yīng)數(shù)據(jù)(主鍵表中數(shù)據(jù)可以有沒(méi)有用到的)
explain select * from film_actor left join film on film_actor.film_id = film.id
ref:比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前綴,索引要和某個(gè)值相比較,可能會(huì)找到多個(gè)符合條件的行。
range:把這個(gè)列當(dāng)作條件只檢索其中一個(gè)范圍。常見(jiàn) where 從句中出現(xiàn) between、<、>、>=、in 等。主要應(yīng)用在具有索引的列中
explain select * from actor where id > 1;
index:掃描全索引就能拿到結(jié)果,一般是掃描某個(gè)二級(jí)索引,這種掃描不會(huì)從索引樹(shù)根節(jié)點(diǎn)開(kāi)始快速查找,而是直接對(duì)二級(jí)索引的葉子節(jié)點(diǎn)遍歷和掃描,速度還是比較慢的,這種查詢一般為使用覆蓋索引,二級(jí)索引一般比較小,所以這種通常比ALL快一些
explain select * from film;
ALL:即全表掃描,掃描你的聚簇索引的所有葉子節(jié)點(diǎn)。通常情況下這需要增加索引來(lái)進(jìn)行優(yōu)化了。
explain select * from actor;
possible_keys:
查詢條件字段涉及到的索引,可能沒(méi)有使用。
explain 時(shí)可能出現(xiàn) possible_keys 有列,而 key 顯示 NULL 的情況,這種情況是因?yàn)楸碇袛?shù)據(jù)不多,mysql認(rèn)為索引對(duì)此查詢幫助不大,選擇了全表查詢。
如果該列是NULL,則沒(méi)有相關(guān)的索引。在這種情況下,可以通過(guò)檢查 where 子句看是否可以創(chuàng)造一個(gè)適當(dāng)?shù)乃饕齺?lái)提高查詢性能,然后用 explain 查看效果
key:
實(shí)際使用的索引。如果為 NULL,則沒(méi)有使用索引。
如果想強(qiáng)制mysql使用或忽視possible_keys列中的索引,在查詢中使用 forceindex、ignore index。
key_len:
表示索引中使用的字節(jié)數(shù),查詢中使用的索引的長(zhǎng)度(最大可能長(zhǎng)度),并非實(shí)際使用長(zhǎng)度,理論上長(zhǎng)度越短越好。key_len 是根據(jù)表定義計(jì)算而得的,不是通過(guò)表內(nèi)檢索出的。
ref
顯示索引的哪一列被使用了,如果可能的話,是一個(gè)常量 const。
rows
根據(jù)表統(tǒng)計(jì)信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數(shù)。注意這個(gè)不是結(jié)果集里的行數(shù)。
fitered
顯示了通過(guò)條件過(guò)濾出的行數(shù)的百分比估計(jì)值。
Extra :?MYSQL 如何解析查詢的額外信息。
1、Distinct:MySQL 發(fā)現(xiàn)第 1 個(gè)匹配行后,停止為當(dāng)前的行組合搜索更多的行。
2、Not exists:MySQL 能夠?qū)Σ樵冞M(jìn)行 LEFT JOIN 優(yōu)化,發(fā)現(xiàn) 1 個(gè)匹配 LEFT JOIN 標(biāo)準(zhǔn)的行后,不再為前面的的行組合在該表內(nèi)檢查更多的行。
3、range checked for each record (index map: #):MySQL 沒(méi)有發(fā)現(xiàn)好的可以使用的索引,但發(fā)現(xiàn)如果來(lái)自前面的表的列值已知,可能部分索引可以使用。
4、Using filesort:MySQL 需要額外的一次傳遞,以找出如何按排序順序檢索行。將用外部排序而不是索引排序,數(shù)據(jù)較小時(shí)從內(nèi)存排序,否則需要在磁盤(pán)完成排序。這種情況下一般也是要考慮使用索引來(lái)優(yōu)化的。
4.1. actor.name未創(chuàng)建索引,會(huì)瀏覽actor整個(gè)表,保存排序關(guān)鍵字name和對(duì)應(yīng)的id,然后排序name并檢索行記錄
4.2. film.name建立了idx_name索引,此時(shí)查詢時(shí)extra是using index
explain select * from film order by name;
5、Using index:從只使用索引樹(shù)中的信息而不需要進(jìn)一步搜索讀取實(shí)際的行來(lái)檢索表中的列信息。(使用覆蓋索引)
覆蓋索引定義:mysql執(zhí)行計(jì)劃explain結(jié)果里的key有使用索引,如果select后面查詢的字段都可以從這個(gè)索引的樹(shù)中獲取,這種情況一般可以說(shuō)是用到了覆蓋索引,extra里一般都有using index;覆蓋索引一般針對(duì)的是輔助索引,整個(gè)查詢結(jié)果只通過(guò)輔助索引就能拿到結(jié)果,不需要通過(guò)輔助索引樹(shù)找到主鍵,再通過(guò)主鍵去主鍵索引樹(shù)里獲取其它字段值
explain select film_id from film_actor where film_id = 1;
6、Using temporary:為了解決查詢,MySQL 需要?jiǎng)?chuàng)建一個(gè)臨時(shí)表來(lái)容納結(jié)果。
6.1. actor.name沒(méi)有索引,此時(shí)創(chuàng)建了張臨時(shí)表來(lái)distinct
explain select distinct name from actor;
6.2. film.name建立了idx_name索引,此時(shí)查詢時(shí)extra是using index,沒(méi)有用臨時(shí)表
explain select distinct name from film;
7、Using where:WHERE 子句用于限制哪一個(gè)行匹配下一個(gè)表或發(fā)送到客戶,并且查詢的列未被索引覆蓋
explain select * from actor where name = 'a';
8、Using sort_union(…), Using union(…), Using intersect(…): 這 些 函 數(shù) 說(shuō) 明 如 何 為index_merge 聯(lián)接類型合并索引掃描。
9、Using index for group-by:類似于訪問(wèn)表的 Using index 方式,Using index for group-by 表示MySQL發(fā)現(xiàn)了一個(gè)索引,可以用來(lái)查 詢GROUP BY或DISTINCT查詢的所有列,而不要額外搜索硬盤(pán)訪問(wèn)實(shí)際的表。
4、索引
什么是索引?
索引是幫助Mysql高效獲取數(shù)據(jù)的排序的數(shù)據(jù)結(jié)構(gòu)。
索引數(shù)據(jù)結(jié)構(gòu):
二叉樹(shù)、紅黑樹(shù)、Hash表、B-Tree
如果不存在索引,當(dāng)我們需要查找數(shù)據(jù)時(shí),就需要按照順序逐個(gè)比較,而每次讀取數(shù)據(jù)都是一次磁盤(pán)的IO,這無(wú)疑將會(huì)帶來(lái)極大的性能問(wèn)題尤其是數(shù)據(jù)量龐大時(shí),但是如果通過(guò)二叉樹(shù)對(duì)原有數(shù)據(jù)進(jìn)行存儲(chǔ),當(dāng)查詢時(shí),我們只需要按照樹(shù)的查詢次數(shù)就可以獲取到對(duì)應(yīng)的值,從而完成搜索。
二叉樹(shù)練習(xí):https://www.cs.usfca.edu/~galles/visualization/BST.html
所有數(shù)據(jù)結(jié)構(gòu):https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
二叉樹(shù)在單邊增長(zhǎng)情況下和鏈表沒(méi)有區(qū)別,二叉平衡樹(shù)仍然存在這個(gè)單邊增長(zhǎng)的問(wèn)題,樹(shù)的高度仍然很高。
可以在節(jié)點(diǎn)上增加索引來(lái)降低樹(shù)的高度,這便是B樹(shù)結(jié)構(gòu):
B+-Tree具有如下特點(diǎn):葉節(jié)點(diǎn)具有相同深度,葉節(jié)點(diǎn)的指針為空、葉節(jié)點(diǎn)中數(shù)據(jù)索引從左到右遞增
B-Tree查找數(shù)據(jù)的順序是,從根節(jié)點(diǎn)開(kāi)始每次load所有元素(一頁(yè)數(shù)據(jù))然后在RAM中經(jīng)過(guò)二分算法(上圖的空白處對(duì)應(yīng)下個(gè)元素的文件位置)進(jìn)行快速比對(duì)。以此類推逐級(jí)向下。
show global status like 'innodb_page_size';可以查詢一頁(yè)數(shù)據(jù)的的大小(default 16KB)。
數(shù)據(jù)庫(kù)常見(jiàn)存儲(chǔ)引擎:InnoDB MyISAM。存儲(chǔ)引擎是用來(lái)修飾表的。
myisam:
數(shù)據(jù)庫(kù)的數(shù)據(jù)是存儲(chǔ)在mysql安裝目錄下的data目錄。.frm、結(jié)構(gòu)文件.MYD數(shù)據(jù)文件、.MYI index文件。
innodb:
相同的目錄但是文件格式不一樣:.frm、結(jié)構(gòu)文件 .ibd、數(shù)據(jù)和索引都存在里面。
InnoDB索引實(shí)現(xiàn)(聚集)
表數(shù)據(jù)文件本身就是按照B-Tree組織的一個(gè)索引結(jié)構(gòu)文件、聚集索引-葉子節(jié)點(diǎn)包含了完整的數(shù)據(jù)結(jié)構(gòu)。
兩者區(qū)別就是葉子節(jié)點(diǎn)存儲(chǔ)的不一樣,一個(gè)是文件的位置,一個(gè)是文件本身。
但是無(wú)論哪種存儲(chǔ)引擎背后都是B+ Tree結(jié)構(gòu)。
B+ Tree (B樹(shù)的變種,增強(qiáng)版):
非葉子節(jié)點(diǎn)不存儲(chǔ)data,只存儲(chǔ)索引,可以放更多的索引,樹(shù)的高度大大降低。
葉子節(jié)點(diǎn)包含所有的索引字段。
葉子節(jié)點(diǎn)相鄰節(jié)點(diǎn)具有相互指針存儲(chǔ)相鄰元素的位置(這也是它能支持范圍查找的根本所在)。
為什么建議InnoDB表必須建主鍵,并且推薦使用整形的自增主鍵?
如果不建主鍵,聚集索引會(huì)在表中找一列不會(huì)重復(fù)的數(shù)據(jù),如果沒(méi)有找到會(huì)建造隱藏?cái)?shù)據(jù),來(lái)構(gòu)造數(shù)據(jù)結(jié)構(gòu)。自增整形數(shù)據(jù)結(jié)構(gòu),比較快,存儲(chǔ)小(SSD還是很貴的,更小的空間成本更低)。如果不是自增元素,每次插入元素則需要從新維護(hù)索引結(jié)構(gòu)。(既要分裂又要平衡樹(shù)結(jié)構(gòu),極其影響寫(xiě)入性能)。
為什么非主鍵索引結(jié)構(gòu)葉子節(jié)點(diǎn)存儲(chǔ)的是主鍵值?
1. 保持一致性:當(dāng)數(shù)據(jù)庫(kù)表進(jìn)行DML操作時(shí),同一行記錄的頁(yè)地址會(huì)發(fā)生改變,因非主鍵索引保存的是主鍵的值,無(wú)需進(jìn)行更改。
2. 節(jié)省存儲(chǔ)空間:Innodb數(shù)據(jù)本身就已經(jīng)匯聚到主鍵索引所在的B+樹(shù)上了, 如果普通索引還繼續(xù)再保存一份數(shù)據(jù),就會(huì)導(dǎo)致有多少索引就要存多少份數(shù)據(jù)。
Hash 索引:
對(duì)索引的key進(jìn)行一次hash計(jì)算就可以定位出數(shù)據(jù)儲(chǔ)存的位置、很多時(shí)候hash索引要比B+樹(shù)更高效、僅能滿足=,in、不支持范圍查詢。存在hash 沖突問(wèn)題。
索引最左前綴原則:
聯(lián)合主鍵結(jié)構(gòu):
從左到右按照聯(lián)合主鍵的字段進(jìn)行排序。
select * from student where name = 'zhangsan' and age=30; 命中索引。
select * from student where age=30 and position = 'dev';不會(huì)命中索引。
如果不從第一個(gè)元素開(kāi)始,索引結(jié)構(gòu)是不具備有序這一特點(diǎn)的。
mysql explain 最佳實(shí)踐:
MVCC(可重復(fù)讀如何實(shí)現(xiàn)?)
在read commited、repeated committed這兩種事務(wù)隔離等級(jí)下執(zhí)行普通的select操作時(shí)訪問(wèn)記錄的版本鏈過(guò)程,使得不同事務(wù)的讀寫(xiě) 、寫(xiě)瀆操作并發(fā)執(zhí)行。
Buffer Pool(load 的數(shù)據(jù)存放的位置):
5、優(yōu)化案例:
1、單表聯(lián)合索引失效
我們預(yù)先在table test 上的column1、column2、column3建立聯(lián)合索引(idx_column1_column1_column3)。然后執(zhí)行如下query:
select id,author_id from test? where?column1 =1 and?column2>2 order by?column3;
select id,author_id from?test? where?column1 =1 and?column2=2 order by?column3;
結(jié)果是第二個(gè)命中索引但是第一個(gè)不會(huì)。這是因?yàn)榘凑誃+Tree索引的工作原理,先按照c1排序,如果遇到相同的c1再按照c2排序,如果相同則繼續(xù),當(dāng)中間字段是條件范圍時(shí),對(duì)于第三個(gè)列無(wú)法按照索引結(jié)構(gòu)掃描從而只能全表掃描。
解決方案,移除需要排序的列在聯(lián)合索引中建立新的索引(idx_column1_column3)。
1、根據(jù)sql實(shí)際解析的順序,調(diào)整聯(lián)合索引的順序。
2、將查詢列放置于聯(lián)合索引可以提升速度,因?yàn)楸苊獾娜?shù)據(jù)庫(kù)。
3、清除廢棄索引,方式干擾。
4、最左前綴原則
5、將含有in 的范圍查詢放置于條件最后,避免in提前失效
2、雙表索引失效
假設(shè)存在 table test1、test2,使用字段c1關(guān)聯(lián)。
select * from test1 join test2 on test1.c1 = test2.c1;
在左表和右表建立index后結(jié)果不同,這是由左連接特性決定的,left join 條件用于確定如何從右表搜索行,左邊一定會(huì)有。所以右邊是關(guān)鍵點(diǎn),一定要有索引,這就是小表驅(qū)動(dòng)大表。
最佳實(shí)踐:
1、全值匹配最合適
2、最佳左前綴原則、存儲(chǔ)引擎不能使用索引中范圍條件右邊的列
3、不在索引上做什么操作,否則會(huì)導(dǎo)致全表掃描,函數(shù)或者計(jì)算
5、盡量使用覆蓋索引,只訪問(wèn)索引的查詢,減少select *
6、mysql 在使用!=的時(shí)候無(wú)法使用索引,is null is not null
7、like百分號(hào) 加右邊,mysql會(huì)引起全表掃描
8、使用 or引起索引失效