MySql優(yōu)化(持續(xù)更新)

很多知識點(diǎn)出自《高性能mysql》

B樹與B+樹

在磁盤設(shè)計(jì)中每經(jīng)過一個(gè)節(jié)點(diǎn)就是一次io操作蛤奥,io操作是會影響性能的箭养,所以磁盤存儲的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)最終的目的就是減少io

B樹又叫做多路平衡樹,如果一個(gè)節(jié)點(diǎn)能存放很多個(gè)數(shù)據(jù)的話涎跨,樹的高度就會相應(yīng)的減少得湘,節(jié)點(diǎn)就會減少募狂,每個(gè)節(jié)點(diǎn)都當(dāng)成一個(gè)磁盤塊畜份,訪問的節(jié)點(diǎn)減少了也就減少了磁盤的io操作诞帐。B樹相對B+樹而言就是不夠矮胖,就是說B樹設(shè)計(jì)比B+樹io次數(shù)更多
B樹

B+樹是B-樹的加強(qiáng)版爆雹,B+樹的枝節(jié)點(diǎn)不存儲行數(shù)據(jù)停蕉,只是存儲主鍵的索引和引用,葉子節(jié)點(diǎn)才是真正的保存數(shù)據(jù)的钙态,所以枝節(jié)點(diǎn)能存放更多的索引慧起,節(jié)點(diǎn)也會相應(yīng)的減少,樹的高度也隨之變低册倒,磁盤io讀寫也就進(jìn)一步減少蚓挤,并且葉子節(jié)點(diǎn)的數(shù)據(jù)都是順序的,葉子節(jié)點(diǎn)之間使用雙向指針連接,最底層的葉子節(jié)點(diǎn)形成了一個(gè)雙向有序鏈表驻子,所以磁盤由隨機(jī)讀取變?yōu)榱隧樞蜃x取灿意,提升了磁盤的性能,所以B+樹作為innodb的磁盤存儲結(jié)構(gòu)比B-樹的讀寫效率更高崇呵,查詢效率更好缤剧。

B+樹的工作原理是根據(jù)索引找索引數(shù)據(jù)所在的頁,這個(gè)頁是葉子節(jié)點(diǎn)域慷,然后將這個(gè)頁讀到內(nèi)存中荒辕,因?yàn)槊總€(gè)節(jié)點(diǎn)內(nèi)的數(shù)據(jù)都是順序排序汗销,所以mysql就會使用二分法對這個(gè)內(nèi)存中的頁進(jìn)行查找。
B+樹

B+樹的優(yōu)勢:

一般來說B+Tree比BTree更適合實(shí)現(xiàn)外存的索引結(jié)構(gòu)抵窒,操作系統(tǒng)以頁(page)為單位管理內(nèi)存弛针,一頁(page)通常默認(rèn)為4K,數(shù)據(jù)庫的頁通常設(shè)置為操作系統(tǒng)頁的整數(shù)倍估脆,在數(shù)據(jù)庫中一頁默認(rèn)為16k钦奋。索引結(jié)構(gòu)的節(jié)點(diǎn)被設(shè)計(jì)為一個(gè)頁的大小,然后利用外存的“預(yù)讀取”原則疙赠,每次讀取的時(shí)候付材,把整個(gè)節(jié)點(diǎn)的數(shù)據(jù)讀取到內(nèi)存中,然后在內(nèi)存中查找圃阳,那么提升查找速度的關(guān)鍵就在于盡可能少的磁盤I/O厌衔,那么可以知道,每個(gè)節(jié)點(diǎn)中能存放的key個(gè)數(shù)越多捍岳,那么樹的高度越小富寿,需要I/O的次數(shù)越少,因此一般來說B+Tree比BTree更快锣夹,因?yàn)锽+Tree的非葉節(jié)點(diǎn)中不存儲data页徐,就可以存儲更多的索引鍵。

MySql的索引

MySQL中最常見的兩種存儲引擎分別是MyISAM和InnoDB银萍,分別實(shí)現(xiàn)了非聚簇索引和聚簇索引

  • 聚簇索引:聚簇索引的存儲了表中所有的主鍵和行數(shù)據(jù),聚集索引查詢效率更高变勇,但是寫入性能差,因?yàn)閷懭胄聰?shù)據(jù)需要調(diào)整索引的位置
  • 非聚簇索引:數(shù)據(jù)存儲和索引分開贴唇,葉子節(jié)點(diǎn)存儲對應(yīng)的行搀绣,需要二次查找,通常稱為[二級索引]或[輔助索引]

InnoDB——聚簇索引

  • 索引值不能重復(fù)戳气,不能為空链患。InnoDB是以主鍵為基礎(chǔ)的,如果我們沒有指定主鍵瓶您,InnoDB會創(chuàng)建一個(gè)隱式的主鍵
  • 聚簇索引的主索引的葉子結(jié)點(diǎn)存儲的是鍵值對應(yīng)的行數(shù)據(jù)和主鍵鍵值麻捻,輔助索引的葉子結(jié)點(diǎn)存儲的是鍵值對應(yīng)的數(shù)據(jù)的主鍵鍵值和對應(yīng)的主索引的鍵值。而枝節(jié)點(diǎn)只存放主鍵鍵值和引用因此主鍵的值長度越小越好呀袱,類型越簡單越好芯肤,這樣樹的一個(gè)枝節(jié)點(diǎn)能存放的數(shù)據(jù)量就越多。
  • 聚簇索引的數(shù)據(jù)是根據(jù)主鍵的順序保存压鉴,可以有更少的磁盤I/O崖咨,加快查詢速度。但是也是因?yàn)檫@個(gè)原因油吭,聚簇索引的插入順序最好按照主鍵單調(diào)的順序插入击蹲,否則會頻繁的引起頁分裂署拟,嚴(yán)重影響性能。
  • 使用主索引的時(shí)候歌豺,盡量使用聚簇索引推穷,因?yàn)榫鄞厮饕恍枰檎乙淮危蔷鄞厮饕诓榈綌?shù)據(jù)的地址后类咧,還要進(jìn)行一次I/O查找數(shù)據(jù)
  • 聚簇索引在插入新數(shù)據(jù)的時(shí)候比非聚簇索引慢很多馒铃,因?yàn)椴迦胄聰?shù)據(jù)時(shí)需要檢測主鍵是否重復(fù),這需要遍歷主索引的所有葉節(jié)點(diǎn)痕惋,而非聚簇索引的葉節(jié)點(diǎn)保存的是數(shù)據(jù)地址区宇,占用空間少,因此分布集中值戳,查詢的時(shí)候I/O更少议谷,但聚簇索引的主索引中存儲的是數(shù)據(jù)本身,數(shù)據(jù)占用空間大堕虹,分布范圍更大卧晓,可能占用好多的扇區(qū),因此需要更多次I/O才能遍歷完畢
聚簇索引B+樹
聚簇索引B+樹和對應(yīng)的輔助索引

MyISAM——非聚簇索引(輔助索引)

  • MyISAM存儲引擎采用的是非聚簇索引赴捞,主索引不允許重復(fù)逼裆,不允許空值,葉子結(jié)點(diǎn)的key都存儲指向鍵值對應(yīng)的數(shù)據(jù)的物理地址赦政。
  • 非聚簇索引的數(shù)據(jù)表和索引表是分開存儲的胜宇。
  • 非聚簇索引中的數(shù)據(jù)是根據(jù)數(shù)據(jù)的插入順序保存。因此非聚簇索引更適合單個(gè)數(shù)據(jù)的查詢昼钻。插入順序不受鍵值影響。
  • 只有在MyISAM中才能使用FULLTEXT索引封寞。(mysql5.6以后innoDB也支持全文索引)
    非聚簇索引

InnoDB和MyISAM圖例
InnoDB和MyISAM

聯(lián)合索引的B+樹

