最上層不是Mysql獨有的, 好比鏈接處理,受權認證, 安全 等等
第二層核心服務功能,包括查詢解析,分析,優化,緩存以及全部內置函數,存儲過程,觸發器,視圖等都在這層實現
第三層 存儲引擎,存儲引擎API包含幾十個底層函數。 html
優化與執行:每一個鏈接都會在mysql服務端產生一個線程(內部經過線程池管理線程),好比一個select語句進入,mysql首先會在查詢緩存中查找是否緩存了這個select的結果集,若是沒有則繼續執行 解析、優化、執行的過程;不然會之間從緩存中獲取結果集。
MySQL解析查詢會建立內部數據結構(解析樹),而後對其進行各類優化,包括重寫查詢,決定表的讀取順序,以及選擇合適的索引等。 (用戶能夠經過特殊的關鍵字提示優化器,影響它的決策過程。也能夠請求優化器解釋優化過程的各個因素)
MySQL原理與實踐,一共六篇java
mysql數據庫支持插件式存儲引擎。就是說支持多種存儲引擎,甚至用戶能夠按照本身的須要定製和使用本身的存儲引擎。
mysql5.5以前默認存儲引擎是MyISAM,5.5後改成InnoDB。
MySQL數據庫各個版本的區別、以及不一樣引擎的適用環境mysql
執行update時,更新語句涉及到了數據的更改,因此必不可少的須要引入日誌模塊。
redo log是InnoDB引擎特有的日誌模塊;binlog是Server層自帶的日誌模塊。sql
在更新數據寫入內存的同時,會先更新內存記錄redo log,而後順序寫入磁盤,而不是等內存佔滿以後再持久化,這樣當數據庫異常宕機以後,咱們能夠根據redo log重作日誌來恢復數據,保證以前提交的數據不會丟失。 數據庫
各類不一樣操做有不一樣的重作日誌格式。數組
binlog是邏輯日誌,記錄本次修改的原始邏輯,說白了就是記錄了修改數據的SQL語句。
經過mysqlbinlog能夠解析查看binlog日誌。緩存
在今天中午12點的時候,發現上午10點執行了錯誤的SQL語句,想把數據庫狀態恢復到上午10點,錯誤語句執行以前。那麼該怎麼辦呢?
數據恢復步驟以下: 首先你要拿到最近一次的備份庫 拿到最近備份庫開始到出錯時候的全部binlog(binlog日誌保留時間能夠自定義) 使用binlog重放到錯誤發生以前。
主要用於事務的回滾。安全
是一種優化查詢的數據結構。服務器
經常使用的有哈希表、徹底平衡二叉搜索樹、B樹、B+樹等數據結構。
一個表的數據行數越多,那麼對應的索引文件其實也是會很大的,實際上也是須要存儲在磁盤中的,而不能所有都放在內存中,因此咱們在考慮選用哪一種數據結構時,咱們能夠換一個角度思考,哪一個數據結構更適合從磁盤中讀取數據,或者哪一個數據結構可以提升磁盤的IO效率。
MySQL中的索引是B+樹實現的,MySQL爲什麼選擇使用B+樹?得看看其餘數據結構實現的索引的特色:session
好比給name字段創建hash索引的狀況下
首先數據庫底層會計算name字段各行值對應的hash值做爲數組下標,其中可能會有hash衝突,存儲對應的那一行數據的地址。
當直接執行select * from users where name = 'tom';
的時候,數據庫會計算'tom'的hash值,獲得數組下標,而後直接從數據中取出數據並拿到鎖對應的那一行數據的地址,進而在數據表文件中查詢那一行數據。
可是當執行select * from users where name > 'tom';
時,索引將再也不起做用。 哈希表並非有序的,能夠快速的精確查詢,可是不支持範圍查詢。因此只適合作等值查詢,區間查詢的效率很低!
好像字典裏的索引去掉了排序。
二叉搜索樹的特色:每一個節點的左兒子小於父節點,父節點又小於右兒子。
因爲徹底平衡二叉搜索樹是有序的,因此支持範圍查找。
二叉樹是搜索效率最高的,可是實際上大多數的數據庫存儲卻並不使用二叉樹。其緣由是,索引不止存在內存中,還要寫到磁盤上。爲了讓一個查詢儘可能少地讀磁盤,就必須讓查詢過程訪問儘可能少的數據塊。那麼,咱們就不該該使用二叉樹,而是要使用「N 叉」樹。「N」取決於數據塊的大小。
一樣的數據,B樹表示的要比徹底平衡二叉搜索樹要 "矮",緣由在於B樹中的一個節點能夠存儲多個元素。
B+樹是B樹的升級版,只是把非葉子節點冗餘一下,這麼作的好處是 爲了提升範圍查找的效率。
一樣的元素,B+樹的表示要比B樹要 "胖",緣由在於全部的葉子結點包含了所有元素的信息,及指向含這些元素記錄的指針,且葉子結點自己依關鍵字的大小自小而大順序連接。
非葉子節點也會冗餘一份全部的中間節點元素,它們是最大(或最小)元素。
提升查詢索引時的磁盤IO效率,而且能夠提升範圍查詢的效率,而且B+樹裏的元素也是有序的,葉子節點造成有序鏈表,便於範圍查詢。
參考的這篇博客
對於主健,oracle/sql server/mysql等都會自動創建惟一索引。
主鍵索引是惟一索引的特殊類型。= 惟一索引+ not null
不容許兩行具備相同的索引值。
例外狀況是,若是該字段被設置爲容許NULL值,則插入該字段的值能夠包含多個NULL值。
它們的一些比較:
(1)對於主健/unique constraint , oracle/sql server/mysql等都會自動創建惟一索引; (2)主鍵不必定只包含一個字段,因此若是你在主鍵的其中一個字段建惟一索引仍是必要的; (3)主健可做外健,惟一索引不可; (4)主健不可爲空,惟一索引可; (5)主健也但是多個字段的組合; (6)主鍵與惟一索引不一樣的是: a.有not null屬性; b.每一個表只能有一個。 (7)主鍵索引比普通索引得查詢速度快。由於普通索引樹上並無完整得數據,而是先找到主鍵後,再到主鍵索引樹上去獲取所需得數據,這個操做被稱爲回表。當你沒有設主鍵或者主鍵忽然被刪除時,會自動創建一個主鍵rowid,保證回表等功能的正常運行。
由關鍵字KEY或INDEX定義的索引。惟一任務是加快對數據的訪問速度。
在多個列上創建索引,這種索引叫作複合索引(組合索引)。https://www.cnblogs.com/zjdxr...
用於在一篇文章中,檢索文本信息的。
explain 語句 extra列出現using index
若是執行的語句是 select ID from T where k between 3 and 5
,這時只須要查 ID 的值,而 ID 的值已經在 k 索引樹上了,所以能夠直接提供查詢結果,不須要回表。也就是說,在這個查詢裏,索引 k 已經「覆蓋了」咱們的查詢需求,咱們稱爲覆蓋索引。
區分度最高的放在最左邊。
涉及到在創建複合索引的時候,如何安排索引內的字段順序的問題。
若是咱們建立了(username,sex,age)的複合索引,那麼其實至關於建立了:
(username,sex,age),(username,sex)、(username,age)、(username)四個索引,這被稱爲最佳左前綴特性。
所以咱們在建立複合索引時應該將最經常使用做限制條件的列放在最左邊,依次遞減。
https://www.cnblogs.com/jiqin...
在索引遍歷過程當中,對索引中包含的字段先作判斷,直接過濾掉不知足條件的記錄,減小回表次數。
1.主鍵長度越小,普通索引的葉子節點就越小,普通索引佔用的空間也就越小。
2.索引的維護的主要代價是因爲有序性的維護引發的,若是每次插入一條新記錄,都是追加操做,都不涉及到挪動其餘記錄,也不會觸發葉子節點的分裂,這樣就能夠減小索引的維護代價。
1.在沒有建索引的狀況下,數據庫查找某一條數據,就必須進行全表掃描了,對全部數據進行一次遍歷,查找出符合條件的記錄。
2.where 子句中對字段進行 null 值判斷。
3.where 子句中使用!=或<>操做符
4.where 子句中使用 or 來鏈接條件
5.in 和 not in
6.使用非打頭字母搜索
7.在 where 子句中對字段進行表達式操做
8.在where子句中對字段進行函數操做
9.在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算
10.複合索引狀況下,使用到該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用,而且應儘量的讓字段順序與索引順序相一致。
https://blog.csdn.net/qq_3631...
在多用戶環境中,在同一時間可能會有多個用戶更新相同的記錄,這會產生衝突。這就是併發性問題。
典型的衝突有:
1.丟失更新:一個事務的更新覆蓋了其它事務的更新結果,就是所謂的更新丟失。例如:用戶A把值從6改成2,用戶B把值從2改成6,則用戶A丟失了他的更新。
2.髒讀:當一個事務讀取其它完成一半事務的記錄時,就會發生髒讀取。例如:用戶A,B看到的值都是6,用戶B把值改成2,用戶A讀到的值仍爲6。
爲了解決這些併發帶來的問題。 咱們須要引入併發控制機制。
mysql採用讀寫鎖來進行併發控制。
兩種鎖的思想——樂觀鎖和悲觀鎖。
指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態,好比表鎖,行鎖。獨佔鎖、java中 synchronized就是悲觀鎖。
依靠數據庫提供的鎖機制實現。適用於數據爭用嚴重/重試代價大的場景。
缺點:
在多線程競爭下,加鎖、釋放鎖會致使比較多的上下文切換和調度延時,引發性能問題。
一個線程持有鎖會致使其它全部須要此鎖的線程掛起,對長事務而言,這樣的開銷每每沒法承受。
若是一個優先級高的線程等待一個優先級低的線程釋放鎖會致使優先級倒置,引發性能風險。
參考悲觀鎖實踐
樂觀鎖假設認爲數據通常狀況下不會形成衝突,因此在數據進行提交更新的時候,纔會正式對數據的衝突與否進行檢測,若是發現衝突了,則讓返回用戶錯誤的信息,讓用戶決定如何去作。即衝突檢測和數據更新
兩步。樂觀鎖不能解決髒讀的問題。ReadCommitted、REPEATABLE READ隔離級別是經過MVCC實現樂觀鎖的,還有hibernate中@Version註解。
依靠數據版本記錄機制實現。
能夠經過使用時間戳(timestamp)字段也能夠定義version字段(自增加的整數)做爲版本標識,當讀取數據時,將版本標識字段的值一同讀出,在更新提交的時候檢查當前數據庫中數據的當前版本標識和本身更新前取到的版本標識進行對比,若是一致則予以更新,不然就是版本衝突返回給用戶。
適用於數據爭用不嚴重/重試代價不大/須要相應速度快的場景。
缺點:在高併發的狀況下,樂觀鎖並不適合。由於致數據庫會有不少的更新失敗,處理異常再次執行的狀況。
瞭解下Compare and Swap(CAS)。
CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知此次競爭中失敗,並能夠再次嘗試。CAS是一種非阻塞式的同步方式。CAS 操做包含三個操做數 —— 內存位置(V)、預期原值(A)和新值(B)。若是內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值。不然,處理器不作任何操做。不管哪一種狀況,它都會在 CAS 指令以前返回該位置的值。(在 CAS 的一些特殊狀況下將僅返回 CAS 是否成功,而不提取當前值。)CAS 有效地說明了「我認爲位置 V 應該包含值 A;若是包含該值,則將 B 放到這個位置;不然,不要更改該位置,只告訴我這個位置如今的值便可。」這其實和樂觀鎖的衝突檢查+數據更新的原理是同樣的。
參考java對CAS的支持
提升併發性的方式就是讓鎖定的對象更有選擇性,只鎖定部分數據,而不是全部的資源,這就是鎖粒度要考慮的問題。
MySQL數據庫的MyISAM引擎支持表鎖。
當你對一張表進行修改時,會鎖死整張表,其餘的請求須要在修改完成釋放鎖才能繼續。
特色:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的機率最高,併發度最低。
更適合於查詢爲主的場景。
MySQL最基本的鎖策略,開銷最小的策略。
MyISAM引擎中表鎖分爲表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)。
InnoDB引擎中表鎖分爲意向共享鎖(IS)和意向排他鎖(IX)。意向鎖是InnoDB自動加的,不須要用戶干預。
對於讀操做,能夠增長讀鎖,一旦數據表被加上讀鎖,其餘請求能夠對該表再次增長讀鎖,可是不能增長寫鎖
。(當一個請求在讀數據時,其餘請求也能夠讀,可是不能寫,本身也不能寫,由於一旦另一個線程寫了數據,就會致使當前線程讀取到的數據不是最新的了。這就是不可重複讀現象)
實例
對於寫操做,能夠增長寫鎖,一旦數據表被加上寫鎖,這會阻塞其餘用戶對該表的全部讀寫操做
。(當一個請求在寫數據時,其餘請求不能執行任何操做,由於在當前事務提交以前,其餘的請求沒法看到本次修改的內容。這有可能產生髒讀、不可重複讀和幻讀)
讀鎖和寫鎖都是阻塞鎖。讀和寫是串行的。同時請求的狀況下,寫進程先得到鎖。
只有沒有寫鎖時,其餘讀取用戶才能得到讀鎖,讀鎖之間是不相互阻塞的。
MyISAM 在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行更新操做 (UPDATE、DELETE、INSERT 等)前,會自動給涉及的表加寫鎖,這個過程並不須要用戶干預,所以,用戶通常不須要直接用LOCK TABLE命令給MyISAM表顯式加鎖。
若是用戶想要顯示的加鎖可使用如下命令:鎖定表:LOCK TABLES tbl_name {READ | WRITE},[ tbl_name {READ | WRITE},…]
解鎖表:UNLOCK TABLES
注意:當使用 LOCK TABLES 時,不只須要一次鎖定用到的全部表,並且,同一個表在 SQL 語句中出現多少次,就要經過與 SQL 語句中相同的別名鎖定多少次,不然也會出錯!
參考資料
表示事務準備給數據行加入共享鎖,也就是說一個數據行加共享鎖前必須先取得該表的IS鎖
相似上面,表示事務準備給數據行加入排他鎖,說明事務在一個數據行加排他鎖前必須先取得該表的IX鎖。
InnoDB存儲引擎既支持行級鎖,也支持表級鎖,但默認狀況下是採用行級鎖。
當你對一張表的某一行數據修改時,會鎖死這一行數據,對錶中其餘的數據沒影響。
行級鎖能夠最大程度地支持併發處理(最大鎖開銷),InnoDB和XtraDB,還有其餘一些存儲引擎中實現了行級鎖,行級鎖只在存儲引擎層實現,而MySQL服務器層沒有實現。
特色:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的機率最低,併發度也最高。
適合於大量按索引條件併發更新少許不一樣的數據,同時又有併發查詢的應用。
開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度通常。
BDB 存儲引擎。
也稱爲讀鎖,讀鎖容許多個鏈接能夠同一時刻併發的讀取同一資源,互不干擾。但任何事務都不能對數據進行修改(獲取數據上的排他鎖),直到已釋放全部共享鎖。
在查詢語句後面增長LOCK IN SHARE MODE
,Mysql會對查詢結果中的每行都加共享鎖。
也稱爲寫鎖,一個寫鎖會阻塞其餘的寫鎖或讀鎖,保證同一時刻只有一個鏈接能夠寫入數據,同時防止其餘用戶對這個數據的讀寫。
在查詢語句後面增長FOR UPDATE
,Mysql會對查詢結果中的每行都加排他鎖。
InnoDB目前處理死鎖的方法是,將持有最小行級排他鎖的事務進行回滾(相對比較簡單的死鎖回滾方法)
越複雜的系統,越能檢測到死鎖的循環依賴,並當即返回一個錯誤,不然會致使出現很是慢的查詢。
參考資料1
參考資料2
要麼都作,要麼都不作的。爲了不出現邏輯處理失敗致使的髒數據等問題。
具體有這些問題:
1.髒讀
:A事務中讀取到了B事務中還沒有提交的數據。
2.不可重複讀
:同一個事務中兩次讀取的數據的內容不一致。其餘事務對同一條數據update了(提交了),時間上的問題。
3.幻讀
:同一個事務中兩次count的數量不一致。其餘事務對該表insert或delete了(提交了)。
定義了事務中,數據庫讀寫方面的控制範圍。
1.Serializable
串行化
最嚴格、最安全的級別,可讀,不可寫。像java中的鎖,至關於鎖表,對於全部的query,即便是查詢,也會加上讀鎖,避免其餘事務對數據的修改。操做數據必須等待另外一個事務結束。
能夠解決髒讀、不可重複讀和幻讀,但事務串行執行,資源消耗最大,數據庫系統的併發處理能力大大下降,因此它不會被用到生產系統中。
2.REPEATABLE READ
可重複讀(mysql默認) 保證了在同一個事務中屢次讀取一樣記錄的結果是一致的。由於只能讀取在它開始以前已經提交的事務對數據庫的修改,在它開始之後,全部其餘事務對數據庫的修改對它來講均不可見,避免了「髒讀取」和「不可重複讀」的狀況,但當其餘事務往表中新增數據時,有幻讀的問題。
不過mysql的innodb引擎採用了多版本併發控制(MVCC)機制能夠解決幻讀問題。
mysql環境作個實驗:
一、session1啓動一個事務start transaction;
;
二、執行一條查詢語句SELECT * FROM class WHERE teacher_id=1;
,觀察結果;
三、session2啓動一個事務,執行更新或刪除teacher_id=1
的命令,而後執行commit;
,在session1中查看teacher_id=1
行,發現並無被更新或刪除。這裏變體現了「可重複讀」,無論別的事物修改或刪除了什麼,即使是提交了,在當前事務下,重複讀取的結果是同樣的。
;
四、session2執行一條插入語句,而後提交,在session1中count一下發現總條數並無加1,不是說可重複讀有幻讀問題嗎?這裏是mysql的MVCC機制解決了幻讀問題。
3.READ COMMITTED
讀取已提交(oracle默認,開發裏經常使用)
其餘事務對數據庫的修改,只要已經提交,其修改的結果就是可見的,與這兩個事務開始的前後順序無關。這種隔離等級避免了髒讀,但屢次讀取的數據結果可能出現不一致的狀況,即解決不可重複讀和幻讀問題。
能夠修改數據庫的事務隔離級別set transaction isolation level read committed;
重複上面的實驗,看看有沒有不可重複讀和幻讀問題。
4Read Uncommitted
讀未提交
查詢能夠讀取到其餘事務正在修改的數據,即便其餘事務的修改尚未提交。這種隔離等級哪一個問題都不能解決。
1.查看當前會話隔離級別:select @@tx_isolation;
2.查看系統當前隔離級別:select @@global.tx_isolation;
3.設置當前會話隔離級別:set session transaction isolatin level repeatable read;
4.設置系統當前隔離級別:set global transaction isolation level repeatable read;
5.命令行,開始事務時:set autocommit=on;
或者 start transaction;
6.讓當前會話的事務自動提交(更新、插入操做後會自動提交):set @@session.autocommit=1;
7.設置手動提交事務:set autocommit = 0;
8.查看當前事務id:SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();
表中能夠查到正在執行的事務信息。
它使得大部分支持行鎖的事務引擎,再也不單純的使用行鎖來進行數據庫的併發控制,取而代之的是,把數據庫的行鎖與行的多個版本結合起來,只須要很小的開銷,就能夠實現非鎖定讀,從而大大提升數據庫系統的併發性能。
innodb對每一行加上了兩個隱含的列,一列存儲行被更新的」時間」,另外一列存儲行被刪除的」時間」。 並非絕對的時間,而是與時間對應的數據庫系統的版本號,每當一個事務開始的時候,innodb都會給這個事務分配一個遞增的版本號,因此版本號也能夠被認爲是事務號
。 對於每個」查詢」語句,innodb都會把這個查詢語句的版本號同這個查詢語句對應的行的版本號進行對比,而後結合不一樣的事務隔離等級,來決定是否返回該行。
每一行數據均可能存在多個版本,那麼這些行組合起來獲得的結果集的版本就更是不可勝數,這就是數據庫多版本的由來。MVCC就是經過事務發生的不一樣的時間點,與數據行的版原本進行對比,從而取回與事務開始的時間點相一致的數據,來實現非阻塞的一致讀。
好比對於select語句,只有同時知足了下面兩個條件的行,纔會被返回: 1.行的被修改版本號小於或者等於該事務號;
2.行的被刪除版本號要麼沒有被定義(說明該行沒有被刪除過),要麼大於事務的版本號(說明該行是被該事務後面啓動的事務刪除的,repeatable read隔離等級下,後開始的事務對數據的影響不該該被先開始的事務看見,因此該行應該被返回)。
深刻理解
好比INSERT語句,對新插入的行,行的更新版本被修改成該事務的事務號;
對於刪除,innodb直接把該行的被刪除版本號設置爲當前的事務號,至關於標記爲刪除,而不是實際刪除;
在更新行的時候,innodb會把原來的行復制一份到回滾段中,並把當前的事務號做爲該行的更新版本。
讀取數據的時候,innodb幾乎不用得到任何鎖, 每一個查詢都經過版本檢查,只得到本身須要的數據版本,從而大大提升了系統的併發度。
爲了實現多版本,innodb必須對每行增長相應的字段來存儲版本信息,同時須要維護每一行的版本信息,並且在檢索行的時候,須要進行版本的比較,於是會下降查詢的效率;
innodb還必須按期清理再也不須要的行版本,及時回收空間,這也增長了一些開銷。
另外,數據庫執行了大事務狀況下,數據庫僅是頻繁更新,沒有插入新數據,也會致使表空間佔用愈來愈大,由於innodb會把被修改數據的前映像存放到稱爲回滾段的公共表空間中,並且對於索引和表中的行的多個版本,若是innodb來不及purge,或者這些行由於要提供一致讀而不能被purge,就會佔用愈來愈多的空間,甚至有可能短期撐爆你的硬盤。因此應用程序中須要合理控制事務的大小!
優化
另外,因爲innodb的mvcc策略的實施,char數據類型相對於varchar類型幾乎沒有任何優點,反而varchar列可能節省更多的存儲空間,建議使用varchar數據類型。???
MVCC因爲其實現原理,只支持read committed和repeatable read隔離等級。
MVCC自己不支持read uncommitted等級,因此能夠經過設置transaction_isolation = read uncommitted 來禁用MVCC。
可是任何改變innodb默認隔離等級的操做,都會起到innodb_locks_unsafe_for_binlog=off相似的效果,這會致使諸如insert into t select * from t_src 之類的語句再也不給源表t_src加鎖,也再也不使用innodb的間隙鎖,從而產生幻讀,直接致使binlog中記錄的sql語句不能正確的串行化,從而主從數據庫的數據再也不一致,並且基於binlog的增量備份也再也不有效,因此除非不須要記錄binlog,不然別這麼作。固然咱們能夠這樣作來優化從庫的性能,由於從庫不須要記錄binlog。