每一個客戶端鏈接會在服務器進程中擁有一個線程,該鏈接的查詢只會在單獨的線程中執行。MySQL會解析查詢,並建立內部數據結構,而後對其進行各類優化。對於SELECT語句,解析查詢以前,服務器會先檢查緩存,若是可以在其中找到對應的查詢,服務器就再也不執行查詢解析,而是直接返回緩存中的結果。mysql
MySQL在兩個層面實現併發控制:服務器層與存儲引擎層。算法
在處理併發讀或寫時,能夠經過實現一個由兩種鎖組成的系統來解決問題。這兩種鎖一般被稱爲共享鎖和排他鎖,或者稱爲讀鎖和寫鎖。讀鎖是共享的,或者說是相互不阻塞的,多個客戶能夠在同時讀取同一數據。而寫鎖是排他的,同一時刻只能有一個用戶可以寫入,並防止其餘用戶讀取正在寫入的數據。sql
鎖粒度是指加鎖的對象的大小。顯然,鎖的粒度越小,併發控制效率越高。鎖的各類操做,包括得到鎖、檢查鎖和釋放鎖等,都會增長系統開銷。所以,若是系統花費大量時間來管理鎖,而不是用來獲取數據,就會影響系統性能。數據庫
有兩種常見的縮策略,表鎖和行級鎖。表鎖開銷較小,可是併發控制很差。行級鎖能夠很好地實現併發控制,可是開銷比較大。緩存
事務將幾個操做做爲一個總體,要麼所有執行,要麼所有放棄。事務的四大特性ACID:安全
事務處理也會使系統作更多額外工做,用戶能夠根據是否須要進行事務處理,選擇合適的存儲引擎。服務器
下面是四種事務的隔離級別:session
死鎖指的是多個事務在同一資源上相互佔用,並請求對方佔用的資源,致使惡性循環的現象。數據庫系統中實現了各類死鎖檢測和死鎖超時機制。數據結構
在MySQL中默認是自動提交事務的,每一個查詢操做被看成一個事務。可使用set autocommit
來設置是否自動提交。能夠經過set session transaction isolation level
來設置隔離級別。在同一事務中使用多種存儲引擎是不可靠的。併發
MySQL中大多事務型存儲引擎實現的都不是簡單的行級鎖,通常同時實現了多版本併發控制。MVCC的實現是經過保存數據在某個時間點的快照來實現的。即,不論須要執行多長時間,每一個事務看到的數據都是一致的。典型的實現有樂觀併發控制和悲觀併發控制。
InnoDB的MVCC經過在每行記錄的後面保存兩個隱藏的列來實現。這兩個列,一個用來保存過時(被刪除)的版本號,一個用來保存建立的版本號。每一個事務開始時,會使版本號遞增。事務開始時的版本號被用做事務的版本號:
.frm
文件用來保存表的定義。
MySQL默認存儲引擎,採用MVCC來支持高併發,而且實現了四個標準的隔離級別。基於聚簇索引創建。
MyISAM不支持事務和行級鎖,並且沒法在崩潰以後安全恢復;它將表存儲在兩個文件中:數據文件和索引文件,分別以.MYD
和.MYI
爲拓展名。它對整張表加鎖,而不是某行。若是建立並導入數據以後,不會再進行修改,可使用壓縮表來減小空間佔用和IO,從而提高查詢性能。
還有一些其餘的存儲引擎。
除非某些InnoDB不具有的特性,而且沒有其餘方法能夠替代,不然都應該優先優先選擇InnoDB引擎。除非萬不得已,不然不要混合使用多種存儲引擎,不然可能帶來一些複雜的問題及潛在的BUG.
修改存儲引擎:
Alter table tbl_name engine = Innodb
:須要執行很長時間,MySQL會按行將數據從原表複製到一張新的表中,在複製期間可能會小號系統的IO能力,同時在原表上加鎖。導入導出
:用mysqldump將數據導出到文件,燃火修改文件中create table語句的存儲引擎選項,注意要同時修改表名。CREATE & SELECT
:數據量不大的時候,能夠先建立一張使用新的存儲引擎的表,而後利用INSERT SELECT
將原表中的數據插入到新表中。當數據量比較大的時候,可使用between語句來分批次操做完成。MySQL整數能夠指定寬度,如int(11),對大多數應用這是沒有意義的:它不會限制值的合法範圍,只是規定了MySQL的一些交互工具用來顯示字符的個數。對於存儲和計算來講,int(20)和int(1)是相同的。
由於須要額外空間和計算開銷,因此儘可能只在對小數進行計算的時候才使用decimal。在數據量比較大的時候,能夠考慮使用bigint代替decimal,將存儲的單位根據小數的位數乘以相應的倍數便可。
CHAR適合存儲短的短的字符串,或者全部的值都接近同一長度。對於常常變動的數據,CHAR也比VARCHAR好,由於定長的CHAR不容易產生碎片。對於很是短的字符串,CHAR也比VARCHAR更好,由於VARCHAR還須要1或2個額外的字節存儲字符串長度。
可使用枚舉替代經常使用的字符串類型,枚舉能夠把一些不重複的字符串存儲成預約義的集合。枚舉字段是按照內部存儲的整數而不是字符串進行排序的。枚舉很差的地方是當向枚舉中增長字段的時候,須要使用ALTER TABLE語句來進行修改。因此,對未來可能會變的字符串,使用枚舉不是個好的主意。
若是須要將時間保存到毫秒級別,可使用BIGINT.
可使用BIT列在一列中存儲一個或多個true/false值。BIT(1)定義一個包含單個位的字段,bit(2)存儲兩個位。bit列最多存儲64個位。
MySQL將BIT看成字符串類型,而不是數字類型。當檢索bit(1)時,結果是一個包含二進制0或1值的字符串,而不是ASCII碼的"0"或"1"。而後,在數字上下文場景中檢索時,結果將是爲字符串轉換成的數字.
所謂的範式就是,好比,若是咱們須要學生和學校的記錄,若是咱們將學生和學校放在不一樣的表中就符合範式,若是放在同一表中就是反範式的。
範式化具備一些好處,好比:操做更快;每次只要修改少許的數據;表更小,適合放在內存中,操做更快。缺點是一般須要表關聯。
不過實際咱們並不徹底遵照範式和反範式的規則。
緩存表表示存儲那些能夠簡單地從schema其餘表獲取數據的表。彙總表保存的是使用GROUP BY語句聚合數據的表,使用匯總表的緣由是,實時計算和統計值是很昂貴的操做,由於要麼須要掃描表中的大部分數據,要麼只能在某些索引上纔能有效運行。
計數器表是用來統計某個操做的次數的表,咱們能夠在一個表中定義一個名爲cnt的字段來表示操做的次數,而後每次執行了操做以後將其加1。可是,加1須要更新操做來完成,每次更新的時候要獲取記錄的鎖,所以併發效率不高。解決這個問題,咱們能夠再增長一個字段slot做爲隨機的槽,每次執行操做的時候,咱們使用隨機數選擇某個slot,並對其進行+1更新(只用鎖住部分數據,所以效率比較高)。最後統計的時候將所有記錄加起來便可。
數據庫的索引相似於書的索引,實際的查找某個值的時候,先按照值進行查找,而後返回包含該值的數據行。索引能夠包含一個或多個列的值,若是索引包含多個列,那麼列的順序也很重要——索引對多個列排序的依據是CREATE TABLE時定義索引的順序,因此MySQL只能高效地使用索引的最左前綴列。
在MySQL中,索引是在存儲引擎層而不是服務器層實現的。因此,沒有統一的標準:不一樣存儲引擎工做方式不一樣。MySQL支持的索引類型以下:
一般人們所說的索引。實際上不少存儲引擎使用的是B+Tree. B-Tree索引適用於全鍵值、鍵值範圍或鍵前綴查找。類型,以多列索引key(last_name, first_name, dob)爲例:
B-Tree的一些限制:
基於哈希表實現,只有精確匹配索引全部列的查詢纔有效。由於它對每行中的全部索引列計算出一個哈希碼,做爲哈希表的鍵(原理是基於拉鍊法的解決碰撞的策略)。在MySQL中只有Memory引擎顯式地支持哈希索引,Memory引擎同時也支持B-Tree索引。
哈希索引只須要存儲對應的哈希值,因此索引的結構十分緊湊,這讓哈希索引的查找速度很是快。然而,它也有自身的限制:
MyISAM表支持空間索引,能夠用做地理數據存儲。
它查找的是文本中的關鍵詞,而不是直接比較索引中的值。全文索引相似於搜索引擎作的事情,而不是簡單的where條件匹配。在相同的列上建立全文索引和基於B-Tree的索引不會衝突。
其餘索引,還有分型樹索引。
索引的優勢有:
對於小型的表,使用全表掃描更高效;對中到大型的表,使用索引很是有效。對於特大型的表,創建和使用索引的代價會隨之增加。這種狀況下可使用分區來查出一組數據,而不是一條一條地匹配。
若是查找中的列不是獨立的,則MySQL不會使用索引。獨立的列是指索引列不能是表達式的一部分,也不能是函數的參數。好比
select * from actor where actor_id + 1 = 5;
select * from actor where to_days(current_date) - to_days(col_day) <= 10;
複製代碼
索引很長的字符串會讓索引變得大且慢。一般能夠只索引開始部分的字符,這樣能夠節約索引空間,從而提升索引的效率。缺點是會下降索引的選擇性。索引的選擇性是指不重複的索引值和記錄總數的比,顯然越大越好。因此,咱們須要選擇足夠長的前綴來保證選擇性,同時又不能太長以下降索引空間。
咱們可使用語句
select count(distinct left(col_name, 3)) / count(*) from tbl_name;
複製代碼
來統計使用3個字符的前綴選擇性,同理能夠計算出4個,5個等的狀況。最後,選擇一個合理的前綴長度便可。選擇了長度以後能夠像下面這樣設置指定長度的索引:
alter table add key(col_nane(4));
複製代碼
常見的錯誤是,爲每一個列建立獨立的索引,或者按照錯誤的順序建立多列索引。
若是一張表在col1和col2列上面存在索引,若是咱們使用col1 and col2做爲where的條件,那麼索引會作相角操做,若是使用col1 or col2,索引會作聯合操做. 相交操做一般意味着須要一個包含全部相關列的多列索引,而不是獨立的單列索引。聯合操做則會消耗CPU和內存在算法的緩存、排序和合並上。若在explain中看到有合併索引,應先檢查查詢和表結構,看看是否是最優的。也能夠經過optimizer_switch來關閉索引合併功能,或使用igonre index提示優化器忽略掉某些索引。
若是要對多個列創建一個索引,除了上面的問題以外,還應該考慮所建的索引中列的順序。好比,對col1, col2兩列數據創建索引,那麼咱們的順序應被指定爲(col1, col2)仍是(col2, col1)呢。咱們能夠依然可使用上面的選擇性來解決這個問題,咱們能夠將選擇性比較高的列做爲索引的第一列,另外一列做爲第二列。
聚簇索引不是一種單獨的索引類型,而是一種數據存儲方式。「聚簇」表示數據行和相鄰的鍵值緊湊地存儲在一塊兒。由於沒法同時把數據行存放在兩個不一樣的地方,因此一個表只能有一個聚簇索引。
InnoDB經過主鍵彙集數據,若是沒有主鍵就選擇一個惟一的非空索引,若是沒有這樣的索引,就隱式定義一個主鍵做爲聚簇索引。
聚餐的優勢:
缺點:
關於聚簇索引和非聚簇索引的存儲方式的區別:
假設有數據以下:
若是是聚簇的方式來存儲,那麼它的一級索引是下面的樣子:
也就是它們使用主鍵的值做爲彙集數據,而後每一個葉子包含了每行的所有記錄。聚簇索引的二級索引是下面的樣子:
注意將二級索引中存儲的值和最上面的表中的數據進行對比。從中能夠看出,實際上它的二級索引是先用二級索引的值找到一級索引,而後使用一級索引來查找整個記錄。
非聚簇的存儲方式是下面的樣子:
以上是非聚簇的一級索引的例子,非聚簇的二級索引的狀況與之相同。即它們都是先用指定的值找到行號,而後使用行號來查找完整記錄。
最好避免隨機的聚簇索引,特別是對於IO密集型的應用。由於隨機插入的時候,須要爲新的行尋找合適的位置——一般是已有數據的中間位置——而且分配空間。這回增長不少額外的工做,並致使分佈不夠優化。最好使用自增的主鍵。
若是一個索引包含了全部須要查詢的字段的值,就稱之爲覆蓋索引。覆蓋索引就是從索引中直接獲取查詢結果,要使用覆蓋索引須要注意select查詢列中包含在索引列中;where條件包含索引列或者複合索引的前導列;查詢結果的字段長度儘量少。
使用延遲關聯解決索引沒法覆蓋問題:下面的解決方法對效率的提高不是絕對的!
SELECT * FROM products WHERE actor = 'SEAB CARREY' AND title like '%APPOLO%'
複製代碼
上面的SQL中要查詢所有的列,而咱們沒有覆蓋所有列的索引,所以沒有覆蓋索引。另外,like操做沒法使用索引,由於like操做只有在匹配左前綴時才能使用索引。
咱們能夠像下面這樣解決問題:
SELECT * FROM products
JOIN (SELECT prod_id FROM products
WHERE actor = 'SEAB CARREY' AND title like '%APPOLO%')
AS t1 ON (t1.prod_id = products.prod_id)
複製代碼
這裏,須要先創建(actor, title, prod_id)索引。咱們先在子查詢中找到匹配的prod_id,而後跟外層中數據進行匹配來獲取全部列值。當符合where條件的數據數量遠小於actor過濾出的數據數量的時候,它的效率尤爲高。由於,根據子查詢的where過濾出數據以後才與外層查詢關聯,然後者使用actor讀取出數據以後,再用title進行關聯。前者須要讀取的數據量更少。
生成有序結果的兩種方式:排序,按索引順序掃描。當explain出的type爲index時,說明使用索引掃描來進行排序。MySQL可使用一個索引既知足排序,又知足查找。只有當索引的列順序和ORDER BY子句順序一致,且列的排序方向都同樣時,才能用索引對結果作排序。
下面是一些例子,假設索引是(col1, col2, col3),那麼:
...where col1 = 1 order by col2, col3;(√)
...where col1 = 1 order by col2;(√)
...where col1 > 1 order by col1, col2;(√)
...where col1 > 1 order by col2, col3;(X)
...where col1 = 1 order by col2 desc, col3 asc;(X)
...where col1 = 1 order by col2, col4;(X)
...where col1 = 1 order by col3;(X)
...where col1 = 1 and col2 in(1,3) order by col3;(X)
複製代碼
重複索引是指在相同的列上按照相同的順序建立的相同類型的索引。常見的錯誤有:
Inn哦DB只有在訪問行的時候纔會對其加鎖,而索引可以減小訪問行的次數,因此索引能減小鎖的數量。
能夠經過下面兩個步驟來分析慢查詢:
典型的請求查過須要的數據的場景:
查詢了不須要的記錄,若是隻須要指定行的記錄,可使用limit語句來只返回部分記錄;
多表關聯的時候返回了所有的列,好比下面的語句會返回tbl1和tbl2的所有記錄:
SELECT * FROM tbl1 INNER JOIN tbl2 ...;
複製代碼
能夠改爲下面的樣子(若是隻須要tbl1的記錄的話)
SELECT tb1.* FROM tbl1 INNER JOIN tbl2 ...;
複製代碼
總數取出所有的列。缺點是沒有辦法使用覆蓋索引完成優化,並且會爲服務器帶來額外的IO、內存和CPU消耗。
重複查詢相同的記錄。最好將這些數據緩存起來。
能夠經過explain輸出的列type中的值來獲得訪問類型。