也是一個(gè)B+樹然评,只是索引由單個(gè)變成了多個(gè),如下圖狈究,我們看到聯(lián)合索引的鍵是按照左邊的數(shù)字進(jìn)行節(jié)點(diǎn)的放置的碗淌,但是無論是最左邊的還是最右邊的每個(gè)鍵都進(jìn)行了排序,所以要使用索引必須要符合最左原則才能符合聯(lián)合索引的數(shù)據(jù)結(jié)構(gòu)抖锥,走了索引查出來的所有的鍵都是排好序了
聯(lián)合索引的B+樹

覆蓋索引:

SQL只需要通過索引就可以返回查詢所需要的數(shù)據(jù)亿眠,而不必通過二級索引查到主鍵之后再去查詢數(shù)據(jù)。下圖中由于索引中包含要查的數(shù)據(jù)磅废,由于輔助索引葉子節(jié)點(diǎn)存放的是主索引和索引字段值纳像,所以在聯(lián)合的輔助索引中查詢數(shù)據(jù)后就不用在去查主索引了
創(chuàng)建聯(lián)合索引
這個(gè)查詢使用了覆蓋索引

如果把上述查詢東西改為select *就不走覆蓋索引了,而先走輔助索引然后利用輔助索引查出來的數(shù)據(jù)去查主索引拯勉,因?yàn)橹魉饕鎯α藢?yīng)表的所有行數(shù)據(jù)

Hash表索引

Hash表竟趾,在Java中的HashMap憔购,TreeMap就是Hash表結(jié)構(gòu),以鍵值對的方式存儲數(shù)據(jù)岔帽。我們使用Hash表存儲表數(shù)據(jù)Key可以存儲索引列玫鸟,Value可以存儲行記錄或者行磁盤地址。Hash表在等值查詢時(shí)效率很高犀勒,時(shí)間復(fù)雜度為O(1)屎飘;但是不支持范圍快速查找,范圍查找時(shí)還是只能通過掃描全表方式贾费。

全文索引

只能在文本類型CHAR,VARCHAR,TEXT類型字段上創(chuàng)建全文索引钦购。字段長度比較大時(shí),如果創(chuàng)建普通索引铸本,在進(jìn)行l(wèi)ike ‘%xxxxx%’,'%xxxxx'模糊查詢時(shí)效率比較低肮雨,這時(shí)可以創(chuàng)建全文索引。 MyISAM和InnoDB中都可以使用全文索引箱玷。

唯一索引

索引列中的值必須是唯一的怨规,但是允許為空值。

1锡足、主鍵索引:即主索引似将,根據(jù)主鍵pk_clolum(length)建立索引,不允許重復(fù)笋妥,不允許空值伴嗡;
ALTER TABLE 'table_name' ADD PRIMARY KEY pk_index('col');

2沐批、唯一索引:用來建立索引的列的值必須是唯一的纫骑,允許空值
ALTER TABLE 'table_name' ADD UNIQUE index_name('col');

3九孩、普通索引:用表中的普通列構(gòu)建的索引先馆,沒有任何限制
ALTER TABLE 'table_name' ADD INDEX index_name('col');

4躺彬、全文索引:用大文本對象的列構(gòu)建的索引(下一部分會講解)
ALTER TABLE 'table_name' ADD FULLTEXT INDEX ft_index('col')煤墙;

5、組合索引:用多個(gè)列組合構(gòu)建的索引宪拥,這多個(gè)列中的值不允許有空值
ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3')仿野;

使用索引的一些規(guī)則:
使用所有的 一些規(guī)則

選用主索引時(shí)最好選擇散列性好,字段長度小她君,并且有順序性遞增的字段脚作。散列性差的字段主主索引時(shí)會造成數(shù)據(jù)庫無法通過一次索引找到對應(yīng)的行數(shù)據(jù),因?yàn)橹魉饕嬖诖罅恐貜?fù)值缔刹,這時(shí)數(shù)據(jù)庫必須對行數(shù)據(jù)再進(jìn)行一次掃描鳖枕,另外有可能造成頁分裂魄梯。無序性的字段作為主索引會造成多次的頁分裂。字段長度大的數(shù)據(jù)在支節(jié)點(diǎn)占用太多空間宾符,這樣會造成支支節(jié)點(diǎn)過多酿秸,使io的次數(shù)變多

慢查詢優(yōu)化

慢查詢?nèi)罩?/h5>

通過手動開啟慢查詢輸出日志可以查看有問題的SQL

  • 設(shè)置開啟慢查詢?nèi)罩镜妮敵?/strong>
    set global slow_query_log=NO
  • 設(shè)置慢查詢?nèi)罩据敵龅奈恢?/strong>
    set global slow_query_log_file=/xxxxx/xxxxx.log
  • 設(shè)置記錄的慢查詢是執(zhí)行時(shí)間超過多少秒的SQL,這里單位是秒
    set global slow_query_time=xxxxx.xxx
  • 開啟記錄沒有使用索引的SQL
    set global slow_queries_not_using_indexes=NO
  • 使用mysql自帶的慢查詢分析工具魏烫,分析慢查詢?nèi)罩?/strong>
    mysqldumpslow /xxxx/xxxxx.log

實(shí)際操作

查看并設(shè)置慢查詢?nèi)罩纠彼眨@里我們看到Mysql慢查詢?nèi)罩灸J(rèn)是10秒,我這里為了方便哄褒,利用語句設(shè)置為了0稀蟋,用來抓取所有的SQL

查看慢查詢?nèi)罩驹O(shè)置,查看慢查詢?nèi)罩镜妮敵鑫恢媚派模O(shè)置啟動慢查詢?nèi)罩就丝停@里默認(rèn)是關(guān)閉的

執(zhí)行了幾條sql后,我們?nèi)タ纯绰樵內(nèi)罩局械挠涗?div id="n27mczl" class="image-package">

慢查詢?nèi)罩局蟹謩e記錄著對應(yīng)sql的執(zhí)行時(shí)間链嘀,執(zhí)行的ip,執(zhí)行sql所消耗的時(shí)間萌狂,鎖的時(shí)間,返回行數(shù)

使用mysql自帶的慢查詢工具分析慢查詢?nèi)罩痉謩e記錄了怀泊,執(zhí)行次數(shù)茫藏,執(zhí)行sql消耗時(shí)間,鎖的時(shí)間霹琼、查詢結(jié)果的行數(shù)务傲、查詢的用戶

mysqldumpslow 分析慢查詢工具

由于開啟慢查詢?nèi)罩荆€上的日志可能會特別大枣申,直接看文本是不切實(shí)際的售葡,使用mysqldumpslow 工具可以分析統(tǒng)計(jì)日志文件形成報(bào)表,方便我們查看

//查看工具的命令
mysqldumpslow --help

下面列出比較常用的忠藤,也是最實(shí)用的

-s ORDER     what to sort by (al, at, ar, c, l, r, t), 'at' is default
                al: average lock time(按平均鎖定時(shí)間排序)
                ar: average rows sent(按平均行數(shù)排序)
                at: average query time(按平均查詢時(shí)間排序)
                 c: count(sql次數(shù))
                 l: lock time(鎖定時(shí)間)
                 r: rows sent(行數(shù))
                 t: query time (查詢時(shí)間) 
  -r           reverse the sort order (largest last instead of first)(倒序)

例如

//按照時(shí)間進(jìn)行排序挟伙,排序是從大到小排序
mysqldumpslow -s t /xxxx/xxx.log
//按照次數(shù)進(jìn)行排序
mysqldumpslow -s c /xxxx/xxx.log

SQL語句的優(yōu)化方案

join分析

一、小表驅(qū)動大表優(yōu)于大表驅(qū)動小表

先了解在join連接時(shí)哪個(gè)表是驅(qū)動表熄驼,哪個(gè)表是被驅(qū)動表:

  • 1.當(dāng)使用left join時(shí)像寒,左表是驅(qū)動表烘豹,右表是被驅(qū)動表
  • 2.當(dāng)使用right join時(shí)瓜贾,右表是驅(qū)動表,左表是被驅(qū)動表
  • 3.當(dāng)使用join時(shí)携悯,mysql會選擇數(shù)據(jù)量比較小的表作為驅(qū)動表祭芦,大表作為被驅(qū)動表
