不少時候,當你的應用程序進行SQL查詢速度很慢時,應該想一想是否能夠建索引。mysql
大多數MySQL索引(PRIMARY KEY、UNIQUE、INDEX和FULLTEXT)在B樹中存儲。只是空間列類型的索引使用R-樹,而且MEMORY表還支持hash索引。正則表達式
索引是一個排序的列表,在這個列表中存儲着索引的值和包含這個值的數據所在行的物理地址,在數據十分龐大的時候,索引能夠大大加快查詢的速度,這是由於使用索引後能夠不用掃描全表來定位某行的數據,而是先經過索引表找到該行數據對應的物理地址而後訪問相應的數據。算法
優點:能夠快速檢索,減小I/O次數,加快檢索速度;根據索引分組和排序,能夠加快分組和排序;sql
劣勢:索引自己也是表,所以會佔用存儲空間,通常來講,索引表佔用的空間的數據表的1.5倍;索引表的維護和建立須要時間成本,這個成本隨着數據量增大而增大;構建索引會下降數據表的修改操做(刪除,添加,修改)的效率,由於在修改數據表的同時還須要修改索引表;數據庫
常見的索引類型有:主鍵索引、惟一索引、普通索引、全文索引、組合索引bash
一、主鍵索引:即主索引,根據主鍵pk_clolum(length)創建索引,不容許重複,不容許空值;服務器
ALTER TABLE 'table_name' ADD PRIMARY KEY('id');
複製代碼
二、惟一索引:用來創建索引的列的值必須是惟一的,容許空值網絡
ALTER TABLE 'table_name' ADD UNIQUE('email');
複製代碼
三、普通索引:用表中的普通列構建的索引,沒有任何限制多線程
ALTER TABLE 'table_name' ADD INDEX index_name('description');
複製代碼
四、全文索引:用大文本對象的列構建的索引(下一部分會講解)併發
ALTER TABLE 'table_name' ADD FULLTEXT('content');
複製代碼
五、組合索引:用多個列組合構建的索引,這多個列中的值不容許有空值
ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3');
複製代碼
遵循「最左前綴」原則,把最經常使用做爲檢索或排序的列放在最左,依次遞減,組合索引至關於創建了col1,col1col2,col1col2col3三個索引,而col2或者col3是不能使用索引的。
在使用組合索引的時候可能由於列名長度過長而致使索引的key太大,致使效率下降,在容許的狀況下,能夠只取col1和col2的前幾個字符做爲索引
ALTER TABLE 'table_name' ADD INDEX index_name(col1(4),col2(3));
複製代碼
表示使用col1的前4個字符和col2的前3個字符做爲索引
MySQL支持諸多存儲引擎,而各類存儲引擎對索引的支持也各不相同,所以MySQL數據庫支持多種索引類型,如BTree索引,B+Tree索引,哈希索引,全文索引等等,
只有memory(內存)存儲引擎支持哈希索引,哈希索引用索引列的值計算該值的hashCode,而後在hashCode相應的位置存執該值所在行數據的物理位置,由於使用散列算法,所以訪問速度很是快,可是一個值只能對應一個hashCode,並且是散列的分佈方式,所以哈希索引不支持範圍查找和排序的功能。
FULLTEXT(全文)索引,僅可用於MyISAM和InnoDB,針對較大的數據,生成全文索引很是的消耗時間和空間。對於文本的大對象,或者較大的CHAR類型的數據,若是使用普通索引,那麼匹配文本前幾個字符仍是可行的,可是想要匹配文本中間的幾個單詞,那麼就要使用LIKE %word%來匹配,這樣須要很長的時間來處理,響應時間會大大增長,這種狀況,就可以使用時FULLTEXT索引了,在生成FULLTEXT索引時,會爲文本生成一份單詞的清單,在索引時及根據這個單詞的清單來索引。FULLTEXT能夠在建立表的時候建立,也能夠在須要的時候用ALTER或者CREATE INDEX來添加:
//建立表的時候添加FULLTEXT索引
CTREATE TABLE my_table(
id INT(10) PRIMARY KEY,
name VARCHAR(10) NOT NULL,
my_text text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
FULLTEXT(my_text));
//建立表之後,在須要的時候添加FULLTEXT索引
ALTER my_table ADD FULLTEXT ft_index(my_text);
CREATE INDEX ft_index ON my_table(my_text);
複製代碼
對於較大的數據集,把數據添加到一個沒有FULLTEXT索引的表,而後添加FULLTEXT索引的速度比把數據添加到一個已經有FULLTEXT索引的錶快。
MySQL自帶的全文索引只能用於MyISAM存儲引擎,若是是其它數據引擎,那麼全文索引不會生效。
在MySQL中,全文索引支隊英文有用,目前對中文還不支持。
在MySQL中,若是檢索的字符串過短則沒法檢索獲得預期的結果,檢索的字符串長度至少爲4字節,此外,若是檢索的字符包括中止詞,那麼中止詞會被忽略。
BTree是平衡搜索多叉樹,設樹的度爲d(d>1),高度爲h,那麼BTree要知足以一下條件:
每一個葉子結點的高度同樣,等於h;
每一個非葉子結點由n-1個key和n個指針point組成,其中d<=n<=2d,key和point相互間隔,結點兩端必定是key;
葉子結點指針都爲null;
非葉子結點的key都是[key,data]二元組,其中key表示做爲索引的鍵,data爲鍵值所在行的數據;
BTree的結構以下:
在BTree的機構下,就可使用二分查找的查找方式,查找複雜度爲h*log(n),通常來講樹的高度是很小的,通常爲3左右,所以BTree是一個很是高效的查找結構。
B+Tree是BTree的一個變種,設d爲樹的度數,h爲樹的高度,B+Tree和BTree的不一樣主要在於:
B+Tree中的非葉子結點不存儲數據,只存儲鍵值;
B+Tree的葉子結點沒有指針,全部鍵值都會出如今葉子結點上,且key存儲的鍵值對應的數據的物理地址;
B+Tree的結構以下:
通常來講B+Tree比BTree更適合實現外存的索引結構,由於存儲引擎的設計專家巧妙的利用了外存(磁盤)的存儲結構,即磁盤的一個扇區是整數倍的page(頁),頁是存儲中的一個單位,一般默認爲4K,所以索引結構的節點被設計爲一個頁的大小,而後利用外存的「預讀取」原則,每次讀取的時候,把整個節點的數據讀取到內存中,而後在內存中查找,已知內存的讀取速度是外存讀取I/O速度的幾百倍,那麼提高查找速度的關鍵就在於儘量少的磁盤I/O,那麼能夠知道,每一個節點中的key個數越多,那麼樹的高度越小,須要I/O的次數越少,所以通常來講B+Tree比BTree更快,由於B+Tree的非葉節點中不存儲data,就能夠存儲更多的key。
不少存儲引擎在B+Tree的基礎上進行了優化,添加了指向相鄰葉節點的指針,造成了帶有順序訪問指針的B+Tree,這樣作是爲了提升區間查找的效率,只要找到第一個值那麼就能夠順序的查找後面的值。
B+Tree的結構以下:
分析了MySQL的索引結構的實現原理,而後咱們來看看具體的存儲引擎怎麼實現索引結構的,MySQL中最多見的兩種存儲引擎分別是MyISAM和InnoDB,分別實現了非聚簇索引和聚簇索引。
首先要介紹幾個概念,在索引的分類中,咱們能夠按照索引的鍵是否爲主鍵來分爲「主索引」和「輔助索引」,使用主鍵鍵值創建的索引稱爲「主索引」,其它的稱爲「輔助索引」。所以主索引只能有一個,輔助索引能夠有不少個。
MyISAM存儲引擎採用的是非聚簇索引,非聚簇索引的主索引和輔助索引幾乎是同樣的,只是主索引不容許重複,不容許空值,他們的葉子結點的key都存儲指向鍵值對應的數據的物理地址。
非聚簇索引的數據表和索引表是分開存儲的。
非聚簇索引中的數據是根據數據的插入順序保存。所以非聚簇索引更適合單個數據的查詢。插入順序不受鍵值影響。
只有在MyISAM中才能使用FULLTEXT索引。
最開始我一直不懂既然非聚簇索引的主索引和輔助索引指向相同的內容,爲何還要輔助索引這個東西呢,後來才明白索引不就是用來查詢的嗎,用在那些地方呢,不就是WHERE和ORDER BY 語句後面嗎,那麼若是查詢的條件不是主鍵怎麼辦呢,這個時候就須要輔助索引了。
聚簇索引的主索引的葉子結點存儲的是鍵值對應的數據自己,輔助索引的葉子結點存儲的是鍵值對應的數據的主鍵鍵值。所以主鍵的值長度越小越好,類型越簡單越好。
聚簇索引的數據和主鍵索引存儲在一塊兒。
聚簇索引的數據是根據主鍵的順序保存。所以適合按主鍵索引的區間查找,能夠有更少的磁盤I/O,加快查詢速度。可是也是由於這個緣由,聚簇索引的插入順序最好按照主鍵單調的順序插入,不然會頻繁的引發頁分裂,嚴重影響性能。
在InnoDB中,若是隻須要查找索引的列,就儘可能不要加入其它的列,這樣會提升查詢效率。
使用主索引的時候,更適合使用聚簇索引,由於聚簇索引只須要查找一次,而非聚簇索引在查到數據的地址後,還要進行一次I/O查找數據。
由於聚簇輔助索引存儲的是主鍵的鍵值,所以能夠在數據行移動或者頁分裂的時候下降委會成本,由於這時不用維護輔助索引。可是輔助索引會佔用更多的空間。
聚簇索引在插入新數據的時候比非聚簇索引慢不少,由於插入新數據時須要減壓主鍵是否重複,這須要遍歷主索引的全部葉節點,而非聚簇索引的葉節點保存的是數據地址,佔用空間少,所以分佈集中,查詢的時候I/O更少,但聚簇索引的主索引中存儲的是數據自己,數據佔用空間大,分佈範圍更大,可能佔用好多的扇區,所以須要更屢次I/O才能遍歷完畢。
下圖能夠形象的說明聚簇索引和非聚簇索引的區別
何時要使用索引?
主鍵自動創建惟一索引;
常常做爲查詢條件在WHERE或者ORDER BY 語句中出現的列要創建索引;
做爲排序的列要創建索引;
查詢中與其餘表關聯的字段,外鍵關係創建索引
高併發條件下傾向組合索引;
何時不要使用索引?
常常增刪改的列不要創建索引;
有大量重複的列不創建索引;
表記錄太少不要創建索引;
在組合索引中不能有列的值爲NULL,若是有,那麼這一列對組合索引就是無效的;
在一個SELECT語句中,索引只能使用一次,若是在WHERE中使用了,那麼在ORDER BY中就不要用了;
LIKE操做中,'%aaa%'不會使用索引,也就是索引會失效,可是‘aaa%’可使用索引;
在索引的列上使用表達式或者函數會使索引失效,例如:select * from users where YEAR(adddate)<2018,將在每一個行上進行運算,這將致使索引失效而進行全表掃描,所以咱們能夠改爲:select * from users where adddate<’2018-12-24′。
在查詢條件中使用正則表達式時,只有在搜索模板的第一個字符不是通配符的狀況下才能使用索引。
在查詢條件中使用<>會致使索引失效。
在查詢條件中使用IS NULL會致使索引失效。
在查詢條件中使用OR鏈接多個條件會致使索引失效,這時應該改成兩次查詢,而後用UNION ALL鏈接起來。
儘可能不要包括多列排序,若是必定要,最好爲這隊列構建組合索引;
只有當數據庫裏已經有了足夠多的測試數據時,它的性能測試結果纔有實際參考價值。若是在測試數據庫裏只有幾百條數據記錄,它們每每在執行完第一條查詢命令以後就被所有加載到內存裏,這將使後續的查詢命令都執行得很是快--無論有沒有使用索引。只有當數據庫裏的記錄超過了1000條、數據總量也超過了MySQL服務器上的內存總量時,數據庫的性能測試結果纔有意義。
索引的最左前綴和和B+Tree中的「最左前綴原理」有關,舉例來講就是若是設置了組合索引<col1,col2,col3>那麼如下3中狀況可使用索引:col1,<col1,col2>,<col1,col2,col3>,其它的列,好比<col2,col3>,<col1,col3>,col2,col3等等都是不能使用索引的。
根據最左前綴原則,咱們通常把排序分組頻率最高的列放在最左邊,以此類推。
在上面已經提到,使用LIKE進行模糊查詢的時候,'%aaa%'不會使用索引,也就是索引會失效。若是是這種狀況,只能使用全文索引來進行優化(上文有講到)。
爲檢索的條件構建全文索引,而後使用
SELECT * FROM tablename MATCH(index_colum) ANGAINST(‘word’);
複製代碼
首先,什麼是事務?事務就是一段sql 語句的批處理,可是這個批處理是一個atom(原子),不可分割,要麼都執行,要麼回滾(rollback)都不執行。
MySQL 事務主要用於處理操做量大,複雜度高的數據。好比說,在人員管理系統中,你刪除一我的員,你即須要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操做語句就構成一個事務!
在 MySQL 中只有使用了 Innodb 數據庫引擎的數據庫或表才支持事務。
事務處理能夠用來維護數據庫的完整性,保證成批的 SQL 語句要麼所有執行,要麼所有不執行。
事務用來管理 insert,update,delete 語句
通常來講,事務是必須知足4個條件(ACID): Atomicity(原子性)、Consistency(穩定性)、Isolation(隔離性)、Durability(可靠性)
一、事務的原子性:一組事務,要麼成功;要麼撤回。
二、穩定性 :有非法數據(外鍵約束之類),事務撤回。
三、隔離性:事務獨立運行。一個事務處理後的結果,影響了其餘事務,那麼其餘事務會撤回。事務的100%隔離,須要犧牲速度。
四、可靠性:軟、硬件崩潰後,InnoDB數據表驅動會利用日誌文件重構修改。可靠性和高速度不可兼得, innodb_flush_log_at_trx_commit 選項 決定何時吧事務保存到日誌裏。
事務併發並不進行事務隔離形成的髒讀、幻讀、不可重複讀
髒讀:事務A讀到未提交事務B修改的數據,若是此時事務B中途執行失敗回滾,那麼此時事務A讀取到的就是髒數據。好比事務A對money進行修改,此時事務B讀取到事務A的更新結果,可是若是後面事務A回滾,那麼事務B讀取到的就是髒數據了。
不可重複讀:同一個事務中,對同一份數據讀取的結果不一致。事務A在事務B對數據更新前進行讀取,而後事務B更新提交,事務A再次讀取,這時候兩次讀取的數據不一樣。
幻讀:(同一個事務中,同一個查詢屢次返回的結果不同。事務B查詢表的記錄數,而後事務A對錶插入一條記錄,接着事務B再次查詢發現記錄數不一樣。注意這個解釋是不正確,網絡上有不少這樣的解釋,包括我認爲比較權威的專家,可是通過實驗發現並不正確。因此這是須要注意的)。能夠作這樣一個實驗,事務A查詢記錄數,事務B插入一條記錄(主鍵值爲6),提交,而後事務A查詢記錄數,發現記錄數沒有改變,可是此時插入一條主鍵值爲6的記錄發現衝突了,感受像出現了幻覺。
一、髒讀和不可重複讀:髒讀是事務讀取了還未提交事務的更新數據。不可重複讀是同一個事務中,幾回讀取的數據不一樣。
二、不可重複讀和幻讀的區別:都是在同一個事務中,前者是幾回讀取數據不一樣,後者是幾回讀取數據總體不一樣。
隔離級別改變影響鎖的週期
mysql支持上面4種隔離級別,默認爲可重複讀
MySQL有三種鎖的級別:頁級、表級、行級。
MyISAM和MEMORY存儲引擎採用的是表級鎖(table-level locking);
BDB存儲引擎採用的是頁面鎖(page-level locking),但也支持表級鎖;
InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認狀況下是
採用行級鎖。
MySQL這3種鎖的特性可大體概括以下: 一、表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。表級鎖讓多線程能夠同時從數據表中讀取數據,可是若是另外一個線程想要寫數據的話,就必需要先取得排他訪問(默認加排他表鎖);(共享讀鎖(Table Read Lock)更新數據時,必需要等到更新完成了,其餘線程才能訪問(讀)這個表。(獨佔寫鎖(Table Write Lock))
二、行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
三、頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。
原則上數據表有一個讀鎖時,其它進程沒法對此表進行更新操做,但在必定條件下,MyISAM表也支持查詢和插入操做的併發進行。
通常MyISAM引擎的表也支持查詢和插入操做的併發進行(原則上數據表有一個讀鎖時,其它進程沒法對此表進行更新操做)
MyISAM引擎有一個系統變量concurrent_insert,專門用以控制其併發插入的行爲,其值分別能夠爲0、1或2:
a、concurrent_insert爲0,不容許併發插入。     
b、concurrent_insert爲1,若是MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM容許在一個進程讀表的同時,另外一個進程從表尾插入記錄。這也是MySQL的默認設置。     
c、concurrent_insert爲2,不管MyISAM表中有沒有空洞,都容許在表尾併發插入記錄。
複製代碼
若是有讀寫請求同時進行的話,MYSQL將會優先執行寫操做。這樣MyISAM表在進行大量的更新操做時(特別是更新的字段中存在索引的狀況下),會形成查詢操做很難得到讀鎖,從而致使查詢阻塞。
咱們還能夠調整MyISAM讀寫的優先級別:
  a、經過指定啓動參數low-priority-updates,使MyISAM引擎默認給予讀請求以優先的權利。
  b、經過執行命令SET LOW_PRIORITY_UPDATES=1,使該鏈接發出的更新請求優先級下降。
  c、經過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,下降該語句的優先級。
複製代碼
MyISAM使用的是 flock 類的函數,直接就是對整個文件進行鎖定(叫作文件鎖定),MyISAM的數據表是按照單個文件存儲的,能夠針對單個表文件進行鎖定;
InnoDB使用的是 fcntl 類的函數,能夠對文件中局部數據進行鎖定(叫作行鎖定),InnoDB是一整個文件,把索引、數據、結構所有保存在 ibdata 文件裏,因此必須用行鎖定。
BEGIN或START TRANSACTION;顯式地開啓一個事務;
COMMIT;也可使用COMMIT WORK,不過兩者是等價的。
COMMIT會提交事務,並使已對數據庫進行的全部修改稱爲永久性的;
ROLLBACK;有可使用ROLLBACK WORK,不過兩者是等價的。回滾會結束用戶的事務,並撤銷正在進行的全部未提交的修改;
SAVEPOINT identifier;SAVEPOINT容許在事務中建立一個保存點,一個事務中能夠有多個SAVEPOINT;
RELEASE SAVEPOINT identifier;刪除一個事務的保存點,當沒有指定的保存點時,執行該語句會拋出一個異常;
ROLLBACK TO identifier;把事務回滾到標記點;
SET TRANSACTION;用來設置事務的隔離級別。
InnoDB存儲引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。
複製代碼
一、用 BEGIN, ROLLBACK, COMMIT來實現
BEGIN 開始一個事務
ROLLBACK 事務回滾
COMMIT 事務確認
複製代碼
二、直接用 SET 來改變 My
SQL 的自動提交模式:
SET AUTOCOMMIT=0 禁止自動提交
SET AUTOCOMMIT=1 開啓自動提交
複製代碼
一、若是事務中sql正確運行,後面沒有commit,結果是不會更新到數據庫的,因此須要手動添加commit。
二、若是事務中部分sql語句出現錯誤,那麼錯誤語句後面不會執行。而咱們可能會認爲正確操做會回滾撤銷,可是實際上並無撤銷正確的操做,此時若是再無錯狀況下進行一次commit,以前的正確操做會生效,數據庫會進行更新。