数据库:MySQL相关知识整理,值得收藏!
一、數(shù)據(jù)庫引擎
選擇:MyISAM相對簡單,所以在效率上要優(yōu)于InnoDB。如果系統(tǒng)插入和查詢操作多,不需要事務(wù)和外鍵,選擇MyISAM,如果需要頻繁的更新、刪除操作,或者需要事務(wù)、外鍵、行級鎖的時候,選擇InnoDB。
二、事務(wù)
1、事務(wù)特性
原子性(Atomicity)
事務(wù)作為一個整體被執(zhí)行 ,要么全部執(zhí)行,要么全部不執(zhí)行。事務(wù)執(zhí)行過程中出錯,會回滾到事務(wù)開始前的狀態(tài),所有的操作就像沒有發(fā)生一樣。
一致性(Consistency)
事務(wù)開始前和結(jié)束后,數(shù)據(jù)庫的完整性約束沒有被破壞,執(zhí)行結(jié)果符合預(yù)期規(guī)則 。比如A向B轉(zhuǎn)賬,不可能A扣了錢,B卻沒收到。
隔離性(Isolation)
同一時間,只允許一個事務(wù)請求同一數(shù)據(jù),不同的事務(wù)之間彼此沒有任何干擾。比如A正在從一張銀行卡中取錢,在A取錢的過程結(jié)束前,B不能向這張卡轉(zhuǎn)賬。
持久性(Durability)
一個事務(wù)一旦提交,對數(shù)據(jù)庫的修改應(yīng)該永久保存,不能回滾。
2、事物的并發(fā)問題
臟讀
事務(wù)A讀取了事務(wù)B已經(jīng)修改但尚未提交的數(shù)據(jù)。若事務(wù)B回滾數(shù)據(jù),事務(wù)A的數(shù)據(jù)存在不一致性的問題,那么A讀取到的數(shù)據(jù)就是臟數(shù)據(jù)。【一致性】
不可重復(fù)讀
事務(wù)A在執(zhí)行過程中,第一次讀取到的是原始數(shù)據(jù),第二次讀取到的是事務(wù)B已經(jīng)提交的修改后的數(shù)據(jù)。導(dǎo)致兩次讀取同一數(shù)據(jù)的值不一致。不符合事務(wù)的隔離性。【隔離性】
幻讀
事務(wù)A根據(jù)相同條件第二次查詢到事務(wù)B提交的新增或刪除的數(shù)據(jù),兩次數(shù)據(jù)結(jié)果集不一致。不符合事務(wù)的隔離性。【隔離性】
小結(jié):不可重復(fù)讀的和幻讀很容易混淆,不可重復(fù)讀側(cè)重于修改,幻讀側(cè)重于新增或刪除。解決不可重復(fù)讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
3、事物的隔離級別
4、總結(jié)
事務(wù)隔離級別為讀已提交時,寫數(shù)據(jù)只會鎖住相應(yīng)的行。
事務(wù)隔離級別為可重復(fù)讀時,若檢索條件有索引(包括主鍵索引),默認(rèn)加鎖方式是next-key 鎖【間隙鎖】;若檢索條件沒有索引,則更新數(shù)據(jù)時會鎖住整張表。一個間隙被事務(wù)加了鎖,其他事務(wù)是不能在這個間隙插入記錄的,這樣可以防止幻讀。
事務(wù)隔離級別為串行化時,讀寫數(shù)據(jù)都會鎖住整張表
隔離級別越高,越能保證數(shù)據(jù)的完整性和一致性,但是對并發(fā)性能的影響也越大。
MySQL默認(rèn)隔離級別是可重復(fù)讀。
查看當(dāng)前數(shù)據(jù)庫的事務(wù)隔離級別:show variables like 'tx_isolation';
MYSQL MVCC實(shí)現(xiàn)機(jī)制
next-key 鎖【間隙鎖】
三、鎖
1、行鎖
①. 優(yōu)勢:
鎖的粒度小;
發(fā)生鎖沖突的概率低;
處理并發(fā)的能力強(qiáng)。
②. 劣勢:
開銷大;
加鎖慢;
會出現(xiàn)死鎖。
③. 加鎖方式:
自動加鎖。
對于UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及的數(shù)據(jù)集加排他鎖;對于普通SELECT語句,InnoDB不會加任何鎖;當(dāng)然我們也可以顯示的加鎖:加共享鎖:select * from tableName where ... lock in share mode 加排他鎖:select * from tableName where ... for update
④. 間隙鎖【Next-Key鎖】:
當(dāng)我們用范圍條件檢索數(shù)據(jù),并請求共享或排他鎖時,InnoDB會給符合條件的已有數(shù)據(jù)記錄的索引項加鎖;對于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做"間隙(GAP)"。InnoDB也會對這個"間隙"加鎖,這種鎖機(jī)制就是所謂的間隙鎖(Next-Key鎖)。
危害(坑):若執(zhí)行的條件范圍過大,則InnoDB會將整個范圍內(nèi)所有的索引鍵值全部鎖定,很容易對性能造成影響。
Transaction-A mysql> update innodb_lock set k=66 where id >=6; Query OK, 1 row affected (0.63 sec) mysql> commit; Transaction-B mysql> insert into innodb_lock (id,k,v) values(7,'7','7000'); Query OK, 1 row affected (18.99 sec)⑤. 排他鎖:
排他鎖,也稱寫鎖,獨(dú)占鎖,當(dāng)前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖。
# Transaction_A mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 for update; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4000 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4001' where id=4; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit; Query OK, 0 rows affected (0.04 sec) # Transaction_B mysql> select * from innodb_lock where id=4 for update; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (9.53 sec)⑥. 共享鎖:
共享鎖,也稱讀鎖,多用于判斷數(shù)據(jù)是否存在,多個讀操作可以同時進(jìn)行而不會互相影響。如果事務(wù)對讀鎖進(jìn)行修改操作,很可能會造成死鎖。如下圖所示。
# Transaction_A mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 lock in share mode; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4002' where id=4; Query OK, 1 row affected (31.29 sec) Rows matched: 1 Changed: 1 Warnings: 0 # Transaction_B mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 lock in share mode; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4002' where id=4; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction⑦. 分析行鎖定:
通過檢查InnoDB_row_lock 狀態(tài)變量分析系統(tǒng)上的行鎖的爭奪情況,命令:
show status like 'innodb_row_lock%' mysql> show status like 'innodb_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | +-------------------------------+-------+
innodb_row_lock_current_waits: 當(dāng)前正在等待鎖定的數(shù)量 innodb_row_lock_time: 從系統(tǒng)啟動到現(xiàn)在鎖定總時間長度;【非常重要的參數(shù)】 innodb_row_lock_time_avg: 每次等待所花平均時間;【非常重要的參數(shù)】 innodb_row_lock_time_max: 從系統(tǒng)啟動到現(xiàn)在等待最長的一次所花的時間;innodb_row_lock_waits: 系統(tǒng)啟動后到現(xiàn)在總共等待的次數(shù);非常重要的參數(shù),直接決定優(yōu)化的方向和策略。
⑧. 行鎖優(yōu)化:
盡可能讓所有數(shù)據(jù)檢索都通過索引來完成,避免無索引行或索引失效導(dǎo)致行鎖升級為表鎖。
盡可能避免間隙鎖帶來的性能下降,減少或使用合理的檢索范圍。
盡可能減少事務(wù)的粒度,比如控制事務(wù)大小,而從減少鎖定資源量和時間長度,從而減少鎖的競爭等,提供性能。
盡可能低級別事務(wù)隔離,隔離級別越高,并發(fā)的處理能力越低。
2、表鎖
①. 優(yōu)勢:
開銷小;
加鎖快;
無死鎖。
②. 劣勢:
鎖粒度大;
發(fā)生鎖沖突的概率高;
并發(fā)處理能力低。
③. 加鎖方式:
自動加鎖。
查詢操作(SELECT),會自動給涉及的所有表加讀鎖,更新操作(UPDATE、DELETE、INSERT),會自動給涉及的表加寫鎖。也可以顯示加鎖:
共享讀鎖:lock table tableName read; ?獨(dú)占寫鎖:lock table tableName write; ?批量解鎖:unlock tables;
④. 共享讀鎖:
對MyISAM表的讀操作(加讀鎖),不會阻塞其他進(jìn)程對同一表的讀操作,但會阻塞對同一表的寫操作。只有當(dāng)讀鎖釋放后,才能執(zhí)行其他進(jìn)程的寫操作。
Transaction-A mysql> lock table myisam_lock read; Query OK, 0 rows affected (0.00 sec) mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> select * from innodb_lock; ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES mysql> update myisam_lock set v='1001' where k='1'; ERROR 1099 (HY000): Table 'myisam_lock' was locked with a READ lock and can't be updated mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) Transaction-B mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> select * from innodb_lock; 8 rows in set (0.01 sec) mysql> update myisam_lock set v='1001' where k='1'; Query OK, 1 row affected (18.67 sec)⑤. 獨(dú)占寫鎖:
對MyISAM表的寫操作(加寫鎖),會阻塞其他進(jìn)程對同一表的讀和寫操作,只有當(dāng)寫鎖釋放后,才會執(zhí)行其他進(jìn)程的讀寫操作。
Transaction-A mysql> set autocommit=0; Query OK, 0 rows affected (0.05 sec) mysql> lock table myisam_lock write; Query OK, 0 rows affected (0.03 sec) mysql> update myisam_lock set v='2001' where k='2'; Query OK, 1 row affected (0.00 sec) mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> update innodb_lock set v='1001' where k='1'; ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) Transaction-B mysql> select * from myisam_lock; 9 rows in set (42.83 sec)小結(jié):表鎖,讀鎖會阻塞寫,不會阻塞讀。而寫鎖則會把讀、寫都阻塞。
⑥. 查看加鎖情況:
show open tables; 1表示加鎖,0表示未加鎖。
mysql> show open tables where in_use > 0; +----------+-------------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------------+--------+-------------+ | lock | myisam_lock | 1 | 0 | +----------+-------------+--------+-------------+⑦. 分析表鎖定:
通過檢查table_locks_waited 和 table_locks_immediate 狀態(tài)變量分析系統(tǒng)上的表鎖定,命令:
show status like 'table_locks%'; mysql> show status like 'table_locks%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Table_locks_immediate | 104 | | Table_locks_waited | 0 | +----------------------------+-------+table_locks_immediate: 表示立即釋放表鎖數(shù)。
table_locks_waited: 表示需要等待的表鎖數(shù)。此值越高則說明存在著越嚴(yán)重的表級鎖爭用情況。
此外,MyISAM的讀寫鎖調(diào)度是寫優(yōu)先,這也是MyISAM不適合做寫為主的存儲引擎。因?yàn)閷戞i后,其他線程不能做任何操作,大量的更新會使查詢很難得到鎖,從而造成永久阻塞。
3、什么情況下用表鎖?
InnoDB默認(rèn)采用行鎖,在未使用索引字段查詢時升級為表鎖。MySQL這樣設(shè)計并不是給你挖坑。它有自己的設(shè)計目的。?即便你在條件中使用了索引字段,MySQL會根據(jù)自身的執(zhí)行計劃,考慮是否使用索引(所以explain命令中會有possible_key 和 key)。如果MySQL認(rèn)為全表掃描效率更高,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖。因此,在分析鎖沖突時,別忘了檢查SQL的執(zhí)行計劃,以確認(rèn)是否真正使用了索引。
第一種情況:全表更新。事務(wù)需要更新大部分或全部數(shù)據(jù),且表又比較大。若使用行鎖,會導(dǎo)致事務(wù)執(zhí)行效率低,從而可能造成其他事務(wù)長時間鎖等待和更多的鎖沖突。
第二種情況:多表查詢。事務(wù)涉及多個表,比較復(fù)雜的關(guān)聯(lián)查詢,很可能引起死鎖,造成大量事務(wù)回滾。這種情況若能一次性鎖定事務(wù)涉及的表,從而可以避免死鎖、減少數(shù)據(jù)庫因事務(wù)回滾帶來的開銷。
4、總結(jié)
InnoDB 支持表鎖和行鎖,使用索引作為檢索條件修改數(shù)據(jù)時采用行鎖,否則采用表鎖。
InnoDB 自動給寫操作加鎖,讀操作不自動加鎖。
行鎖可能因?yàn)槲词褂盟饕墳楸礞i,所以除了檢查索引是否創(chuàng)建的同時,也需要通過explain執(zhí)行計劃查詢索引是否被實(shí)際使用。
行鎖相對于表鎖來說,優(yōu)勢在于高并發(fā)場景下表現(xiàn)更突出,畢竟鎖的粒度小。
當(dāng)表的大部分?jǐn)?shù)據(jù)需要被修改,或者是多表復(fù)雜關(guān)聯(lián)查詢時,建議使用表鎖優(yōu)于行鎖。
為了保證數(shù)據(jù)的一致完整性,任何一個數(shù)據(jù)庫都存在鎖定機(jī)制。鎖定機(jī)制的優(yōu)劣直接影響到一個數(shù)據(jù)庫的并發(fā)處理能力和性能。
四、索引
1、原理
我們拿出一本新華字典,它的目錄實(shí)際上就是一種索引:非聚集索引。我們可以通過目錄迅速定位我們要查的字。而字典的內(nèi)容部分一般都是按照拼音排序的,這實(shí)際上又是一種索引:聚集索引。聚集索引這種實(shí)現(xiàn)方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然后用主鍵到主索引中檢索獲得記錄。
2、底層實(shí)現(xiàn)【數(shù)據(jù)結(jié)構(gòu)】
①、mysql主要使用B+樹來構(gòu)建索引,為什么不用二叉樹和紅黑樹?
B+樹是多叉的,可以減少樹的高度。
索引本身較大,不會全部存儲在內(nèi)存中,會以索引文件的形式存儲在磁盤上,所以索引在查找數(shù)據(jù)的過程中會涉及到磁盤I/O操作。
因磁盤I/O效率低下,mysql為了盡量減少磁盤IO的存取次數(shù),需要利用了磁盤存取的局部性原理進(jìn)行磁盤預(yù)讀。
局部性原理:為了減少磁盤IO,磁盤往往會進(jìn)行數(shù)據(jù)預(yù)讀,會從某位置開始,預(yù)先順序向后讀取一定長度的數(shù)據(jù)放入內(nèi)存。因?yàn)榇疟P順序讀取的效率較高,不需要尋道時間,因此可以提高IO效率。
磁盤預(yù)讀長度一般為頁的整數(shù)倍,主存和磁盤以頁作為單位交換數(shù)據(jù)。當(dāng)需要讀取的數(shù)據(jù)不在內(nèi)存時,觸發(fā)缺頁中斷,系統(tǒng)會向磁盤發(fā)出讀取磁盤數(shù)據(jù)的請求,磁盤找到數(shù)據(jù)的起始位置并向后連續(xù)讀取一頁或幾頁數(shù)據(jù)載入內(nèi)存,然后中斷返回,系統(tǒng)繼續(xù)運(yùn)行。
mysql將B+數(shù)的一個節(jié)點(diǎn)的大小設(shè)為一個頁,這樣每個節(jié)點(diǎn)只需要一次I/O就可以完全載入內(nèi)存【由于節(jié)點(diǎn)中有若干個數(shù)組,所以地址連續(xù)】。
紅黑樹的結(jié)構(gòu)深度更深,很多邏輯上很近的節(jié)點(diǎn)(如父子節(jié)點(diǎn))在物理上可能很遠(yuǎn),無法利用局部性原理。
在InnoDB里,每個頁默認(rèn)16KB,假設(shè)索引的是8B的long型數(shù)據(jù),每個key后有個頁號4B,還有6B的其他數(shù)據(jù)(參考《MySQL技術(shù)內(nèi)幕:InnoDB存儲引擎》P193的頁面數(shù)據(jù)),那么每個頁的扇出系數(shù)為16KB/(8B+4B+6B)≈1000,即每個頁可以索引1000個key。在高度h=3時,s=1000^3=10億!!也就是說,InnoDB通過三次索引頁的I/O,即可索引10億的key。通常來說,索引樹的高度在2~4。
②. B+Tree與B-Tree的區(qū)別
M階B-Tree
定義:
樹中每個結(jié)點(diǎn)至多有m個孩子;
除根結(jié)點(diǎn)和葉子結(jié)點(diǎn)外,其它每個結(jié)點(diǎn)至少有m/2個孩子;
若根結(jié)點(diǎn)不是葉子結(jié)點(diǎn),則至少有2個孩子;
所有葉子結(jié)點(diǎn)都在同一層;
特性:
關(guān)鍵字集合分布在整顆樹中;
任何一個關(guān)鍵字出現(xiàn)且只出現(xiàn)在一個結(jié)點(diǎn)中;
搜索有可能在非葉子結(jié)點(diǎn)結(jié)束;
其搜索性能等價于在關(guān)鍵字全集內(nèi)做一次二分查找;
自動層次控制;
M階B+Tree
定義:
有m個子樹的節(jié)點(diǎn)包含有m個元素(B-Tree中是m-1);
非葉子節(jié)點(diǎn)不保存數(shù)據(jù),只用于索引,所有數(shù)據(jù)都保存在葉子節(jié)點(diǎn)中。
所有分支節(jié)點(diǎn)和根節(jié)點(diǎn)都同時存在于子節(jié)點(diǎn)中,在子節(jié)點(diǎn)元素中是最大或者最小的元素。
葉子節(jié)點(diǎn)會包含所有的關(guān)鍵字,以及指向數(shù)據(jù)記錄的指針,并且葉子節(jié)點(diǎn)本身是根據(jù)關(guān)鍵字的大小從小到大順序鏈接。
特性:
所有關(guān)鍵字都出現(xiàn)在葉子結(jié)點(diǎn)的鏈表中(稠密索引),且鏈表中的關(guān)鍵字是有序的;
不可能在非葉子結(jié)點(diǎn)命中,因?yàn)榉侨~子節(jié)點(diǎn)只有Key,沒有Data;
非葉子結(jié)點(diǎn)相當(dāng)于是葉子結(jié)點(diǎn)的索引(稀疏索引),葉子結(jié)點(diǎn)相當(dāng)于是存儲所有關(guān)鍵字?jǐn)?shù)據(jù)的數(shù)據(jù)層;
更適合文件索引系統(tǒng)。
③. B+Tree與B-Tree的區(qū)別
B+Tree有n棵子樹的結(jié)點(diǎn)中含有n個關(guān)鍵字;(而B樹是n棵子樹有n-1個關(guān)鍵字)。
B+Tree所有Key(關(guān)鍵字)存儲在葉子節(jié)點(diǎn),非葉子節(jié)點(diǎn)不存儲真正的data(數(shù)據(jù))。
B+Tree為所有葉子節(jié)點(diǎn)增加了一個鏈指針,且所有葉子節(jié)點(diǎn)的關(guān)鍵字按從小到大順序鏈接,增強(qiáng)了區(qū)間訪問性。
④. 為什么mysql的索引使用B+樹而不是B樹呢?
B+樹更適合外部存儲(一般指磁盤存儲),由于內(nèi)節(jié)點(diǎn)(非葉子節(jié)點(diǎn))不存data(數(shù)據(jù))只存Key(關(guān)鍵字),所以B+樹一個節(jié)點(diǎn)可以存儲更多的Key,即每個節(jié)點(diǎn)能索引的范圍更大更精確。也就是說使用B+樹單次磁盤I/O的信息量相比較B樹更大,I/O效率更高。
mysql是關(guān)系型數(shù)據(jù)庫,經(jīng)常會按照區(qū)間來訪問某個索引列,B+樹的葉子節(jié)點(diǎn)間按Key的順序建立了鏈指針,加強(qiáng)了區(qū)間訪問性,所以B+樹對索引列上的區(qū)間范圍查詢很友好。而B樹每個節(jié)點(diǎn)的key和data在一起,無法進(jìn)行區(qū)間查找。
3、索引優(yōu)點(diǎn)
快速讀取數(shù)據(jù)。
唯一性索引能保證數(shù)據(jù)記錄的唯一性。
實(shí)現(xiàn)表與表之間的參照完整性【通過主鍵索引】。
4、索引缺點(diǎn)
索引需要占用物理空間。
當(dāng)對表中的數(shù)據(jù)進(jìn)行增加、刪除和修改的時候,索引也要動態(tài)的維護(hù),降低了數(shù)據(jù)的維護(hù)速度。
五、mysql性能分析
1、MySQL 自身瓶頸
MySQL自身常見的性能問題有磁盤空間不足,磁盤I/O太大,服務(wù)器硬件性能低。
CPU瓶頸:CPU 飽和一般發(fā)生在數(shù)據(jù)裝入內(nèi)存或從磁盤上讀取數(shù)據(jù)的時候。
IO瓶頸:磁盤I/O 瓶頸發(fā)生在裝入數(shù)據(jù)遠(yuǎn)大于內(nèi)存容量的時候。
服務(wù)器硬件的性能瓶頸:可通過top,free,iostat 和 vmstat來查看系統(tǒng)的性能狀態(tài)。
2、explain 分析sql語句
使用explain關(guān)鍵字可以模擬優(yōu)化器執(zhí)行sql查詢語句,從而得知MySQL 是如何處理sql語句。
①. id
select 查詢的序列號,包含一組可以重復(fù)的數(shù)字,表示查詢中sql語句的執(zhí)行順序。一般有三種情況:第一種:id全部相同,sql的執(zhí)行順序是由上至下;第二種:id全部不同,sql的執(zhí)行順序是根據(jù)id大的優(yōu)先執(zhí)行(如果是子查詢,id的序號會遞增);第三種:id既存在相同,又存在不同的。先根據(jù)id大的優(yōu)先執(zhí)行,再根據(jù)相同id從上至下的執(zhí)行。
②. select_type
select 查詢的類型,主要是用于區(qū)別普通查詢,聯(lián)合查詢,嵌套的復(fù)雜查詢:
simple:簡單的select 查詢,查詢中不包含子查詢或者union。
primary:查詢中若包含任何復(fù)雜的子查詢,最外層查詢則被標(biāo)記為primary。
subquery:在select或where 列表中包含了子查詢。
derived:在from列表中包含的子查詢被標(biāo)記為derived(衍生),MySQL會遞歸執(zhí)行這些子查詢,把結(jié)果放在臨時表里。
union:若第二個select出現(xiàn)在union之后,則被標(biāo)記為union【聯(lián)合查詢】,若union包含在from子句的子查詢中,外層select將被標(biāo)記為:derived。
union result:從union表獲取結(jié)果的select。
subquery和union 還可以被標(biāo)記為dependent和uncacheable。dependent意味著select依賴于外層查詢中發(fā)現(xiàn)的數(shù)據(jù)。uncacheable意味著select中的某些特性阻止結(jié)果被緩存于一個item_cache中。
③. table
查詢結(jié)果來自于哪個表。
④. partitions
表所使用的分區(qū),如果要統(tǒng)計十年公司訂單的金額,可以把數(shù)據(jù)分為十個區(qū),每一年代表一個區(qū)。這樣可以大大的提高查詢效率。
⑤. type
這是一個非常重要的參數(shù),連接類型,常見的有:all , index , range , ref , eq_ref , const , system , null 八個級別。
性能從最優(yōu)到最差的排序:null > system > const > eq_ref > ref > range > index > all
對java程序員來說,若保證查詢至少達(dá)到range級別或者最好能達(dá)到ref則算是一個優(yōu)秀而又負(fù)責(zé)的程序員。
all:(full table scan)全表掃描無疑是最差,若是百萬千萬級數(shù)據(jù)量,全表掃描會非常慢。
index:(full index scan)全索引文件掃描比all好很多,畢竟從索引樹中找數(shù)據(jù),比從全表中找數(shù)據(jù)要快。
range:只檢索給定范圍的行,使用索引來匹配行。范圍縮小了,當(dāng)然比全表掃描和全索引文件掃描要快。sql語句中一般會有between,>,< 等查詢,IN()和OR列表,也會顯示range。
ref:非唯一性索引掃描,本質(zhì)上也是一種索引訪問,返回所有匹配某個單獨(dú)值的行。比如查詢公司所有屬于研發(fā)團(tuán)隊的同事,匹配的結(jié)果是多個并非唯一值。
eq_ref:唯一性索引掃描,對于每個索引鍵,表中有一條記錄與之匹配。比如用主鍵或唯一字段作為判斷條件。
const:表示通過索引一次就可以找到,const用于比較primary key 或者unique索引。因?yàn)橹黄ヅ湟恍袛?shù)據(jù),所以很快,若將主鍵至于where列表中,MySQL就能將該查詢轉(zhuǎn)換為一個常量。
system:表只有一條記錄(等于系統(tǒng)表),這是const類型的特列,平時不會出現(xiàn),了解即可。
NULL:MySQL在優(yōu)化過程中分解語句,執(zhí)行時甚至不用訪問表或索引,例如從一個索引列里選取最小值可以通過單獨(dú)索引查找完成。
⑥. possible_keys
顯示查詢語句可能用到的索引(即查詢涉及字段中存在索引的字段,可能為一個、多個或?yàn)閚ull),不一定被查詢實(shí)際使用,僅供參考使用。
⑦. key
顯示查詢語句實(shí)際使用的索引字段。若為null,則表示沒有使用索引。
⑧. key_len
顯示索引中使用的字節(jié)數(shù),可通過key_len計算查詢中使用的索引長度。
在不損失精確性的情況下索引長度越短越好。key_len 顯示的值為索引字段的最可能長度,并非實(shí)際使用長度,即key_len是根據(jù)表定義計算而得,并不是通過表內(nèi)檢索出的。
⑨. ref
表示上述表的連接匹配條件,即哪些列或常量被用于查找索引列上的值。即顯示使用哪個列或常數(shù)與key一起從表中選擇行。
**⑩. rows
根據(jù)表統(tǒng)計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數(shù),值越大越不好。
即根據(jù)查詢語句及索引選用情況,大致估算出要得到查詢結(jié)果,所需要在表中讀取的行數(shù)。
?. filtered
一個百分比的值,和rows 列的值一起使用,可以估計出查詢執(zhí)行計劃(QEP)中的前一個表的結(jié)果集,從而確定join操作的循環(huán)次數(shù)。小表驅(qū)動大表,減輕連接的次數(shù)。
?. extra
包含不適合在其他列中顯示但又十分重要的額外信息。
Using filesort:?“文件排序”,說明MySQL中無法利用索引完成的排序操作 ,MySQL會對數(shù)據(jù)使用一個外部的索引排序,而不是按照表內(nèi)的索引順序進(jìn)行讀取。出現(xiàn)這個就要立刻優(yōu)化sql。
Using temporary:使用了臨時表保存中間結(jié)果,說明MySQL在對查詢結(jié)果排序時使用臨時表。常見于排序 order by 和 分組查詢 group by時,需要借助輔助表再進(jìn)行排序的情況(這種情況是多個表都涉及到排序字段才會引起的)。出現(xiàn)這個更要立刻優(yōu)化sql。
Using index:表示相應(yīng)的select 操作中使用了覆蓋索引(Covering index),避免訪問了表的數(shù)據(jù)行,效果不錯!如果同時出現(xiàn)Using where,表明索引被用來執(zhí)行索引鍵值的查找。如果沒有同時出現(xiàn)Using where,表示索引只是用來讀取數(shù)據(jù)而非執(zhí)行查找動作。覆蓋索引(Covering Index) :也叫索引覆蓋,就是select 的數(shù)據(jù)列只用從索引中就能夠取得,不必讀取數(shù)據(jù)行,MySQL可以利用索引返回select 列表中的字段,而不必根據(jù)索引再次讀取數(shù)據(jù)文件。
Using index condition:在5.6版本后加入的新特性,優(yōu)化器會在索引存在的情況下,通過符合RANGE范圍的條數(shù) 和 總數(shù)的比例來選擇是使用索引還是進(jìn)行全表遍歷。
Using where:表明使用了where 過濾。
Using join buffer:表明使用了連接緩存。
impossible where:where 語句的值總是false,不可用,不能用來獲取任何元素。
distinct:優(yōu)化distinct操作,在找到第一匹配的元組后即停止找同樣值的動作。
3、explain總結(jié)
通過explain的參數(shù)介紹,我們可以得知:
sql的查詢順序(根據(jù)id,id越大越先執(zhí)行)。
數(shù)據(jù)讀取操作的操作類型(type)
哪些索引被實(shí)際使用(key)
表之間的引用(ref)
每張表有多少行被優(yōu)化器查詢(rows)
六、數(shù)據(jù)庫性能優(yōu)化
1、優(yōu)化思路
可從以下幾個方面對數(shù)據(jù)庫性能進(jìn)行優(yōu)化:
優(yōu)化數(shù)據(jù)庫與索引的設(shè)計。
優(yōu)化SQL語句。
加緩存【Memcached, Redis】
主從復(fù)制,讀寫分離。
垂直拆分,其實(shí)就是根據(jù)你模塊的耦合度,將一個包含多個字段的表分成多個小的表,將一個大的系統(tǒng)分為多個小的系統(tǒng),也就是分布式系統(tǒng)。
水平切分,針對數(shù)據(jù)量大的表,這一步最麻煩,最能考驗(yàn)技術(shù)水平,要選擇一個合理的sharding key,為了有好的查詢效率,表結(jié)構(gòu)也要改動,做一定的冗余,應(yīng)用也要改,sql中盡量帶sharding key,將數(shù)據(jù)定位到限定的表上去查,而不是掃描全部的表;
2、數(shù)據(jù)庫與索引設(shè)計
①. 數(shù)據(jù)庫設(shè)計
表字段避免null值出現(xiàn),null值很難進(jìn)行查詢優(yōu)化且占用額外的索引空間,推薦默認(rèn)數(shù)字0代替null。
盡量使用INT而非BIGINT,如果非負(fù)則加上UNSIGNED(這樣數(shù)值容量會擴(kuò)大一倍),當(dāng)然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
使用枚舉或整數(shù)代替字符串類型。
盡量使用TIMESTAMP而非DATETIME。
單表不要有太多字段,建議在20以內(nèi)。
用整型來存IP。【可去搜索IP地址轉(zhuǎn)為整型】
等。
②. 索引設(shè)計
索引要占用物理內(nèi)存,并不是越多越好,要根據(jù)查詢有針對性的創(chuàng)建,考慮在WHERE和ORDER BY命令上涉及的列建立索引,可根據(jù)EXPLAIN來查看是否用了索引還是全表掃描。
應(yīng)盡量避免在WHERE子句中對字段進(jìn)行NULL值判斷,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。
值分布很稀少的字段不適合建索引,例如"性別"這種只有兩三個值的字段。
字符字段只建前綴索引。
字符字段最好不要做主鍵。
不用外鍵,由程序保證約束。
盡量不用UNIQUE,由程序保證約束。
使用多列索引時主意順序和查詢條件保持一致,同時刪除不必要的單列索引。
③. 總結(jié)
就一句話:使用合適的數(shù)據(jù)類型,選擇合適的索引:
使用合適的數(shù)據(jù)類型
使用可存下數(shù)據(jù)的最小的數(shù)據(jù)類型,整型 < date,time < char,varchar < blob
使用簡單的數(shù)據(jù)類型,整型比字符處理開銷更小,因?yàn)樽址谋容^更復(fù)雜。如,int類型存儲時間類型,bigint類型轉(zhuǎn)ip函數(shù)。
使用合理的字段屬性長度,固定長度的表會更快。使用enum、char而不是varchar。
盡可能使用not null定義字段。
盡量少用text,非用不可最好分表。
選擇合適的索引列(即哪些列適合添加索引)
查詢頻繁的列,在where,group by,order by,on從句中出現(xiàn)的列。
where條件中<,<=,=,>,>=,between,in,以及l(fā)ike 字符串+通配符(%)出現(xiàn)的列。
長度小的列,索引字段越小越好,因?yàn)閿?shù)據(jù)庫的存儲單位是頁,一頁中能存下的數(shù)據(jù)越多越好。
離散度大(不同的值多)的列,放在聯(lián)合索引前面。查看離散度,通過統(tǒng)計不同的列值來實(shí)現(xiàn),count越大,離散程度越高。
3、sql優(yōu)化
使用limit對查詢結(jié)果的記錄進(jìn)行限定。
*避免select ,將需要查找的字段列出來。
使用連接(join)來代替子查詢。
拆分大的delete或insert語句。
可通過開啟慢查詢?nèi)罩緛碚页鲚^慢的SQL。如何開啟mysql慢查詢?nèi)罩?#xff1f;
不做列運(yùn)算:SELECT id WHERE age + 1 = 10,任何對列的操作都將導(dǎo)致表掃描,它包括數(shù)據(jù)庫教程函數(shù)、計算表達(dá)式等等,查詢時要盡可能將操作移至等號右邊。
sql語句盡可能簡單:一條sql只能在一個cpu運(yùn)算;大語句拆小語句,減少鎖時間;一條大sql可以堵死整個庫。
OR改寫成IN:OR的效率是O(n)級別,IN的效率是O(logn)級別,in的個數(shù)建議控制在200以內(nèi)。
不用函數(shù)和觸發(fā)器,在應(yīng)用程序?qū)崿F(xiàn)。
避免%xxx式查詢。
少用JOIN。
使用同類型進(jìn)行比較,比如用'123'和'123'比,123和123比。
盡量避免在WHERE子句中使用 != 或 <> 操作符,否則導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。
對于連續(xù)數(shù)值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5。
列表數(shù)據(jù)不要拿全表,要使用LIMIT來分頁,每頁數(shù)量也不要太大。
4、分區(qū)、分庫、分表
①. 分區(qū)
把一張表的數(shù)據(jù)分成N個區(qū)塊,在邏輯上看最終只是一張表,但底層是由N個物理區(qū)塊組成的,通過將不同數(shù)據(jù)按一定規(guī)則放到不同的區(qū)塊中提升表的查詢效率。
②. 分表
水平分表:為了解決單表數(shù)據(jù)量過大(數(shù)據(jù)量達(dá)到千萬級別)問題。所以將固定的ID hash之后mod,取若0~N個值,然后將數(shù)據(jù)劃分到不同表中,需要在寫入與查詢的時候進(jìn)行ID的路由與統(tǒng)計。
垂直分表:為了解決表的寬度問題,同時還能分別優(yōu)化每張單表的處理能力。所以將表結(jié)構(gòu)根據(jù)數(shù)據(jù)的活躍度拆分成多個表,把不常用的字段單獨(dú)放到一個表、把大字段單獨(dú)放到一個表、把經(jīng)常使用的字段放到一個表。
③. 分庫
面對高并發(fā)的讀寫訪問,當(dāng)數(shù)據(jù)庫無法承載寫操作壓力時,不管如何擴(kuò)展slave服務(wù)器,此時都沒有意義了。因此需對數(shù)據(jù)庫進(jìn)行拆分,從而提高數(shù)據(jù)庫寫入能力,這就是分庫。
④. 問題
事務(wù)問題。在執(zhí)行分庫之后,由于數(shù)據(jù)存儲到了不同的庫上,數(shù)據(jù)庫事務(wù)管理出現(xiàn)了困難。如果依賴數(shù)據(jù)庫本身的分布式事務(wù)管理功能去執(zhí)行事務(wù),將付出高昂的性能代價;如果由應(yīng)用程序去協(xié)助控制,形成程序邏輯上的事務(wù),又會造成編程方面的負(fù)擔(dān)。
跨庫跨表的join問題。在執(zhí)行了分庫分表之后,難以避免會將原本邏輯關(guān)聯(lián)性很強(qiáng)的數(shù)據(jù)劃分到不同的表、不同的庫上,我們無法join位于不同分庫的表,也無法join分表粒度不同的表,結(jié)果原本一次查詢能夠完成的業(yè)務(wù),可能需要多次查詢才能完成。
額外的數(shù)據(jù)管理負(fù)擔(dān)和數(shù)據(jù)運(yùn)算壓力。額外的數(shù)據(jù)管理負(fù)擔(dān),最顯而易見的就是數(shù)據(jù)的定位問題和數(shù)據(jù)的增刪改查的重復(fù)執(zhí)行問題,這些都可以通過應(yīng)用程序解決,但必然引起額外的邏輯運(yùn)算。
七、緩存
對于很多的數(shù)據(jù)庫系統(tǒng)都能夠緩存執(zhí)行計劃,對于完全相同的sql, 可以使用已經(jīng)已經(jīng)存在的執(zhí)行計劃,從而跳過解析和生成執(zhí)行計劃的過程。MYSQL提供了更為高級的查詢結(jié)果緩存功能,對于完全相同的SQL (字符串完全相同且大小寫敏感) 可以執(zhí)行返回查詢結(jié)果。
MySQL緩存機(jī)制簡單的說就是緩存sql文本及查詢結(jié)果,如果運(yùn)行相同的sql,服務(wù)器直接從緩存中取到結(jié)果,而不需要再去解析和執(zhí)行sql。如果表更改了,那么使用這個表的所有緩沖查詢將不再有效,查詢緩存值的相關(guān)條目被清空。更改指的是表中任何數(shù)據(jù)或是結(jié)構(gòu)的改變,包括INSERT、UPDATE、 DELETE、TRUNCATE(截斷)、ALTER TABLE、DROP TABLE或DROP DATABASE等,也包括那些映射到改變了的表的使用MERGE表的查詢。顯然,這對于頻繁更新的表,查詢緩存是不適合的,而對于一些不常改變數(shù)據(jù)且有 大量相同sql查詢的表,查詢緩存會節(jié)約很大的性能。
八、面試真題
問:有個表特別大,字段是姓名、年齡、班級,如果調(diào)用select * from table where name = xxx and age = xxx該如何通過建立索引的方式優(yōu)化查詢速度?
答:由于mysql查詢每次只能使用一個索引,如果在name、age兩列上創(chuàng)建復(fù)合索引的話將帶來更高的效率。如果我們創(chuàng)建了(name, age)的復(fù)合索引,那么其實(shí)相當(dāng)于創(chuàng)建了(name)、(name, age)兩個索引,這被稱為最佳左前綴特性。因此我們在創(chuàng)建復(fù)合索引時應(yīng)該將最常用作限制條件的列放在最左邊,依次遞減。其次還要考慮該列的數(shù)據(jù)離散程度,如果有很多不同的值的話建議放在左邊,name的離散程度也大于age。
問:max(xxx)如何用索引優(yōu)化?
答:在xxx列上建立索引,因?yàn)樗饕荁+樹順序排列的,鎖在下次查詢的時候就會使用索引來查詢到最大的值是哪個。
問:如何對分頁進(jìn)行優(yōu)化?
答:SELECT * FROM big_table order by xx LIMIT 1000000,20,這條語句會查詢出1000020條的所有數(shù)據(jù)然后丟棄掉前1000000條,為了避免全表掃描的操作,在order by的列上加索引就能通過掃描索引來查詢。但是這條語句會查詢還是會掃描1000020條,還能改進(jìn)成select id from big_table where id >= 1000000 order by xx LIMIT 0,20,用ID作為過濾條件將不需要查詢的數(shù)據(jù)直接去除。
IT技術(shù)分享社區(qū)
個人博客網(wǎng)站:https://programmerblog.xyz
文章推薦程序員效率:畫流程圖常用的工具程序員效率:整理常用的在線筆記軟件遠(yuǎn)程辦公:常用的遠(yuǎn)程協(xié)助軟件,你都知道嗎?51單片機(jī)程序下載、ISP及串口基礎(chǔ)知識硬件:斷路器、接觸器、繼電器基礎(chǔ)知識
總結(jié)
以上是生活随笔為你收集整理的数据库:MySQL相关知识整理,值得收藏!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux操作系统启动流程简单介绍
- 下一篇: mysql 5.7.4 m14_win7