例如: A是小表,B是大表
  使用left join 時(shí)憔鬼,則應(yīng)該這樣寫select * from A a left join B b on a.code=b.code
  A表時(shí)驅(qū)動表龟劲,B表是被驅(qū)動表

測試:A表140多條數(shù)據(jù)胃夏,B表20萬左右的數(shù)據(jù)量
  select * from A a left join B b on a.code=b.code
  執(zhí)行時(shí)間:7.5s

  select * from B b left join A a on a.code=b.code
  執(zhí)行時(shí)間:19s
二、在join中驅(qū)動表的索引會失效昌跌,只有被驅(qū)動表的索引起效果仰禀,所以給被驅(qū)動表建立索引

在以小表驅(qū)動大表的情況下,再給大表建立索引會大大提高執(zhí)行速度蚕愤,驅(qū)動表的索引會失效答恶。
假如驅(qū)動表A有100條數(shù)據(jù),驅(qū)動表B有1000條數(shù)據(jù) 并且B表的字段s有索引萍诱,現(xiàn)在執(zhí)行這樣的語句:

select * from A a left join B b on (a.s = b.s)悬嗓;

這條語句會執(zhí)行多少次查詢呢?答案是:200次裕坊。
首先遍歷A表包竹,把A表的100條數(shù)據(jù)拿到B表一一查詢,由于B表的s字段是有索引的籍凝,所以每一次都能查到一一對應(yīng)的數(shù)據(jù)周瞎,所以需要100次查詢。也就是100+100=200静浴。
但是如果被驅(qū)動表B沒有的s字段沒有索引堰氓,那么就變成了全表掃描:100*1000=100000。性能就會變得及其低下苹享。所以使用join查詢時(shí),被驅(qū)動表務(wù)必加上索引双絮,否則性能會急劇下降。

三得问、在排序時(shí)使用索引字段效率會大幅度提升

  • 1囤攀、在進(jìn)行排序時(shí)如果排序字段是索引字段,那么排序?qū)呙杷饕瓿膳判蚬常饕緛砭褪怯行虻姆倌樱恍枰獟呙璞恚孕蕦浅漓骚??/li>
  • 2蝌衔、而不使用索引字段排序的情況,需要回表掃描然后開辟額外的內(nèi)存空間進(jìn)行排序蝌蹂,如果內(nèi)存空間不足還會生成臨時(shí)文件進(jìn)行落盤增加物理磁盤IO,極大的影響了SQL效率

using filesort

using filesort有兩種算法噩斟,一個(gè)是常規(guī)排序法,一個(gè)是優(yōu)化排序法

using filesort的常規(guī)排序法執(zhí)行步驟:
實(shí)例:

假設(shè)索引如下圖

可以看到查詢是使用了默認(rèn)的主鍵索引孤个,但是由于排序沒有遵循索引的最左原則所以使用了using filesort

排序緩沖區(qū)的大小262144B也就是256kb
排序緩沖區(qū)

由于排序的緩沖內(nèi)存只有256kb剃允,所以當(dāng)數(shù)據(jù)量很大時(shí)只能分批處理,第一批256kb的數(shù)據(jù)先在緩沖區(qū)排好序后,然后形成臨時(shí)文件保存起來(也就是落盤)斥废,接著第二批256kb排序形成的臨時(shí)文件需要和已經(jīng)存在的臨時(shí)文件進(jìn)行合并椒楣,就這樣按照步驟一直到排完數(shù)據(jù),在緩沖區(qū)存放的是(主鍵牡肉,排序字段)捧灰,臨時(shí)文件形成完了后利用主鍵去數(shù)據(jù)庫撈數(shù)據(jù),撈數(shù)據(jù)時(shí)會對主鍵再進(jìn)行排序以加快查詢速率统锤,這個(gè)過程會在另外的緩沖區(qū)進(jìn)行凤壁,默認(rèn)也是256kb。如下圖:
using filesort的優(yōu)化排序法執(zhí)行步驟:
using filesort的優(yōu)化排序法執(zhí)行步驟
using filesort什么時(shí)候執(zhí)行常規(guī)排序和優(yōu)化排序:

using filesort選擇什么樣的排序方法取決于參數(shù)的大小跪另,如下:

當(dāng)排序數(shù)量小4096B時(shí)使用的優(yōu)化排序法拧抖,否則使用常規(guī)排序

以下是聯(lián)合索引時(shí)排序是否走索引的情況

先建立一個(gè)聯(lián)合索引
索引 key a_b_c(a,b,c)

  • 使用索引的左端字段排序時(shí)能走索引,并且排序的升降序要一致
ORDER BY a
ORDER BY a,b
ORDER BY a,b,c
ORDER BY a DESC,b DESC,c DESC
  • 如果WHERE使用了索引的最左端,則ORDER BY 也能使用索引
WHERE a = const  ORDER BY b,c
WHERE a = const AND b = const ORDER BY c
WHERE a = const  ORDER BY b,c
WHERE a = const AND b > const ORDER BY b,c
  • 以下情況不能使用索引排序
WHERE a > const  ORDER BY b,c/*范圍排序不能使用索引*/
ORDER BY a ASC,b DESC,c DESC /*排序不一致*/
WHERE g = const ORDER BY b,c    /*丟失a索引*/
WHERE a = const ORDER BY c     /*丟失b索引*/
WHERE a = const ORDER BY a,d /*d不是索引的一部分*/

四免绿、禁止使用Select *唧席,只查自己需要的字段

1、在數(shù)據(jù)庫查詢出來的數(shù)據(jù)是需要網(wǎng)絡(luò)傳輸?shù)娇蛻舳说某凹荩樵兂鰜淼臄?shù)據(jù)字段越多數(shù)據(jù)量就越多淌哟,占用網(wǎng)絡(luò)帶寬
2、在排序時(shí)如果沒有走索引的情況下辽故,數(shù)據(jù)庫是需要另外開辟空間進(jìn)行排序的徒仓,字段越多數(shù)據(jù)量大導(dǎo)致內(nèi)存不足就需要進(jìn)行創(chuàng)建臨時(shí)文件進(jìn)行落盤增加物理磁盤IO
3、很有可能會使覆蓋索引失效

五誊垢、order by,group by,distinct盡量使用索引

除了order by會排序外,group by(group by在Mysql8的中進(jìn)行了優(yōu)化掉弛,內(nèi)部已經(jīng)不排序了)和distinct是內(nèi)部先進(jìn)行排序的,所以最好的優(yōu)化方案就是盡量的使它們走索引

group by:

索引:

建立的索引如下圖:

使用在Mysql5.6中g(shù)roup by分組時(shí)出現(xiàn)了using filesort就代表進(jìn)行了無索引的排序
mysql5.6 group by會默認(rèn)排序

使用在Mysql8中g(shù)roup by分組時(shí)不排序喂走,如果出現(xiàn)了下面的using temporary代表這個(gè)分組時(shí)沒有走索引殃饿,而是利用了臨時(shí)表進(jìn)行分組
mysql8group by分組時(shí)默認(rèn)不排序

出現(xiàn)了using temporary和using filesort的分組都需要進(jìn)行優(yōu)化

group by的三種類型
group by的三種類型

六、盡量使用子關(guān)聯(lián)查詢代替使用子查詢芋肠。

盡量將子查詢轉(zhuǎn)化為join,子查詢產(chǎn)生的結(jié)果集無法使用索引乎芳,并且子查詢的結(jié)果集會產(chǎn)生臨時(shí)表,如果子查詢數(shù)量過多生成大量的臨時(shí)表帖池,嚴(yán)重消耗cup io資源奈惑。

但是現(xiàn)實(shí)中子查詢是很多業(yè)務(wù)必不可少的,現(xiàn)在mysql的優(yōu)化器對子查詢的優(yōu)化也有相應(yīng)的支撐睡汹,有些子查詢效果不一定比關(guān)聯(lián)查詢差肴甸,所以還以實(shí)際的執(zhí)行計(jì)劃去定

七、分頁優(yōu)化

