MySQL 一直是本人很薄弱的部分,後面會多輸出 MySQL 的文章貢獻給你們,畢竟 MySQL 涉及到數據存儲、鎖、磁盤尋道、分頁等操做系統概念,並且互聯網對 MySQL 的注重程度是不言而喻的,後面要加緊對 MySQL 的研究。寫的若是很差,還請你們見諒。php
非關係型數據庫(感受翻譯不是很準確)稱爲 NoSQL
,也就是 Not Only SQL,不只僅是 SQL。非關係型數據庫不須要寫一些複雜的 SQL 語句,其內部存儲方式是以 key-value
的形式存在能夠把它想象成電話本的形式,每一個人名(key)對應電話(value)。常見的非關係型數據庫主要有 Hbase、Redis、MongoDB 等。非關係型數據庫不須要通過 SQL 的重重解析,因此性能很高;非關係型數據庫的可擴展性比較強,數據之間沒有耦合性,碰見須要新加字段的需求,就直接增長一個 key-value 鍵值對便可。html
關係型數據庫以表格
的形式存在,以行和列
的形式存取數據,關係型數據庫這一系列的行和列被稱爲表,無數張表組成了數據庫
,常見的關係型數據庫有 Oracle、DB二、Microsoft SQL Server、MySQL等。關係型數據庫可以支持複雜的 SQL 查詢,可以體現出數據之間、表之間的關聯關係;關係型數據庫也支持事務,便於提交或者回滾。mysql
它們之間的劣勢都是基於對方的優點來知足的。算法
一說到 MySQL 事務,你確定能想起來四大特性:原子性
、一致性
、隔離性
、持久性
,下面再對這事務的四大特性作一個描述sql
原子性(Atomicity)
: 原子性指的就是 MySQL 中的包含事務的操做要麼所有成功
、要麼所有失敗回滾
,所以事務的操做若是成功就必需要所有應用到數據庫,若是操做失敗則不能對數據庫有任何影響。這裏涉及到一個概念,什麼是 MySQL 中的事務?數據庫
事務是一組操做,組成這組操做的各個單元,要不全都成功要不全都失敗,這個特性就是事務。segmentfault
在 MySQL 中,事務是在引擎層實現的,只有使用
innodb
引擎的數據庫或表才支持事務。緩存
一致性(Consistency)
:一致性指的是一個事務在執行先後其狀態一致。好比 A 和 B 加起來的錢一共是 1000 元,那麼無論 A 和 B 之間如何轉帳,轉多少次,事務結束後兩個用戶的錢加起來還得是 1000,這就是事務的一致性。安全
持久性(Durability)
: 持久性指的是一旦事務提交,那麼發生的改變就是永久性的,即便數據庫遇到特殊狀況好比故障的時候也不會產生干擾。數據結構
隔離性(Isolation)
:隔離性須要重點說一下,當多個事務同時進行時,就有可能出現髒讀(dirty read)
、不可重複讀(non-repeatable read)
、幻讀(phantom read)
的狀況,爲了解決這些併發問題,提出了隔離性的概念。
髒讀:事務 A 讀取了事務 B 更新後的數據,可是事務 B 沒有提交,而後事務 B 執行回滾操做,那麼事務 A 讀到的數據就是髒數據
不可重複讀:事務 A 進行屢次讀取操做,事務 B 在事務 A 屢次讀取的過程當中執行更新操做並提交,提交後事務 A 讀到的數據不一致。
幻讀:事務 A 將數據庫中全部學生的成績由 A -> B,此時事務 B 手動插入了一條成績爲 A 的記錄,在事務 A 更改完畢後,發現還有一條記錄沒有修改,那麼這種狀況就叫作出現了幻讀。
SQL的隔離級別有四種,它們分別是讀未提交(read uncommitted)
、讀已提交(read committed)
、可重複讀(repetable read)
和 串行化(serializable)
。下面分別來解釋一下。
讀未提交:讀未提交指的是一個事務在提交以前,它所作的修改就可以被其餘事務所看到。
讀已提交:讀已提交指的是一個事務在提交以後,它所作的變動纔可以讓其餘事務看到。
可重複讀:可重複讀指的是一個事務在執行的過程當中,看到的數據是和啓動時看到的數據是一致的。未提交的變動對其餘事務不可見。
串行化:顧名思義是對於同一行記錄,寫
會加寫鎖
,讀
會加讀鎖
。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。
這四個隔離級別能夠解決髒讀、不可重複讀、幻象讀這三類問題。總結以下
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 | 容許 | 容許 | 容許 |
讀已提交 | 不容許 | 容許 | 容許 |
可重複讀 | 不容許 | 不容許 | 容許 |
串行化 | 不容許 | 不容許 | 不容許 |
其中隔離級別由低到高是:讀未提交 < 讀已提交 < 可重複讀 < 串行化
隔離級別越高,越可以保證數據的完整性和一致性,可是對併發的性能影響越大。大多數數據庫的默認級別是讀已提交(Read committed)
,好比 Sql Server、Oracle ,可是 MySQL 的默認隔離級別是 可重複讀(repeatable-read)
。
MySQL 常見的存儲引擎,可使用
SHOW ENGINES
複製代碼
命令,來列出全部的存儲引擎
能夠看到,InnoDB 是 MySQL 默認支持的存儲引擎,支持事務、行級鎖定和外鍵。
在 5.1 版本以前,MyISAM 是 MySQL 的默認存儲引擎,MyISAM 併發性比較差,使用的場景比較少,主要特色是
不支持事務
操做,ACID 的特性也就不存在了,這一設計是爲了性能和效率考慮的。
不支持外鍵
操做,若是強行增長外鍵,MySQL 不會報錯,只不過外鍵不起做用。
MyISAM 默認的鎖粒度是表級鎖
,因此併發性能比較差,加鎖比較快,鎖衝突比較少,不太容易發生死鎖的狀況。
MyISAM 會在磁盤上存儲三個文件,文件名和表名相同,擴展名分別是 .frm(存儲表定義)
、.MYD(MYData,存儲數據)
、MYI(MyIndex,存儲索引)
。這裏須要特別注意的是 MyISAM 只緩存索引文件
,並不緩存數據文件。
MyISAM 支持的索引類型有 全局索引(Full-Text)
、B-Tree 索引
、R-Tree 索引
Full-Text 索引:它的出現是爲了解決針對文本的模糊查詢效率較低的問題。
B-Tree 索引:全部的索引節點都按照平衡樹的數據結構來存儲,全部的索引數據節點都在葉節點
R-Tree索引:它的存儲方式和 B-Tree 索引有一些區別,主要設計用於存儲空間和多維數據的字段作索引,目前的 MySQL 版本僅支持 geometry 類型的字段做索引,相對於 BTREE,RTREE 的優點在於範圍查找。
數據庫所在主機若是宕機,MyISAM 的數據文件容易損壞,並且難以恢復。
增刪改查性能方面:SELECT 性能較高,適用於查詢較多的狀況
自從 MySQL 5.1 以後,默認的存儲引擎變成了 InnoDB 存儲引擎,相對於 MyISAM,InnoDB 存儲引擎有了較大的改變,它的主要特色是
可重複讀(repetable-read)
、經過MVCC(併發版本控制)來實現的。可以解決髒讀
和不可重複讀
的問題。行級鎖
,併發性能比較好,會發生死鎖的狀況。.frm文件存儲表結構
定義,可是不一樣的是,InnoDB 的表數據與索引數據是存儲在一塊兒的,都位於 B+ 數的葉子節點上,而 MyISAM 的表數據和索引數據是分開的。鎖粒度方面
:因爲鎖粒度不一樣,InnoDB 比 MyISAM 支持更高的併發;InnoDB 的鎖粒度爲行鎖、MyISAM 的鎖粒度爲表鎖、行鎖須要對每一行進行加鎖,因此鎖的開銷更大,可是能解決髒讀和不可重複讀的問題,相對來講也更容易發生死鎖可恢復性上
:因爲 InnoDB 是有事務日誌的,因此在產生因爲數據庫崩潰等條件後,能夠根據日誌文件進行恢復。而 MyISAM 則沒有事務日誌。查詢性能上
:MyISAM 要優於 InnoDB,由於 InnoDB 在查詢過程當中,是須要維護數據緩存,並且查詢過程是先定位到行所在的數據塊,而後在從數據塊中定位到要查找的行;而 MyISAM 能夠直接定位到數據所在的內存地址,能夠直接找到數據。表結構文件上
: MyISAM 的表結構文件包括:.frm(表結構定義),.MYI(索引),.MYD(數據);而 InnoDB 的表數據文件爲:.ibd和.frm(表結構定義);這道題應該從 MySQL 架構來理解,咱們能夠把 MySQL 拆解成幾個零件,以下圖所示
大體上來講,MySQL 能夠分爲 Server
層和 存儲引擎
層。
Server 層包括鏈接器、查詢緩存、分析器、優化器、執行器,包括大多數 MySQL 中的核心功能,全部跨存儲引擎的功能也在這一層實現,包括 存儲過程、觸發器、視圖等。
存儲引擎層包括 MySQL 常見的存儲引擎,包括 MyISAM、InnoDB 和 Memory 等,最經常使用的是 InnoDB,也是如今 MySQL 的默認存儲引擎。存儲引擎也能夠在建立表的時候手動指定,好比下面
CREATE TABLE t (i INT) ENGINE = <Storage Engine>;
複製代碼
而後咱們就能夠探討 MySQL 的執行過程了
首先須要在 MySQL 客戶端登錄才能使用,因此須要一個鏈接器
來鏈接用戶和 MySQL 數據庫,咱們通常是使用
mysql -u 用戶名 -p 密碼
複製代碼
來進行 MySQL 登錄,和服務端創建鏈接。在完成 TCP 握手
後,鏈接器會根據你輸入的用戶名和密碼驗證你的登陸身份。若是用戶名或者密碼錯誤,MySQL 就會提示 Access denied for user,來結束執行。若是登陸成功後,MySQL 會根據權限表中的記錄來斷定你的權限。
鏈接完成後,你就能夠執行 SQL 語句了,這行邏輯就會來到第二步:查詢緩存。
MySQL 在獲得一個執行請求後,會首先去 查詢緩存
中查找,是否執行過這條 SQL 語句,以前執行過的語句以及結果會以 key-value
對的形式,被直接放在內存中。key 是查詢語句,value 是查詢的結果。若是經過 key 可以查找到這條 SQL 語句,就直接返回 SQL 的執行結果。
若是語句不在查詢緩存中,就會繼續後面的執行階段。執行完成後,執行結果就會被放入查詢緩存中。能夠看到,若是查詢命中緩存,MySQL 不須要執行後面的複雜操做,就能夠直接返回結果,效率會很高。
可是查詢緩存不建議使用
爲何呢?由於只要在 MySQL 中對某一張表執行了更新操做,那麼全部的查詢緩存就會失效,對於更新頻繁的數據庫來講,查詢緩存的命中率很低。
若是沒有命中查詢,就開始執行真正的 SQL 語句。
詞法分析
,你寫的 SQL 就是由多個字符串和空格組成的一條 SQL 語句,MySQL 須要識別出裏面的字符串是什麼,表明什麼。語法分析
,根據詞法分析的結果, 語法分析器會根據語法規則,判斷你輸入的這個 SQL 語句是否知足 MySQL 語法。若是 SQL 語句不正確,就會提示 You have an error in your SQL syntax通過分析器的詞法分析和語法分析後,你這條 SQL 就合法
了,MySQL 就知道你要作什麼了。可是在執行前,還須要進行優化器的處理,優化器會判斷你使用了哪一種索引,使用了何種鏈接,優化器的做用就是肯定效率最高的執行方案。
MySQL 經過分析器知道了你的 SQL 語句是否合法,你想要作什麼操做,經過優化器知道了該怎麼作效率最高,而後就進入了執行階段,開始執行這條 SQL 語句
在執行階段,MySQL 首先會判斷你有沒有執行這條語句的權限,沒有權限的話,就會返回沒有權限的錯誤。若是有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。對於有索引的表,執行的邏輯也差很少。
至此,MySQL 對於一條語句的執行過程也就完成了。
咱們在編寫一個查詢語句的時候
SELECT DISTINCT
< select_list >
FROM
< left_table > < join_type >
JOIN < right_table > ON < join_condition >
WHERE
< where_condition >
GROUP BY
< group_by_list >
HAVING
< having_condition >
ORDER BY
< order_by_condition >
LIMIT < limit_number >
複製代碼
它的執行順序你知道嗎?這道題就給你一個回答。
首先,對 SELECT 語句執行查詢時,對FROM
關鍵字兩邊的表執行鏈接,會造成笛卡爾積
,這時候會產生一個虛表VT1(virtual table)
首先先來解釋一下什麼是
笛卡爾積
如今咱們有兩個集合 A = {0,1} , B = {2,3,4}
那麼,集合 A * B 獲得的結果就是
A * B = {(0,2)、(1,2)、(0,3)、(1,3)、(0,4)、(1,4)};
B * A = {(2,0)、{2,1}、{3,0}、{3,1}、{4,0}、(4,1)};
上面 A * B 和 B * A 的結果就能夠稱爲兩個集合相乘的
笛卡爾積
咱們能夠得出結論,A 集合和 B 集合相乘,包含了集合 A 中的元素和集合 B 中元素之和,也就是 A 元素的個數 * B 元素的個數
再來解釋一下什麼是虛表
在 MySQL 中,有三種類型的表
一種是
永久表
,永久表就是建立之後用來長期保存數據的表一種是
臨時表
,臨時表也有兩類,一種是和永久表同樣,只保存臨時數據,可是可以長久存在的;還有一種是臨時建立的,SQL 語句執行完成就會刪除。一種是
虛表
,虛表其實就是視圖
,數據可能會來自多張表的執行結果。
而後對 FROM 鏈接的結果進行 ON 篩選,建立 VT2,把符合記錄的條件存在 VT2 中。
第三步,若是是 OUTER JOIN(left join、right join)
,那麼這一步就將添加外部行,若是是 left join 就把 ON 過濾條件的左表添加進來,若是是 right join ,就把右表添加進來,從而生成新的虛擬表 VT3。
第四步,是執行 WHERE 過濾器,對上一步生產的虛擬表引用 WHERE 篩選,生成虛擬表 VT4。
WHERE 和 ON 的區別
應用
根據 group by 字句中的列,會對 VT4 中的記錄進行分組操做,產生虛擬機表 VT5。果應用了group by,那麼後面的全部步驟都只能獲得的 VT5 的列或者是聚合函數(count、sum、avg等)。
緊跟着 GROUP BY 字句後面的是 HAVING,使用 HAVING 過濾,會把符合條件的放在 VT6
第七步纔會執行 SELECT 語句,將 VT6 中的結果按照 SELECT 進行刷選,生成 VT7
在第八步中,會對 TV7 生成的記錄進行去重操做,生成 VT8。事實上若是應用了 group by 子句那麼 distinct 是多餘的,緣由一樣在於,分組的時候是將列中惟一的值分紅一組,同時只爲每一組返回一行記錄,那麼因此的記錄都將是不相同的。
應用 order by 子句。按照 order_by_condition 排序 VT8,此時返回的一個遊標,而不是虛擬表。sql 是基於集合的理論的,集合不會預先對他的行排序,它只是成員的邏輯集合,成員的順序是可有可無的。
SQL 語句執行的過程以下
什麼是臨時表?MySQL 在執行 SQL 語句的過程當中,一般會臨時建立一些存儲中間結果集
的表,臨時表只對當前鏈接可見,在鏈接關閉時,臨時表會被刪除並釋放全部表空間。
臨時表分爲兩種:一種是內存臨時表
,一種是磁盤臨時表
,什麼區別呢?內存臨時表使用的是 MEMORY 存儲引擎,而臨時表採用的是 MyISAM 存儲引擎。
MEMORY 存儲引擎:
memory
是 MySQL 中一類特殊的存儲引擎,它使用存儲在內容中的內容來建立表,並且數據所有放在內存中。每一個基於 MEMORY 存儲引擎的表實際對應一個磁盤文件。該文件的文件名與表名相同,類型爲frm
類型。而其數據文件,都是存儲在內存中,這樣有利於數據的快速處理,提升整個表的效率。MEMORY 用到的不多,由於它是把數據存到內存中,若是內存出現異常就會影響數據。若是重啓或者關機,全部數據都會消失。所以,基於 MEMORY 的表的生命週期很短,通常是一次性的。
MySQL 會在下面這幾種狀況產生臨時表
使用 UNION 查詢:UNION 有兩種,一種是UNION
,一種是 UNION ALL
,它們都用於聯合查詢;區別是 使用 UNION 會去掉兩個表中的重複數據,至關於對結果集作了一下去重(distinct)
。使用 UNION ALL,則不會排重,返回全部的行。使用 UNION 查詢會產生臨時表。
使用 TEMPTABLE 算法
或者是 UNION 查詢中的視圖。TEMPTABLE 算法是一種建立臨時表的算法,它是將結果放置到臨時表中,意味這要 MySQL 要先建立好一個臨時表,而後將結果放到臨時表中去,而後再使用這個臨時表進行相應的查詢。
ORDER BY 和 GROUP BY 的子句不同時也會產生臨時表。
DISTINCT 查詢而且加上 ORDER BY 時;
SQL中用到 SQL_SMALL_RESULT 選項時;若是查詢結果比較小的時候,能夠加上 SQL_SMALL_RESULT 來優化,產生臨時表
FROM 中的子查詢;
EXPLAIN 查看執行計劃結果的 Extra 列中,若是使用 Using Temporary
就表示會用到臨時表。
索引是存儲在一張表中特定列上的數據結構
,索引是在列上建立的。而且,索引是一種數據結構。
在 MySQL 中,主要有下面這幾種索引
全局索引(FULLTEXT)
:全局索引,目前只有 MyISAM 引擎支持全局索引,它的出現是爲了解決針對文本的模糊查詢效率較低的問題。哈希索引(HASH)
:哈希索引是 MySQL 中用到的惟一 key-value 鍵值對的數據結構,很適合做爲索引。HASH 索引具備一次定位的好處,不須要像樹那樣逐個節點查找,可是這種查找適合應用於查找單個鍵的狀況,對於範圍查找,HASH 索引的性能就會很低。B-Tree 索引
:B 就是 Balance 的意思,BTree 是一種平衡樹,它有不少變種,最多見的就是 B+ Tree,它被 MySQL 普遍使用。R-Tree 索引
:R-Tree 在 MySQL 不多使用,僅支持 geometry 數據類型,支持該類型的存儲引擎只有MyISAM、BDb、InnoDb、NDb、Archive幾種,相對於 B-Tree 來講,R-Tree 的優點在於範圍查找。MySQL 中沒有 nvarchar 數據類型,因此直接比較的是 varchar 和 char 的區別
char
:表示的是定長
的字符串,當你輸入小於指定的數目,好比你指定的數目是 char(6)
,當你輸入小於 6 個字符的時候,char 會在你最後一個字符後面補空值。當你輸入超過指定容許最大長度後,MySQL 會報錯
varchar
: varchar 指的是長度爲 n 個字節的可變長度,而且是非Unicode
的字符數據。n 的值是介於 1 - 8000 之間的數值。存儲大小爲實際大小。
Unicode 是一種字符編碼方案,它爲每種語言中的每一個字符都設定了統一惟一的二進制編碼,以實現跨語言、跨平臺進行文本轉換、處理的要求
使用 char 存儲定長的數據很是方便、char 檢索效率高,不管你存儲的數據是否到了 10 個字節,都要去佔用 10 字節的空間
使用 varchar 能夠存儲變長的數據,但存儲效率沒有 char 高。
鏈接的方式主要有三種:外鏈接、內連接、交叉鏈接
外鏈接(OUTER JOIN)
:外鏈接分爲三種,分別是左外鏈接(LEFT OUTER JOIN 或 LEFT JOIN)
、右外鏈接(RIGHT OUTER JOIN 或 RIGHT JOIN)
、全外鏈接(FULL OUTER JOIN 或 FULL JOIN)
左外鏈接:又稱爲左鏈接,這種鏈接方式會顯示左表不符合條件的數據行,右邊不符合條件的數據行直接顯示 NULL
右外鏈接:也被稱爲右鏈接,他與左鏈接相對,這種鏈接方式會顯示右表不符合條件的數據行,左表不符合條件的數據行直接顯示 NULL
MySQL 暫不支持全外鏈接
內鏈接(INNER JOIN)
:結合兩個表中相同的字段,返回關聯字段相符的記錄。笛卡爾積(Cartesian product)
: 我在上面提到了笛卡爾積,爲了方便,下面再列出來一下。如今咱們有兩個集合 A = {0,1} , B = {2,3,4}
那麼,集合 A * B 獲得的結果就是
A * B = {(0,2)、(1,2)、(0,3)、(1,3)、(0,4)、(1,4)};
B * A = {(2,0)、{2,1}、{3,0}、{3,1}、{4,0}、(4,1)};
上面 A * B 和 B * A 的結果就能夠稱爲兩個集合相乘的
笛卡爾積
咱們能夠得出結論,A 集合和 B 集合相乘,包含了集合 A 中的元素和集合 B 中元素之和,也就是 A 元素的個數 * B 元素的個數
交叉鏈接的原文是Cross join
,就是笛卡爾積在 SQL 中的實現,SQL中使用關鍵字CROSS JOIN
來表示交叉鏈接,在交叉鏈接中,隨便增長一個表的字段,都會對結果形成很大的影響。
SELECT * FROM t_Class a CROSS JOIN t_Student b WHERE a.classid=b.classid
複製代碼
或者不用 CROSS JOIN,直接用 FROM 也能表示交叉鏈接的效果
SELECT * FROM t_Class a ,t_Student b WHERE a.classid=b.classid
複製代碼
若是表中字段比較多,不適宜用交叉鏈接,交叉鏈接的效率比較差。
全鏈接:全鏈接也就是 full join
,MySQL 中不支持全鏈接,可是可使用其餘鏈接查詢來模擬全鏈接,可使用 UNION
和 UNION ALL
進行模擬。例如
(select colum1,colum2...columN from tableA ) union (select colum1,colum2...columN from tableB )
或
(select colum1,colum2...columN from tableA ) union all (select colum1,colum2...columN from tableB );
複製代碼
使用 UNION 和 UNION ALL 的注意事項
經過 union 鏈接的 SQL 分別單獨取出的列數必須相同
使用 union 時,多個相等的行將會被合併,因爲合併比較耗時,通常不直接使用 union 進行合併,而是一般採用 union all 進行合併
WHERE
左側的條件查詢字段不要使用函數或者表達式EXPLAIN
命令優化你的 SELECT 查詢,對於複雜、效率低的 sql 語句,咱們一般是使用 explain sql 來分析這條 sql 語句,這樣方便咱們分析,進行優化。LIMIT 1
SELECT *
,而應該使用具體須要查詢的表字段。WHERE
字句中對字段進行 NULL
判斷WHERE
中使用 !=
或 <>
操做符BETWEEN AND
替代 IN
LIKE %abc%
不會走索引,而使用 LIKE abc%
會走索引ENUM
而不是VARCHAR
,如性別、星期、類型、類別等NOT NULL
水平分割:經過創建結構相同的幾張表分別存儲數據
垂直分割:將常常一塊兒使用的字段放在一個單獨的表中,分割後的表記錄之間是一一對應關係。
文章參考:
《極客時間》- MySQL實戰45講