Mysql的架構
第二層的架構是所有的跨引擎的功能實現的地方,例如:存儲,觸發器,視圖等。
第三層半酣了 存儲引擎,負責MySQL中的數據存儲和提取。
連接管理與安全性
每個客戶端連接都會在服務器進程中擁有一個線程,這個連接的查詢只會在這個單獨的線程中執行,該線程只能輪流在某個CPU核心或者CPU中運行。服務器會負責緩存線程,因此不需要為每一個新建的連接創建或者銷毀線程。當客戶端(應用)連接MySQL服務器時,服務器需要對其進行認證。
優化與執行
解析查詢,內部會創建數據結構(解析樹),然后對其優化,選擇合適的索引,可以請求優化器的解釋,優化器不關心用的什么引擎,但是存儲的引擎對優化查詢是有影響的,對於select語句,解析查詢的時候,先查緩存,命中了就返回緩存的結果集。
並發控制
倆個層面的並發控制:服務器層與存儲引擎層。
簡單的處理就是上鎖,例如讀寫鎖。
鎖力度
一種提高共享資源的並發性的方式是讓鎖定對象更具有選擇性。但是問題是加鎖也是需要消耗資源的,所以需要把握平衡。MySQL提供了多種選擇:有着自己的鎖策略和鎖粒度。
表鎖
表鎖是MySQL中最基本的鎖策略,並且是開銷最小的策略。它會鎖定整張表。
行級鎖
行級鎖可以最大程度地支持並發處理(同時也帶來了最大的鎖開銷)。行級鎖只在存儲引擎層實現,而MySQL服務器層沒有實現,服務器層完全不了解存儲引擎中的鎖實現。
事務
ACID
這里討論一下隔離性,實際比想象的要復雜。在SQL標准中定義了四種隔離級別,每一種級別都規定了一個事務中所做的修改,哪些在事務內和事務間是可見的,哪些是不可見的。較低級別的隔離通常可以執行更高的並發,系統的開銷也更低。
| 讀未提交 | 一個事務還沒提交時,它做的變更就能被別的事務看到 | 直接返回記錄上的最新值,沒有視圖概念 |
| 讀提交 | 一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的 | 這個視圖是在每個 SQL 語句開始執行的時候創建的 |
| 可重復讀 | 未提交變更對其他事務是不可見的 | 這個視圖是在事務啟動時創建的,整個事務存在期間都用這個視圖 |
| 串行化 | 顧名思義是對於同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。 | 直接用加鎖的方式來避免並行訪問 |
Read Uncommitted(讀取未提交內容)
在該隔離級別,所有事務都可以看到其他未提交事務的執行結果。
本隔離級別很少用於實際應用,因為它的性能也不比其他級別好多少。讀取未提交的數據,也被稱之為臟讀(Dirty Read)。
Read Committed(讀取提交內容)
這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。
它滿足了隔離的簡單定義:一個事務只能看見已經提交事務所做的改變。
這種隔離級別 也支持所謂的不可重復讀(Nonrepeatable Read),
因為同一事務的其他實例在該實例處理其間可能會有新的commit,所以同一select可能返回不同結果。
Repeatable Read(可重讀)
這是MySQL的默認事務隔離級別,它確保同一事務的多個實例在並發讀取數據時,會看到同樣的數據行。
不過理論上,這會導致另一個棘手的問題:幻讀 (Phantom Read)。
簡單的說,幻讀指當用戶讀取某一范圍的數據行時,另一個事務又在該范圍內插入了新行,
當用戶再讀取該范圍的數據行時,會發現有新的“幻影” 行。
InnoDB和Falcon存儲引擎通過多版本並發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。
Serializable(可串行化)
這是最高的隔離級別,它通過強制事務排序,使之不可能相互沖突,從而解決幻讀問題。
簡言之,它是在每個讀的數據行上加上共享鎖。在這個級別,可能導致大量的超時現象和鎖競爭。
死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
事務日志
你可將MVCC看成行級別鎖的一種妥協,它在許多情況下避免了使用鎖,同時可以提供更小的開銷。根據實現的不同,它可以允許非阻塞式讀,在寫操作進行時只鎖定必要的記錄。
MVCC會保存某個時間點上的數據快照。這意味着事務可以看到一個一致的數據視圖,不管他們需要跑多久。這同時也意味着不同的事務在同一個時間點看到的同一個表的數據可能是不同的。
Schema與數據優化
選擇優化的數據類型,有幾個簡單的規則:
- 更小通常是最好的:越小,占用cpu、磁盤和內存越少。
- 盡量簡單:簡單的數據類型可以減少cpu開銷。
- 盡量避免null:包含null值的列在sql語句中通常很難優化。
各種數據類型的特點
整數類型
| 數據類型 | 占用存儲空間 | 范圍 |
|---|---|---|
| tinyint | 8位 | -128~127 |
| smallint | 16位 | -32 768,32 767 |
| mediumint | 24位 | -8388608~8388607 |
| int | 32位 | -2147483648~2147483647 |
| bigint | 64位 | ±9.22*10的18次方 |
注意:
1、整數類型有個UNSIGED屬性,表示不允許你負值,這大致可以讓數據類型的上限提高一倍。
2、很多情況下,對於 int(1)和 int(20),只是限制了顯示字符的個數,對於存儲和計算是相同的。
實數類型
float(M,N):4個字節存儲,8位精度
double(M,N):8個字節存儲,16位精度( M:總長度 N:小數位數 )
decimal不僅可以存帶有小數點的數據,也可以存儲比 bigint 還大的數據
decimal(M,D)
M: 數字的最大長度 (5.0 以后,最大上限位 65 ; 之前是 254 位)
D: 小數位數 (D < M , D < 30)注意:
cpu不支持對decimal的直接計算,所以decimal只是一種存儲格式,在計算的時候,還是會轉換為double類型
字符串類型
-
varchar
特點: 1、存儲可變長字符串 2、設置: ROW_FORMAT = FIXED ,會變成每一行都定長存儲,浪費空間 3、需要額外的 1 到 2 個字節存儲字符串長度 適用的列:很少去更新的那種 -
char
適合的列 : 1、很短的字符串 2、所有值長度都近似 InnoDB:建議使用 varchar類型 char 和 varchar 保存和檢索方式是不同,最大長度和是否尾部空格被保留等方面也不同。 -
binary 和 varbinvar:存儲二進制字符串 (binary 使用 \0 來填充)
-
text :存儲數據量非常大的字符串
和varchar的對比: 1、不能有默認值 2、必須創建前綴索引 3、不能像 varcha(n) 一樣設置n -
blob :存儲數據量非常大的二進制字符串
和text對比: 1、存儲方式不同:text 存儲文本,區分大小寫,blob 二級制方式存儲,不區分大小寫 2、blob 只能整體讀出 3、text 可以指定字符集,blob 不能
日期時間類型
datetime
1、范圍:1001 ~ 9999,精度位秒,與時區無關,8字節存儲
2、格式:YYYYMMDDHHMMSS
timestamp
1、范圍: 1970.01.01 ~ 2038.12.31
2、與時區有關
3、此類型默認位 not null 類型,如果你插入的時候,沒有填寫時間,默認位當前時間
綜合對比:
1、通常情況下,使用 timestamp ,空間效率更高
2、但是 timestamp 的行為比較復雜,你在選用這個數據類型的時候,需要驗證以下它的行為是否符合你的需要
位數據類型
bit
- 用來存儲一個或者多個
true/false - 最大長度 64 位
- mysql,它被作為字符串類型存儲
范式和反范式
范式是基礎規范,反范式是針對性設計。
目前關系數據庫有六種范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,還又稱完美范式)。
第一范式 (1st NF)
第一范式的目標是確保每列的原子性,如果每列都是不可再分的最小數據單元,則滿足第一范式
第二范式(2nd NF)
第二范式(2NF)是在第一范式(1NF)的基礎上建立起來的,即滿足第二范式(2NF)必須先滿足第一范式(1NF)。
第二范式要求每個表只描述一件事情
第三范式(3rd NF)
如果一個關系滿足第二范式,並且除了主鍵以外的其他列都不傳遞依賴於主鍵列,則滿足第三范式.
第三范式需要確保數據表中的每一列數據都和主鍵直接相關,而不能間接相關。
范式優缺點
- 避免數據冗余,減少維護數據完整性的麻煩。
- 減少數據庫的空間。
- 數據變更速度快。
同時,也有如下缺點:
- 按照范式的規范設計的表,等級越高的范式設計出來的表數量越多。
- 獲取數據時,表關聯過多,性能較差。
表的數量越多,查詢所需要的時間越多。也就是說所用的范式越高,對數據操作的性能越低。
反范式
范式是普適的規則,滿足大多數的業務場景的需求。對於一些特殊的業務場景,范式設計的表,無法滿足性能的需求。此時,就需要根據業務場景,在范式的基礎之上進行靈活設計,也就是反范式設計。
反范式設計主要從三方面考慮:
- 業務場景。
- 響應時間。
- 字段冗余。
反范式設計就是用空間來換取時間,提高業務場景的響應時間,減少多表關聯。主要的優點如下。
- 允許適當的數據冗余,業務場景中需要的數據幾乎都可以在一張表上顯示,避免關聯。
- 可以設計有效的索引。
MySQL 使用原則和設計規范
講完范式,接下來我們看看 MySQL 使用中的一些使用原則和設計規范。
MySQL 雖然具有很多特性並提供了很多功能,但是有些特性會嚴重影響它的性能,比如,在數據庫里進行計算,寫大事務、大 SQL、存儲大字段等。
想要發揮 MySQL 的最佳性能,需要遵循 3 個基本使用原則。
- 首先是需要讓 MySQL 回歸存儲的基本職能:MySQL 數據庫只用於數據的存儲,不進行數據的復雜計算,不承載業務邏輯,確保存儲和計算分離;
- 其次是查詢數據時,盡量單表查詢,減少跨庫查詢和多表關聯;
- 還有就是要杜絕大事務、大 SQL、大批量、大字段等一系列性能殺手。
- 大事務,運行步驟較多,涉及的表和字段較多,容易造成資源的爭搶,甚至形成死鎖。一旦事務回滾,會導致資源占用時間過長。
- 大 SQL,復雜的 SQL 意味着過多的表的關聯,MySQL 數據庫處理關聯超過 3 張表以上的 SQL 時,占用資源多,性能低下。
- 大批量,意味着多條 SQL 一次性執行完成,必須確保進行充分的測試,並且在業務低峰時段或者非業務時段執行。
- 大字段,blob、text 等大字段,盡量少用。必須要用時,盡量與主業務表分離,減少對這類字段的檢索和更新。
創建高性能的索引
索引(在MySQL中也叫做“鍵(key)") 是存儲引擎用於快速找到記錄的一種數據結構。索引對於良好的性能非常關鍵。尤其是當表中的數據量越來越大時,索引對性能的影響愈發重要。索引優化應該是對查詢性能優化最有效的手段了。索引能夠輕易將查詢性能提高幾個數量級,“最優”的索引有時比一個“好的”索引性能要好兩個數量級。創建一一個真正“最優”的索引經常需要重寫查詢。
索引的類型
B樹
索引的底層結構:數據結構-B樹
哈希索引
哈希索引基於哈希表實現,對於每一行數據,存儲引擎會對所有的索引列計算一個哈希碼,通過哈希碼能以 O(1) 時間進行查找,但是無法用於排序與分組,並且只支持精確查找,無法用於部分查找和范圍查找。
在MySQL 中,只有Memory引擎顯式支持哈希索引。
InnoDB 存儲引擎有一個特殊的功能叫“自適應哈希索引”,當某個索引值被使用的非常頻繁時,會在 B+Tree 索引之上再創建一個哈希索引,這樣就讓 B+Tree 索引具有哈希索引的一些優點,比如快速的哈希查找。
空間數據索引(R-Tree)
MyISAM 存儲引擎支持空間數據索引(R-Tree),可以用於地理數據存儲。空間數據索引會從所有維度來索引數據,可以有效地使用任意維度來進行組合查詢。
全文索引
MyISAM 存儲引擎支持全文索引,用於查找文本中的關鍵詞,而不是直接比較是否相等。
查找條件使用 MATCH AGAINST,而不是普通的 WHERE。全文索引使用倒排索引實現,它記錄着關鍵詞到其所在文檔的映射。
InnoDB 存儲引擎在 MySQL 5.6.4 版本中也開始支持全文索引。
索引的優缺點
優點
- 索引大大減少了服務器需要掃描的數據量
- 通過索引可以幫助服務器避免排序和臨時表,降低CPU消耗
- 可以將隨機IO變為順序IO,加快IO速度
缺點
- 雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對表進行INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因為更新所帶來的鍵值變化后的索引信息
- 實際上索引也是一張表,該表保存了主鍵與索引字段,並指向實體表的記錄,所以索引列也是要占用空間的
高性能索引的策略
獨立的列
如果MySQL查詢的列不是獨立的,就不會使用索引,“獨立的列”指的是,索引列不能是表達式的一部分,也不能是函數的參數。
mysql> SELECT id, name FROM t_user WHERE id + 1 = 5;
前綴索引和索引的選擇性
有時候需要索引很長的字符列,這會讓索引變得大且慢,比如對於 BLOB、TEXT 和 VARCHAR 類型的列,必須使用前綴索引,只索引開始的部分字符。前綴長度的選取需要根據索引選擇性來確定。
多列索引
很多人對於多列索引的理解都不夠,一個常見的錯誤就是,為每個列創建獨立的索引,或者按照錯誤的順序創建多列索引。在多個列上建立獨立的單列索引大部分情況下並不能提高MySQL的查詢性能,所以引入“索引合並”的策略,一定程度上可以使用表上的多個單列索引來定位指定的行。
索引合並策略有時候是一種優化的結果,但是實際上表明表上的索引建立的很槽糕。
合適的索引列順序
讓選擇性最強的索引列放在前面。
索引的選擇性是指:不重復的索引值和記錄總數的比值。最大值為 1,此時每個記錄都有唯一的索引與其對應。選擇性越高,每個記錄的區分度越高,查詢效率也越高。
聚簇索引
聚簇索引並不是一種單獨的索引類型,而是一種數據存儲方式,術語“聚簇”表示數據行和相鄰的鍵值緊湊地存儲在一起。
InnoDB 通過主鍵聚集數據,如果沒有定義主鍵,InnoDB會選擇一個唯一的非空索引來代替,如果沒有這樣的索引,InnoDB會隱式的定義一個主鍵來作為聚簇索引。
聚集的數據的優缺點
優點:
- 可以把相關的數據保存在一起
- 例如實現電子郵箱時,根據用戶ID來聚集數據,這樣只需要從磁盤讀取少量的數據就可以獲取某個用戶的全部郵件,如果沒有聚簇索引,獲取每封郵件都會導致一次磁盤IO
- 數據訪問更快,聚簇索引將索引和數據保存在同一個B+樹中,能更快的查找數據
- 使用覆蓋索引掃描的查詢可以直接使用頁節點中的主鍵值
缺點:
- 聚簇數據最大限度提高了IO密集型應用的性能,但是如果數據全部放在內存中,則訪問的順序就不重要,聚簇索引也沒有優勢
- 插入速度嚴重依賴於插入順序,如果不是按照主鍵的順序加載數據,那么加載完成后最好使用OPTIMIZE TABLE命令重新組織一下表,所以建議選擇自增的主鍵
- 更新聚簇索引列的代價很高,因為會強制InnoDB將每個被更新的行移動到新的位置
- 基於聚簇索引的表在插入新行,或者主鍵被更新導致需要移動行的時候,可能面臨“頁分裂”的問題。當行的主鍵值要求必須將這一行插入到某個已滿的頁中時,存儲引擎會將該頁分裂成兩個頁面來容納該行,這就是一次分裂操作。頁分裂會導致表占用更多的磁盤空間。
- 聚簇索引可能導致全表掃描變慢,尤其是行比較稀疏,或者由於頁分裂導致數據存儲不連續的時候
非聚簇索引
將數據存儲於索引分開結構,索引結構的葉子節點指向了數據的對應行,myisam通過key_buffer把索引先緩存到內存中,當需要訪問數據時(通過索引訪問數據),在內存中直接搜索索引,然后通過索引找到磁盤相應數據,這也就是為什么索引不在key buffer命中時,速度慢的原因
覆蓋索引
索引覆蓋所有需要查詢的字段的值
好處:
- 索引條目遠小於數據行大小,所以可以幾大減少數據訪問量以及更容易全部放到內存
- 索引是按照列值順序存儲,對於IO密級型的范圍查詢會比隨機從磁盤讀取每一行數據的IO要少得多
- 一些存儲引擎(例如 MyISAM)在內存中只緩存索引,而數據依賴於操作系統來緩存。因此,只訪問索引可以不使用系統調用(通常比較費時)
- InnoDB 的二級索引(非聚簇索引)在葉子結點保存了行的主鍵值,如果二級主鍵能夠覆蓋查詢,則可以避免對主鍵索引的二次查詢
查詢性能優化
查詢的時候要在不同的地方花費時間,包括網絡,CPU計算,刪生成統計信息和執行任務,執行並且返回客戶端。優化查詢就是減少或者消除這些不必要的操作。
慢查詢基礎:優化數據的訪問
分析步驟:
- 確認是否檢索大量超過需要的數據
- 服務層是否在分析大量超過需要的行
本文還有很多不足的地方,后面會繼續加油。