八帮孔、union優(yōu)化

盡可能的使用union all 代替union

九雷滋、復(fù)合索引的優(yōu)化

在通常我們查詢時(shí),sex(男文兢,女)晤斩,flag(0,1)之類的是不適合建立索引的姆坚,這類的散列性太差澳泵,但是很多查詢又用到它們,我們可以給它們建立一個(gè)復(fù)合索引(sex,flag),每一次使用時(shí)必須遵守最左前綴原則兼呵,這里通惩酶ǎ可以這么用:例如我們要查一個(gè)帶上flag又不帶sex字段的查詢,我們可以寫成這樣繞過sex字段击喂,又符合了最左前綴

select xxx from xxx where sex in ('男','女') and flag=xxx

十维苔、范圍查詢優(yōu)化

范圍查詢(between,<,>)會使右邊的索引條件失效,所以在sql中盡可能的把范圍查詢放在最后面

十一懂昂、其他方案

  • 1介时、不要在數(shù)據(jù)庫進(jìn)行運(yùn)算,比如:觸發(fā)器凌彬,存儲過程沸柔、函數(shù)。應(yīng)當(dāng)用后端代碼實(shí)現(xiàn)
  • 2铲敛、主鍵推薦使用自增的褐澎,不推薦使用字符串作為索引,主鍵不應(yīng)該被修改伐蒋。選擇自增主鍵作為索引時(shí)插入數(shù)據(jù)時(shí)都是按照索引的順序插入的工三,索引不需要移動數(shù)據(jù)和銷毀額外的性能維護(hù)索引數(shù)據(jù),而頻繁修改索引會造索引數(shù)據(jù)頻繁移動產(chǎn)生索引碎片得不到一個(gè)結(jié)構(gòu)順序緊湊的索引結(jié)構(gòu)先鱼。如果我們沒有選擇自增主鍵也沒有指定其他字段當(dāng)主鍵徒蟆,innodb會選擇一個(gè)rowid作為內(nèi)置主鍵,往后我們插入的數(shù)據(jù)的主鍵都是按照這個(gè)rowid自增生成
  • 3型型、事務(wù)簡單段审,甚至不要事務(wù)
  • 4、使用or代替in,用union代替or,使用union all代替union(union有去重的開銷)
  • 5闹蒜、limit優(yōu)化:偏移量越大寺枉,執(zhí)行越慢
select * from a where id in (select id from a where id>10000) limit0,10
select * from a limit10000,10
第一個(gè)sql優(yōu)于第二個(gè)sql

分析sql的執(zhí)行計(jì)劃

分析的內(nèi)容如下圖

id表示查詢的順序:

  • 當(dāng)id相同時(shí),sql是由上至下執(zhí)行绷落,應(yīng)該由上至下分析
  • 當(dāng)id不同時(shí)姥闪,其id越大則表示執(zhí)行的優(yōu)先級越高,應(yīng)該從id高的到id低的分析
  • 當(dāng)id為null時(shí)砌烁,其代表了是union查詢的結(jié)果
  • 出現(xiàn)兩組id,應(yīng)該id大的一組優(yōu)先權(quán)高

此處是id相同的筐喳,可以看到這里sql是先查詢c表
當(dāng)id相同時(shí)催式,sql是由上至下執(zhí)行,應(yīng)該由上至下分析

此處是id不相同的避归,可以看到這里sql是先查詢b表
當(dāng)id不同時(shí)荣月,其id越大則表示執(zhí)行的優(yōu)先級越高

此處出現(xiàn)兩組id,應(yīng)該先看id=2的組,出現(xiàn)兩組id,應(yīng)該id大的一組優(yōu)先權(quán)高
出現(xiàn)兩組id,應(yīng)該id大的一組優(yōu)先權(quán)高

各個(gè)select_type的含義

table

查詢的表名梳毙,并不一定是真實(shí)存在的表哺窄,有別名顯示別名,也可能為臨時(shí)表

復(fù)雜的table的含義:

type

mysql查詢的方式账锹,這是Mysql查詢性能好壞最重要的指標(biāo)

1萌业、system
system:當(dāng)系統(tǒng)表僅有一行記錄時(shí),數(shù)據(jù)量很少奸柬,往往不需要進(jìn)行磁盤IO生年,速度非常快廓奕。
2晶框、const
const:表示通過索引一次就找到了,const用于比較primary key 或者 unique索引懂从。因?yàn)橹恍杵ヅ湟恍袛?shù)據(jù)授段,所有很快。如果將主鍵置于where列表中番甩,mysql就能將該查詢轉(zhuǎn)換為一個(gè)const侵贵。
3、eq_ref
eq_ref:查詢時(shí)命中主鍵primary key 或者 unique key索引缘薛, type 就是 eq_ref窍育。
4、ref
ref:非唯一性索引掃描宴胧,返回匹配某個(gè)單獨(dú)值的所有行漱抓。本質(zhì)是也是一種索引訪問,它返回所有匹配某個(gè)單獨(dú)值的行恕齐,然而他可能會找到多個(gè)符合條件的行乞娄,所以它應(yīng)該屬于查找和掃描的混合體
5、ref_or_null
ref_or_null:這種連接類型類似于 ref显歧,區(qū)別在于 MySQL會額外搜索包含NULL值的行仪或。
6、index_merge
index_merge:使用了索引合并優(yōu)化方法士骤,查詢使用了兩個(gè)以上的索引范删。
7、range
range:使用索引選擇行拷肌,僅檢索給定范圍內(nèi)的行到旦。簡單點(diǎn)說就是針對一個(gè)有索引的字段旨巷,給定范圍檢索數(shù)據(jù)。在where語句中使用 bettween...and添忘、<采呐、>、<=昔汉、in 等條件查詢 type 都是 range。
10拴清、index
index:Index 與ALL 其實(shí)都是讀全表靶病,區(qū)別在于index是遍歷索引樹讀取,而ALL是從硬盤中讀取口予。
11娄周、ALL
ALL:將遍歷全表以找到匹配的行,性能最差沪停。

keys

  • possible_index:查詢出可能會用到的索引
  • key:查詢時(shí)實(shí)際用到的索引
  • key_len:查詢實(shí)際使用索引的最大長度煤辨,與字段的數(shù)據(jù)類型有關(guān)

ref

常見的有:const,func木张,null众辨,字段名。
4舷礼、當(dāng)使用常量等值查詢鹃彻,顯示const,
1妻献、當(dāng)關(guān)聯(lián)查詢時(shí)蛛株,會顯示相應(yīng)關(guān)聯(lián)表的關(guān)聯(lián)字段
2、如果查詢條件使用了表達(dá)式育拨、函數(shù)谨履,或者條件列發(fā)生內(nèi)部隱式轉(zhuǎn)換,可能顯示為func
3熬丧、其他情況null

extra

這一列包含的是不在其他列顯示的額外信息笋粟。

  • using index:這個(gè)說明MySQL使用覆蓋索引,避免了回表操作析蝴,效率不錯(cuò)矗钟。
  • using where:查詢時(shí)未找到可用的索引,進(jìn)而通過where條件過濾獲取所需數(shù)據(jù)嫌变,但要注意的是并不是所有帶where語句的查詢都會顯示Using where吨艇。
  • using filesort:表示無法利用索引完成的排序操作,也就是ORDER BY的字段沒有索引腾啥,通常這樣的SQL都是需要優(yōu)化的东涡。
  • using temporay:表示查詢后結(jié)果需要使用臨時(shí)表來存儲冯吓,一般在排序或者分組查詢時(shí)用到。
  • Using join buffer:在我們聯(lián)表查詢的時(shí)候疮跑,如果表的連接條件沒有用到索引组贺,需要有一個(gè)連接緩沖區(qū)來存儲中間結(jié)果。
  • Impossible where:表示在我們用不太正確的where語句祖娘,導(dǎo)致沒有符合條件的行失尖。

索引的優(yōu)化

哪些字段適合創(chuàng)建索引、哪些不適合的:
  • 1渐苏、字段值散列度大掀潮,唯一性高的字段。例如:狀態(tài)字段可能只是兩個(gè)值這種唯一性太差的值不適合當(dāng)索引琼富。散列性越差仪吧,說明重復(fù)數(shù)據(jù)越多,如果作為索引鞠眉,占據(jù)的葉子節(jié)點(diǎn)量龐大薯鼠,此時(shí)索引掃描跟全表掃描區(qū)別不大
  • 2、如果非要為這些唯一性性差的字段創(chuàng)建索引械蹋,可以嘗試使用復(fù)合索引
  • 3出皇、更新太頻繁的字段不適合創(chuàng)建索引,因?yàn)槊看胃伦侄位└辏饕紩匦抡{(diào)整排序io消耗巨大
  • 4恶迈、頻繁使用where、order by谱醇、group by暇仲、distinct、join 建立索引或者聯(lián)合索引
  • 5副渴、建立聯(lián)合索引奈附,應(yīng)當(dāng)優(yōu)先左側(cè)優(yōu)化,如:散列性最好煮剧、使用最頻繁斥滤、字段長度最小符合這些條件的優(yōu)先放在聯(lián)合索引的左側(cè)
在 MySQL 中,下列幾種情況下有可能使用到索引勉盅。
  • 索引一是否被用到舉例說明如下佑颇。
    有幾個(gè)原則:
1、 復(fù)合索引左前綴和中間不斷原則草娜,復(fù)合索引的前綴不能丟失挑胸,中間索引不能斷,
例如:key(a,b,c),where a b c宰闰、where a b茬贵、where a簿透、where a f b 都能用到索引,
where a c,where b c解藻、where c老充、where b都用不到索引

2、不能在索引上進(jìn)行特殊操作螟左。例如:函數(shù)啡浊、計(jì)算、類型轉(zhuǎn)換等

3胶背、在聯(lián)合索引中使用范圍(between,<,>,in等)會導(dǎo)致右邊的索引失效巷嚣,
例如:where a=1 and b>1 order by c desc b這個(gè)字段的條件是一個(gè)范圍,
導(dǎo)致右邊c字段索引失效奄妨。解決辦法:把b從聯(lián)合索引移除涂籽,只留下a c

4苹祟、在索引中使用(!=,<>,is null,%xx,%xx%)會導(dǎo)致索引失效,
注意(is not null)在mysql8以下的版本會使索引失效

5砸抛、索引是字符串,查詢時(shí)卻不用單引號括起來會導(dǎo)致索引失效

6树枫、使用or時(shí)直焙,查詢的字段不是索引字段就會失效,or中的條件不是索引字段也會失效砂轻,
例如奔誓,key g,key(a,b,c),   select a b where a or b、select a g where a or g   都走索引搔涝,
select f a g where a or g厨喂、select a g where a or g or f就不走索引了

7、禁止使用子查詢庄呈,將子查詢轉(zhuǎn)化為join,子查詢產(chǎn)生的結(jié)果集無法使用索引蜕煌,
并且子查詢的結(jié)果集會產(chǎn)生臨時(shí)表,如果子查詢數(shù)量過多生成大量的臨時(shí)表诬留,嚴(yán)重消耗cup io資源

首先按 company_id斜纪,moneys 的順序創(chuàng)建一個(gè)復(fù)合索引,具體如下:

create index ind_sales2_companyid_moneys on sales2(company_id,moneys);
create index ind_company2_name on company2(name);
create index ind_sales_year on sales(year);

  • 然后按 company_id 進(jìn)行表查詢文兑,具體如下:

可以發(fā)現(xiàn)即便 where 條件中不是用的 company_id 與 moneys 的組合條件盒刚,索引仍然能 用到,這就是索引的最左側(cè)原則绿贞。但是如果只按 moneys 條件查詢表因块,那么索引就不會 被用到,具體如下:

可見雖然在 moneys 上面建有復(fù)合索引籍铁,但是由于 moneys不是聯(lián)合索引的第一列贮聂,那么在查詢中 這個(gè)索引也不會被 MySQL 采用靠柑。

  • 對于使用 like 的查詢,后面如果是常量并且只有%號不在第一個(gè)字符吓懈,索引才可能會 被使用歼冰,來看下面這個(gè)個(gè)執(zhí)行計(jì)劃

可以發(fā)現(xiàn)第一個(gè)例子沒有使用索引,而第二例子就能夠使用索引耻警,區(qū)別就在于“%”的位置 不同隔嫡,前者把“%”放到第一位就不能用到索引,而后者沒有放到第一位就使用了索引甘穿。 另外腮恩,如果如果 like 后面跟的是一個(gè)列的名字,那么索引也不會被使用温兼。
如果對大的文本進(jìn)行搜索秸滴,使用全文索引而不用使用 like ‘%…%’。

  • 如果列名是索引募判,使用column_name is null可以走索引荡含。如下例中查詢name為null 的記錄就用到了索引,但是如果是name is not null届垫,not in,<>都是使用不到索引
  • 用 or 分割開的條件释液,如果 or 前的條件中的列有索引,而后面的列中沒有索引装处, 那么涉及到的索引都不會被用到误债,例如:

可見雖然在 year 這個(gè)列上存在索引 ind_sales_year,但是這個(gè) SQL 語句并沒有用到這個(gè)索引妄迁, 原因就是 or 中有一個(gè)條件中的列沒有索引寝蹈。

  • 如果列類型是字符串,那么一定記得在 where 條件中把字符常量值用引號引 起來登淘,否則的話即便這個(gè)列上有索引箫老,MySQL 也不會用到的,因?yàn)樾伟铮琈ySQL 默認(rèn)把輸入的 常量值進(jìn)行轉(zhuǎn)換以后才進(jìn)行檢索槽惫。如下面的例子中 company2 表中的 name 字段是字符型的, 但是 SQL 語句中的條件值 294 是一個(gè)數(shù)值型值辩撑,因此即便在 name 上有索引界斜,MySQL 也不能 正確地用上索引,而是繼續(xù)進(jìn)行全表掃描合冀。

從上面的例子中可以看到吁伺,第一個(gè) SQL 語句中把一個(gè)數(shù)值型常量賦值給了一個(gè)字符型的列 name陪捷,那么雖然在 name 列上有索引九杂,但是也沒有用到;而第二個(gè) SQL 語句就可以正確使 用索引教硫。

其他SQL優(yōu)化

INSERT

如果同時(shí)從同一客戶插入很多行族吻,盡量使用多個(gè)值表的 INSERT 語句,這種方式將大大
縮減客戶端與數(shù)據(jù)庫之間的連接、關(guān)閉等消耗,使得效率比分開執(zhí)行的單個(gè) INSERT 語 句快(在一些情況中幾倍)奕删。下面是一次插入多值的一個(gè)例子:

insert into test values(1,2),(1,3),(1,4)...
GROUP BY

提高GROUP BY語句的效率, 可以通過將不需要的記錄在GROUP BY 之前過濾掉.下面兩個(gè)查詢返回相同結(jié)果,但第二個(gè)明顯就快了許多.

低效:
SELECT JOB , AVG(SAL) FROM EMP GROUP BY JOB
HAVING JOB ='PRESIDENT' OR JOB ='MANAGER'
高效:
SELECT JOB , AVG(SAL) FROM EMP WHERE JOB ='PRESIDENT'
OR JOB ='MANAGER' GROUP BY JOB
嵌套查詢

盡量使用JOIN代替子查詢疗认,使用JOIN時(shí)
子查詢產(chǎn)生的結(jié)果集無法使用索引完残,并且子查詢的結(jié)果集會產(chǎn)生臨時(shí)表,如果子查詢數(shù)量過多生成大量的臨時(shí)表横漏,嚴(yán)重消耗cup io資源
如果一定要使用子查詢進(jìn)行復(fù)制的sql嵌套谨设,應(yīng)當(dāng)盡量縮小內(nèi)表,也就是先把子查詢的表按條先進(jìn)行篩選然后在進(jìn)行查詢關(guān)聯(lián)這樣關(guān)聯(lián)的范圍就會變小

MySql表字段優(yōu)化

char和varchar
  • char:定長缎浇,一般用于固定長度的數(shù)據(jù)存儲 扎拣,例如:身份證號,手機(jī)號素跺,電話二蓝,密碼等
    CHAR(M)定義的列的長度為固定的,M取值可以為0~255之間亡笑,M代表了可以存儲的字符的個(gè)數(shù)而不是字節(jié)數(shù)侣夷,當(dāng)存儲的字符不足指定的M值時(shí)會充空格以達(dá)到指定的長度横朋,比如定義 char(10)仑乌,那么不論你存儲的數(shù)據(jù)是否達(dá)到了10個(gè)字節(jié),都要占去10個(gè)字節(jié)的空間,不足的自動用空格填充琴锭。晰甚。當(dāng)檢索時(shí),尾部的空格被刪除掉决帖。在存儲或檢索過程中不進(jìn)行大小寫轉(zhuǎn)換厕九。由于是固定長度,更新時(shí)不需要調(diào)整存儲空間地回,所以在CHAR字段上的索引效率級高

  • varchar:不定長扁远,VARCHAR(M),M取值可以為0~65535之間刻像,M代表了可以存儲的字符的個(gè)數(shù)而不是字節(jié)數(shù)畅买,整體最大長度是65,532字節(jié),在utf-8中一個(gè)字符占用磁盤空間的3個(gè)字節(jié)细睡,另加一個(gè)字節(jié)或兩個(gè)字節(jié)來記錄長度(如果列聲明的長度超過255谷羞,否則使用兩個(gè)字節(jié))。VARCHAR值保存時(shí)不進(jìn)行填充溜徙。當(dāng)值保存和檢索時(shí)尾部的空格仍保留湃缎。varchar存儲變長數(shù)據(jù)犀填,但是VARCHAR可以節(jié)約內(nèi)存塊的長度和磁盤空間的大小,檢索上可以提升性能嗓违,但存儲效率沒有CHAR高九巡。

char盡量存儲一些定長的數(shù)據(jù),比如身份證蹂季、電話號碼比庄、姓名等,char因?yàn)槭枪潭碎L度所以可以避免不定長度字符串的空間調(diào)節(jié)乏盐。

varchar根據(jù)實(shí)際存儲的數(shù)據(jù)分配最終的存儲空間佳窑,由于可以自動調(diào)節(jié)長度,所以可以節(jié)省磁盤空間父能,但是并不能調(diào)節(jié)內(nèi)存空間神凑,內(nèi)存空間是定長的時(shí)候固定的,但是如果進(jìn)行字段更新時(shí)會做空間調(diào)節(jié)操作何吝,額外消耗性能溉委,如果擴(kuò)展長度時(shí)頁空間不足還會通過頁的分裂來達(dá)到存儲的目的,進(jìn)一步降低性能爱榕,所以在創(chuàng)建varchar不要使用默認(rèn)的255瓣喊,而根據(jù)需要的中文個(gè)數(shù)創(chuàng)建,過大的字符長度會消耗過多的緩沖池內(nèi)存和B+樹結(jié)構(gòu)葉子節(jié)點(diǎn)的增多

char和varchar相同點(diǎn):
char(n)黔酥,varchar(n)中的n都代表字符的個(gè)數(shù)
超過char藻三,varchar最大長度n的限制后,字符串會被截?cái)唷?/p>

varchar定長的意義:
使用VARCHAR(100)與VARCHAR(200)存儲的90字符的效果是的相同嗎?結(jié)果是否定的跪者。雖然他們用來存儲90個(gè)字符的數(shù)據(jù)棵帽,其存儲空間相同。但是對于內(nèi)存的消耗是不同的渣玲。對于VARCHAR數(shù)據(jù)類型來說逗概,硬盤上的存儲空間雖然都是根據(jù)實(shí)際字符長度來分配存儲空間的,但是對于內(nèi)存來說忘衍,則不是逾苫。其時(shí)使用固定大小的內(nèi)存塊來保存值。簡單的說枚钓,就是使用字符類型中定義的長度铅搓,即200個(gè)字符空間。顯然秘噪,這對于排序或者臨時(shí)表(這些內(nèi)容都需要通過內(nèi)存來實(shí)現(xiàn))作業(yè)會產(chǎn)生比較大的不利影響狸吞。所以如果某些字段會涉及到文件排序或者基于磁盤的臨時(shí)表時(shí),分配VARCHAR數(shù)據(jù)類型時(shí)仍然不能夠太過于慷慨。還是要評估實(shí)際需要的長度蹋偏,然后選擇一個(gè)最長的字段來設(shè)置字符長度便斥。如果為了考慮冗余,可以留10%左右的字符長度威始。千萬不能認(rèn)為其為根據(jù)實(shí)際長度來分配存儲空間枢纠,而隨意的分配長度,或者說干脆使用最大的字符長度黎棠。

由于B+樹的特點(diǎn)所以設(shè)計(jì)字段時(shí)盡量設(shè)計(jì)的小晋渺,夠用就好了,這樣可以減少磁盤的io次數(shù)脓斩,有以下方案:

  • 1.表字段避免null值出現(xiàn)木西,null值很難查詢優(yōu)化且占用額外的索引空間,推薦默認(rèn)數(shù)字0代替null随静。
  • 2.盡量使用INT而非BIGINT八千,如果非負(fù)則加上UNSIGNED(這樣數(shù)值容量會擴(kuò)大一倍),當(dāng)然能使用TINYINT燎猛、SMALLINT恋捆、MEDIUM_INT更好。
  • 3.少用text類型重绷,盡量用varchar代替text沸停,char雖然比varchar快,但是char是定長昭卓,varchar是可擴(kuò)展長度的愤钾,更適合代替text類型的場景
  • 4.使用枚舉或整數(shù)代替字符串類型
  • 5.日期盡量使用TIMESTAMP代替DATETIME
  • 6.單表不要有太多字段,建議在20以內(nèi)
  • 7.用整型來存IP葬凳,如:使用函數(shù)相互轉(zhuǎn)化inet_aton('255.255.255.255')=4294967295,inet_ntoa(4294967295)='255.255.255.255'

內(nèi)存優(yōu)化

https://www.cnblogs.com/ilifeilong/p/7224678.html

緩沖池:

innodb是基于磁盤存儲的绰垂,為提升性能室奏,innodb會在內(nèi)存中有一個(gè)緩沖池火焰,每次讀書數(shù)據(jù)會將數(shù)據(jù)放到緩沖池中緩存,修改數(shù)據(jù)也是先對緩沖池進(jìn)行修改胧沫,然后按照一定的頻率刷回硬盤昌简,當(dāng)查詢命中內(nèi)存中的緩沖池時(shí)就不用查詢磁盤。

在專用數(shù)據(jù)庫服務(wù)器上绒怨,通常將多達(dá)80%的物理內(nèi)存分配給InnoDB緩沖池纯赎。因?yàn)镮nnoDB的存儲引擎的工作方式總是將數(shù)據(jù)庫文件按頁讀取到緩沖池,每個(gè)頁16k默認(rèn)(innodb_page_size=16k)南蹂,在MySQL 5.7中增加了32KB和64KB頁面大小的支持犬金,通過innodb_buffer_pool_size設(shè)置緩沖池大小,通過innodb_page_size設(shè)置頁的大小,設(shè)置innodb_buffer_pool_size時(shí)晚顷,操作以塊(chunk)形式執(zhí)行峰伙。塊大小由innodb_buffer_pool_chunk_size配置選項(xiàng)定義,默認(rèn)值128M该默,也就是說每個(gè)緩沖池實(shí)例大小默認(rèn)是128M瞳氓,innodb_buffer_pool_size=innodb_buffer_pool_chunk_sizeinnodb_buffer_pool_instances的倍數(shù),如果配置innodb_buffer_pool_size為不等于innodb_buffer_pool_chunk_sizeinnodb_buffer_pool_instances的倍數(shù)栓袖,則緩沖池大小將自動調(diào)整為等于或不小于指定緩沖池大小的innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍數(shù)匣摘。
例如:

show variables like 'innodb_buffer_pool_size'   //查詢innodb_buffer_pool_size的大小
SET GLOBAL innodb_buffer_pool_size=16106127360 //設(shè)置innodb_buffer_pool_size的大小
LRU

InnoDB將buffer pool作為一個(gè)list管理,基于LRU算法裹刮。當(dāng)有新的頁要讀入到buffer pool的時(shí)候音榜,buffer pool就將最近最少使用的頁從buffer pool中驅(qū)逐出去(類似redis的內(nèi)存淘汰)并且將新頁加入到list的中間位置,這就是所謂的“中點(diǎn)插入策略”捧弃。一般情況下list頭部存放的是熱數(shù)據(jù)就是所謂的young pages(最近經(jīng)常訪問的數(shù)據(jù))囊咏,list尾部存放的就是old pages(最近不被訪問的數(shù)據(jù)),默認(rèn)3/8的list信息是作為old list塔橡,這些信息是被驅(qū)逐的對象梅割,通過innodb_old_block_pct設(shè)置比例,默認(rèn)是37也就是3/8葛家,每次插入新頁都會從倒數(shù)的3/8的位置插入户辞,倒數(shù)的3/8的頁是要被刷出緩沖池的

  • 1、3/8的list信息是作為old list癞谒,這些信息是被驅(qū)逐的對象底燎。
  • 2、list的中點(diǎn)就是我們所謂的old list頭部和new list尾部的連接點(diǎn)弹砚,相當(dāng)于一個(gè)界限双仍。
  • 3、新數(shù)據(jù)的讀入首先會插入到old list的頭部桌吃。
  • 4朱沃、如果是old list的數(shù)據(jù)被訪問到了,這個(gè)頁信息就會變成new list茅诱,變成young page逗物,就會將數(shù)據(jù)頁信息移動到new sublist的頭部。

innodb_old_blocks_time瑟俭,單位是毫秒翎卓,默認(rèn)是1000,可以設(shè)置當(dāng)頁在磁盤中被讀出后經(jīng)過多長時(shí)間才能被加入的緩沖池摆寄。如果增大這個(gè)值的話失暴,就會讓buffer pool里面很多頁信息變老的速度變快坯门,因?yàn)檫@些數(shù)據(jù)不會很快被內(nèi)存中擦除的話,就會變成熱數(shù)據(jù)而擠掉原有緩存的數(shù)據(jù)逗扒。

緩沖池實(shí)例

并且緩沖池是支持多實(shí)例的田盈,也就是一個(gè)MySql實(shí)例支持多個(gè)緩沖池以減少并發(fā)操作中內(nèi)存結(jié)構(gòu)的爭用,對應(yīng)內(nèi)存比較大的64位服務(wù)器缴阎,可以設(shè)置為8個(gè)允瞧,通過show engine innodb status查詢,通過innodb_buffer_pool_instances設(shè)置緩沖池的實(shí)例數(shù)

show engine innodb status //查詢緩沖池實(shí)例
SET GLOBAL innodb_buffer_pool_instances=8 // 設(shè)置緩沖池的實(shí)例數(shù)

MySql的優(yōu)化極限

當(dāng)面對百萬級別的數(shù)據(jù)量級時(shí)蛮拔,就算是Mysql調(diào)優(yōu)大濕都不可能調(diào)得動了述暂,這時(shí)就到了MySql的性能極限了,請放棄在MySql的關(guān)聯(lián)查詢吧建炫,盡量利用緩存和隊(duì)列

這時(shí)一個(gè)優(yōu)秀的架構(gòu)就比極限調(diào)優(yōu)更勝百倍畦韭,MySql在一個(gè)不錯(cuò)的物理機(jī)器上最多能承載3500-4500的QPS

讀多寫少用緩存,寫多讀少用隊(duì)列肛跌,那么高并發(fā)用哪種艺配?當(dāng)然是兩種一起用:
  • 1、高并發(fā)讀數(shù)據(jù):利用redis將熱點(diǎn)數(shù)據(jù)進(jìn)行緩存衍慎,將絕大部分的讀壓力讓redis承擔(dān)转唉,以減輕mysql的讀壓力
  • 2、高并發(fā)寫數(shù)據(jù):當(dāng)大量的寫請求到達(dá)Mysql時(shí)稳捆,將寫流量先進(jìn)隊(duì)列赠法,通過隊(duì)列慢慢的來對MySql進(jìn)行寫操作,緩沖了MySql的寫壓力

MySQL的并發(fā)控制優(yōu)化

LBCC乔夯,基于鎖的并發(fā)控制砖织,Lock Based Concurrency Control。

使用鎖的機(jī)制末荐,在當(dāng)前事務(wù)需要對數(shù)據(jù)修改時(shí)侧纯,將當(dāng)前事務(wù)加上鎖,同一個(gè)時(shí)間只允許一條事務(wù)修改當(dāng)前數(shù)據(jù)甲脏,其他事務(wù)必須等待鎖釋放之后才可以操作眶熬。性能太差

MVCC,多版本的并發(fā)控制剃幌,Multi-Version Concurrency Control聋涨。

MVCC的目的就是多版本并發(fā)控制,在數(shù)據(jù)庫中的實(shí)現(xiàn)负乡,就是為了解決讀寫沖突。使用鎖和鎖協(xié)議來實(shí)現(xiàn)相應(yīng)的隔離級別來進(jìn)行并發(fā)控制會因?yàn)殒i會造成事務(wù)阻塞脊凰。而多版本并發(fā)控制使得對同一行記錄做讀寫的事務(wù)之間不用相互阻塞等待抖棘,提高了事務(wù)的并發(fā)能力茂腥,可以認(rèn)為MVCC是一種解決讀寫阻塞等待的行級鎖。

MVCC的數(shù)據(jù)庫表中每一行數(shù)據(jù)都可能存在多個(gè)版本切省,對數(shù)據(jù)庫的任何修改的提交都不會直接覆蓋之前的數(shù)據(jù)最岗,而是產(chǎn)生一個(gè)新的版本與老版本共存,通過讀寫數(shù)據(jù)時(shí)讀不同的版本來避免加鎖阻塞

  • 1朝捆、MVCC只支持(已提交讀)和(可重復(fù)讀)隔離級別般渡。
  • 2、MVCC能解決臟讀芙盘、不可重復(fù)讀問題驯用,不能解決幻讀問題。
  • 3儒老、MVCC是用來解決讀寫操作之間的阻塞問題蝴乔。

隱式字段

每行記錄除了我們自定義的字段外,還有數(shù)據(jù)庫隱式定義的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段

  • DB_TRX_ID:數(shù)據(jù)行版本號:大小為6byte驮樊,記錄最近修改(修改/插入)事務(wù)ID薇正,記錄創(chuàng)建這條記錄/最后一次修改該記錄的事務(wù)ID
  • DB_ROLL_PTR:刪除版本號:大小為7byte,記錄回滾指針囚衔,指向當(dāng)前記錄行的undo log信息(指向該數(shù)據(jù)的前一個(gè)版本數(shù)據(jù))
  • DB_ROW_ID:行數(shù)據(jù)隱式id:大小為6byte挖腰,隱含的自增ID(隱藏主鍵),如果數(shù)據(jù)表沒有主鍵练湿,InnoDB會自動以DB_ROW_ID
    產(chǎn)生一個(gè)聚簇索引
ReadView

read view是讀視圖曙聂,其實(shí)就相當(dāng)于一種快照,里面記錄了系統(tǒng)中當(dāng)前活躍事務(wù)的ID以及相關(guān)信息鞠鲜,主要用途是用來做可見性判斷宁脊,判斷當(dāng)前事務(wù)是否有資格訪問該行數(shù)據(jù)。read view有多個(gè)變量:

  • trx_ids: 它里面的trx_ids變量存儲了活躍事務(wù)列表贤姆,也就是Read View開始創(chuàng)建時(shí)其他未提交的活躍事務(wù)的ID列表榆苞。例如事務(wù)A在創(chuàng)建read view(快照)時(shí),數(shù)據(jù)庫中事務(wù)B和事務(wù)C還沒提交或者回滾結(jié)束事務(wù)霞捡,此時(shí)trx_ids就會將事務(wù)B和事務(wù)C的事務(wù)ID記錄下來坐漏。

假設(shè)當(dāng)前事務(wù)生成了一個(gè)ReadView,trx_ids列表里的事務(wù)id為[60,100]碧信。

1赊琳、如果你要訪問的記錄版本的事務(wù)id為50,比當(dāng)前列表最小的id 60還小砰碴,那說明這個(gè)事務(wù)在ReadView生成之前就提交了躏筏,所以對當(dāng)前活動的事務(wù)來說是可訪問的。
2呈枉、如果你要訪問的記錄版本的事務(wù)id為70,發(fā)現(xiàn)此事務(wù)在列表id最大值和最小值之間趁尼,那就再判斷一下 70 這個(gè)id是否在列表內(nèi)埃碱,如果在那就說明此事務(wù)還未提交,所以版本不能被訪問酥泞。如果不在那說明事務(wù)已經(jīng)提交砚殿,所以版本可以被訪問。
3芝囤、如果你要訪問的記錄版本的事務(wù)id為110似炎,那比事務(wù)列表最大id100都大,那說明這個(gè)版本是在ReadView生成之后才發(fā)生的悯姊,所以不能被訪問羡藐。

Undo log

Undo log中存儲的是老版本數(shù)據(jù),當(dāng)一個(gè)事務(wù)需要讀取記錄行時(shí)挠轴,如果當(dāng)前記錄行不可見传睹,可以通過回滾指針順著undo log鏈找到滿足其可見性條件的記錄行版本。

在InnoDB里岸晦,undo log分為如下兩類:
①insert undo log : 事務(wù)對insert新記錄時(shí)產(chǎn)生的undo log, 只在事務(wù)回滾時(shí)需要, 并且在事務(wù)提交后就可以立即丟棄欧啤。
②update undo log : 事務(wù)對記錄進(jìn)行delete和update操作時(shí)產(chǎn)生的undo log,在事務(wù)回滾時(shí)需要

實(shí)際還有一個(gè)刪除flag隱藏字段, 既記錄被更新或刪除并不代表真的刪除启上,而是刪除flag變了

插入:獲取最新的事務(wù)版本號n,保存n到對應(yīng)行的行版本號

刪除:獲取最新的事務(wù)版本號n邢隧,保存到對應(yīng)行的刪除版本號

修改:變?yōu)閕nsert和delete操作的組合,先獲取最新的事務(wù)版本號n冈在,然后進(jìn)行數(shù)據(jù)行拷貝倒慧,插入拷貝的數(shù)據(jù),保存n到新插入數(shù)據(jù)行行版本號的字段中包券,然后保存n到舊的數(shù)據(jù)行的刪除版本號字段中

查詢:獲取最新的事務(wù)版本號n,查詢行版本號小于或者等于n的行數(shù)據(jù)纫谅,防止讀到其他事務(wù)提交的數(shù)據(jù)

MVCC實(shí)現(xiàn)過程原理主要的原理:

參考:https://baijiahao.baidu.com/s?id=1629409989970483292&wfr=spider&for=pc

版本記錄都是去版本鏈里面找的,然后根據(jù)不同隔離級別生成的ReadView就會有所不同

  • 例如:在一個(gè)讀已提交或者是重復(fù)讀的級別事務(wù)中

    有一個(gè)事務(wù)id為100的事務(wù)溅固,修改了name,使得的name等于小明2付秕,但是事務(wù)還沒提交。則此時(shí)的版本鏈如下:
    此時(shí)另一個(gè)事務(wù)發(fā)起了select 語句要查詢id為1的記錄侍郭,那此時(shí)生成的ReadView 列表就是[100]询吴。最新版本100是活躍事務(wù)不能訪問,那就得使用版本鏈去找了亮元,首先找100的下個(gè)版本name為小明1的記錄猛计,發(fā)現(xiàn)trx_id是60,小于列表中的最小id,所以可以訪問爆捞,直接訪問結(jié)果為小明1奉瘤。那這時(shí)候我們把事務(wù)id為100的事務(wù)提交了,并且新建了一個(gè)事務(wù)id為110也修改id為1的記錄嵌削,name修改為小明3毛好,并且不提交事務(wù)

    這時(shí)候之前那個(gè)select事務(wù)又執(zhí)行了一次查詢,要查詢id為1的記錄望艺。這時(shí)候會發(fā)生兩種情況

  • 1苛秕、已提交讀隔離級別
    會重新一個(gè)生成一個(gè)ReadView肌访,那你的活動事務(wù)列表中的值就變了,變成了[110]艇劫,通過版本鏈查trx_id對比吼驶,查到的只能是小明2。
  • 2店煞、可重復(fù)讀隔離級別
    ReadView還是第一次select時(shí)候生成的ReadView,也就是列表的值還是[100]蟹演。所以select的結(jié)果是小明1。所以第二次select結(jié)果和第一次一樣顷蟀,所以叫可重復(fù)讀酒请!

也就是說已提交讀隔離級別下的事務(wù)在每次查詢的開始都會生成一個(gè)獨(dú)立的ReadView,而可重復(fù)讀隔離級別則在第一次讀的時(shí)候生成一個(gè)ReadView,之后的讀都復(fù)用之前的ReadView鸣个。

當(dāng)前讀
需要特別注意的是在MVCC下的可重復(fù)讀在讀操作是防止了幻讀羞反,讀操作下完全就是按照ReadView進(jìn)行的快照讀。但是對于會對數(shù)據(jù)的操作例如:

select * from .... where ... for update 
select * from .... where ... lock in share mode 
update .... set .. where ... 
delete from. . where ..

都是采用當(dāng)前讀的模式囤萤。在執(zhí)行這幾個(gè)操作時(shí)會讀取最新的記錄昼窗,即使是別的事務(wù)提交的數(shù)據(jù)也可以查詢到。假設(shè)要update一條記錄涛舍,但是在另一個(gè)事務(wù)中已經(jīng)delete掉這條數(shù)據(jù)并且commit了澄惊,如果update就會產(chǎn)生沖突,所以在update的時(shí)候需要知道最新的數(shù)據(jù)富雅。如果事務(wù)中都使用快照讀掸驱,那么就不會產(chǎn)生幻讀現(xiàn)象,但是快照讀和當(dāng)前讀混用就會產(chǎn)生幻讀没佑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毕贼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子图筹,更是在濱河造成了極大的恐慌帅刀,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件远剩,死亡現(xiàn)場離奇詭異扣溺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瓜晤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門锥余,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人痢掠,你說我怎么就攤上這事驱犹〕盎校” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵雄驹,是天一觀的道長佃牛。 經(jīng)常有香客問我,道長医舆,這世上最難降的妖魔是什么俘侠? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮蔬将,結(jié)果婚禮上爷速,老公的妹妹穿的比我還像新娘。我一直安慰自己霞怀,他們只是感情好惫东,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毙石,像睡著了一般廉沮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胁黑,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天废封,我揣著相機(jī)與錄音,去河邊找鬼丧蘸。 笑死漂洋,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的力喷。 我是一名探鬼主播刽漂,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弟孟!你這毒婦竟也來了贝咙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤拂募,失蹤者是張志新(化名)和其女友劉穎庭猩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陈症,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔼水,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了录肯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趴腋。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出优炬,到底是詐尸還是另有隱情颁井,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布蠢护,位于F島的核電站雅宾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏糊余。R本人自食惡果不足惜秀又,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一单寂、第九天 我趴在偏房一處隱蔽的房頂上張望贬芥。 院中可真熱鬧,春花似錦宣决、人聲如沸蘸劈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽威沫。三九已至,卻和暖如春洼专,著一層夾襖步出監(jiān)牢的瞬間棒掠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工屁商, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烟很,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓蜡镶,卻偏偏與公主長得像雾袱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子官还,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348