MySQL 體系結構
連接層
:最上層是一些客戶端和連接服務,主要進行連接處理、授權認證、以及相關的安全方案等,服務器也會對安全接入的每個客戶端驗證其所具有的操作權限
服務層
:第二層結構主要完成大部分核心服務功能,比如SQL接口、SQL的分析優化、緩存的查詢、部分內置函數的執行,所有夸存儲引擎的功能也在這一層實現
引擎層
:存儲引擎真正負責了MySQL中數據的存儲和提取,服務器通過API和存儲引擎通信,
存儲層
:主要是將數據存儲在文件系統之上,并完成和存儲引擎的交互
存儲引擎
存儲引擎就是存儲數據、建立索引、更新、查詢數據等技術的實現方式,存儲引擎是基于表的。MySQL5.5版本開始默認的存儲引擎是InnoDB
,之前版本是MyISAM
。
建表時指定存儲引擎:
create table 表名(......) engine = InnoDB;
-
查看數據庫支持的存儲引擎:
show engines;
engines InnoDB
:
1、一種兼顧高可靠性和高性能的存儲引擎;MySQL5.5版本開始默認的存儲引擎。
2、支持事務
,DML 操作遵循事務的 ACID 模型;支持行級鎖
,提高了并發訪問性;支持外鍵
約束,保證了數據的完整性和正確性。
3、InnoDB 引擎的每張表都對應一個表名.ibd
的表空間文件,用來存儲表的結構(sdi)、數據、索引。
4、innodb_file_per_table
,該參數值默認為ON
,會給每張表創建一個單獨的表空間文件。可以查看該參數的值:show variables like 'innodb_file_per_table';
5、從 ibd 文件中提取表結構,在命令行窗口執行:ibd2sdi 表名.ibd
6、InnoDB 的邏輯存儲結構,按照從大到小的包含關系為:Tablespace(表空間)
、Segment(段)
、Extent(區,大小1M)
、Page(頁,大小16k)
、Row(行,表中的每一行數據)
。
7、如果需要支持事務、外鍵,對增刪改查都有比較多的需求,對事務完整性要求高,在高并發下要求事務的一致性,可以選擇該引擎。MyISAM
1、MySQL5.5版本之前默認存儲引擎。
2、不支持事務,不支持外鍵。
3、支持表鎖,不支持行鎖。
4、訪問速度快。
5、每張表對應三個文件表名.sdi
(存儲表結構信息)、表名.MYD
(存儲數據)、表名.MYI
(存儲索引)。
6、如果是以讀和插入操作為主,只有很少的更新和刪除操作,并對事務完整性和并發要求不高可以使用這個存儲引擎。
7、可以用 MongoDB 替代。Memory
1、表數據存儲在內存中,訪問速度快,但不能存儲太大的表,由于會受到硬件故障、斷電問題的響應,只能將這些表作為臨時表或者緩存使用。
2、支持 hash 索引,支持表鎖。
3、每張表對應一個文件表名.sdi
(存儲表結構信息)。
4、可以用 Redis 替代
索引
索引是幫助 MySQL 高效查詢數據的數據結構
(有序)。在數據之外,數據庫系統還維護著滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法,這種數據結構就是索引。
優缺點
1、優點:提高數據檢索效率,降低數據庫的 IO 成本;通過索引列對數據進行排序,降低數據排序成本,降低 CPU 消耗。
2、缺點:索引也要占磁盤空間,雖然大大提高了數據檢索效率,但也會降低表中數據的更新效率(Insert、Update、Delete),因為此時需要維護索引結構。
索引實現方式
B+tree索引
1、默認的索引類型,基于 B+tree 實現。該網址可以模擬 B+tree 的構建過程:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
2、MySQL 索引數據結構對經典的 B+tree 進行了優化,在原基礎上,增加了一個指向相鄰葉子節點的鏈表指針,即最下邊一層的葉子節點變成了雙向循環鏈表,提高了區間訪問的性能。
3、相對于二叉樹,層級更少,搜索效率更高。
4、在 B-tree 中,無論葉子節點還是非葉子節點都會保存數據,這樣導致一頁中存儲的鍵值減少,指針跟著減少,要保存和 B+tree 同樣的數據只能增加樹的高度,導致性能降低。
5、InnoDB、MyISAM、Memory 支持。Hash索引
1、采用一定的 hash 算法,將鍵值換算成 hash 值,映射到對應的槽位上,然后存儲到 hash 表,如果兩個或多個鍵值映射到同一個槽位上,則會產生 hash 沖突,此時通過鏈表將 hash 值串起來。
2、hash 索引只支持對等比較(=、in),不支持范圍查詢(between、>、<...)。
3、無法利用索引進行排序操作,但查詢效率一般高于 B+Tree,通常只需要一次檢索。
4、Memory、InnoDB 支持。R-tree(空間索引)
:MyISAM 中的一個特殊索引,主要用于地理空間數據類型,使用較少。Full-text(全文索引)
:一種通過建立倒排索引實現快速查詢的方式,類似于 Elasticsearch。(InnoDB5.6+、MyISAM)
索引分類
-
主鍵索引
:primary
,針對表中主鍵創建的索引,每張表默認只有一個。 -
唯一索引
:unique
,避免表中某一列的值重復。 -
常規索引
:快速定位特定數據。 -
全文索引
:fulltext
,查找文本中的關鍵字,而不是進行比較。 - 在 InnoDB 中,根據索引的存儲形式,索引又分為以下兩種:
1、聚集索引(Clustered Index)
:將行數據與索引存儲到一起,索引結構的葉子節點保存了行數據以及索引字段的值。每張表必須有,且只能有一個。
2、二級索引(Secondary Index)
:將行數據與索引分開存儲,索引結構的葉子節點保存了行數據的主鍵的值以及索引字段的值。使用二級索引找到行數據的主鍵后,再用主鍵去聚集索引中查找對應的行數據(回表查詢
)。每張表可以有多個。
3、如果存在主鍵,主鍵索引就是聚集索引;如果不存在主鍵,則將第一個唯一索引作為聚集索引;如果沒有主鍵索引或者唯一索引,則 InnoDB 會自動生成一個 rowid 作為隱藏的聚集索引。其它的索引就是二級索引。
索引語法
- 創建索引:
create [unique | fulltext] index index_name on table_name (col_name,...);
索引名稱一般為idx_表名_字段名
,不指定索引類型就是常規索引,col_name
指定多個則是創建聯合索引。默認索引按照字段值升序排列(asc
),也可以在創建索引時顯示指定......(col_name1 asc, col_name2 desc)
- 查看索引:
show index from table_name;
- 刪除索引:
drop index index_name on table_name;
SQL 性能分析
-
SQL 執行次數
客戶端連接到服務器后,可查看CRUD等操作的執行次數show global status like 'Com_______';
慢查詢日志
慢查詢日志記錄了所有執行時間超過指定參數(long_query_time,單位:秒,默認10秒)的所有 SQL 語句日志。通過如下方式開啟慢查詢日志:
#啟用慢查詢日志
slow_query_log = 1
#指定慢查詢日志文件的路徑和名字
slow_query_log_file = slow.log
#SQL語句運行時間超過該值才會被記錄
long_query_time = 10
-
profile
1、查看 MySQL 是否支持 profile 操作:select @@have_profiling;
2、查看 profile 是否打開:select @@profiling;
3、打開 profile:set profiling = 1;
4、執行一系列的業務 SQL 后,可以查看每一條 SQL 的耗時的情況show profiles;
查看指定query_id
對應 SQL 各個階段的耗時情況show profile for query 5;
如果需要查看 cpu 使用情況可以使用show profile cpu for query 5;
-
explain
explain 或 desc 命令獲取 MySQL 如何執行 select 語句的信息,包括在 select 語句執行過程中如何連接表和連接的順序。可直接在查詢語句前添加explain
:
結果中各個字段的含義:
1、id
:表示查詢中執行 select 字句或者操作表的順序,id 相同時執行順序從上到下,id 不同時值越大越先執行。
2、select type
:表示當前查詢語句的類型(simple:不使用表連接或子查詢;primary:主查詢,即外層的查詢;union:union中第二個或者后面的查詢;subquery:select/where后邊的子查詢)
3、type
:連接的類型,性能由好到壞依次為:null(不查詢表,直接查詢指定值)、system(只有一條數據的系統表)、const(查詢索引字段,并且最多只有一行匹配)、eq_ref(主鍵、唯一索引)、ref(非唯一索引)、range(索引的范圍查詢)、index(查詢索引中全部的數據)、all(查詢沒有索引的列,全表掃描)。
4、possible_keys
:查詢時可能用到的索引
5、key
:實際用到的索引,沒有使用索引則為 null
6、key_len
:索引長度,在不損失精度的情況下,越短越好
7、ref
:表示索引的哪一列被使用了,如果可能的話,是一個常數
8、rows
:MySQL 認為需要執行查詢的行數(預估值)
9、filtered
:查詢返回的行數占讀取行數的百分比,值越大越好
索引的使用
最左前綴法則:如果使用了聯合索引,則會遵循最左前綴法則。最左前綴法則指的是查詢從索引的最左邊的列開始,如果查詢時指定了全部索引列(和書寫順序無關),則全部索引生效。如果跳過某一列,則從該列后邊列的索引都會失效(由聯合索引順序決定)。
比如有聯合索引:idx_user_pro_age_sta
,則如下查詢聯合索引都會生效:select * from user where profession = '會計' and age = 30 and status = 0;
如下查詢status = 0
查詢條件不會走索引:select * from user where profession = '會計' and status = 0;
范圍查詢:聯合索引中,如果出現范圍查詢(>、<),則范圍查詢右側查詢的列的索引失效,這種情況盡量使用 >=、<= 來避免問題。
比如有聯合索引:idx_user_pro_sta
,則如下查詢中status = 0
查詢條件不會走索引:select * from user where profession = '會計' and age > 30 and status = 0;
索引列運算:不要在索引列上進行運算操作,否則索引會失效。
字符串不加引號:如果索引列是字符串類型,查詢時指定的值不加引號,則索引會失效。
模糊查詢:如果在索引列尾部進行模糊查詢(
like 'xxx%'
),索引不會失效,如果在頭部則失效(like '%xxx'
、like '%xxx%'
)。or 連接的條件:用
or
連接的條件,如果or
前邊條件的列有索引,而后邊的列沒有索引,那么涉及的索引不會生效。只有兩邊列都有索引才會生效。數據分布影響:如果 MySQL 評估使用索引比全表掃描更慢,則不使用索引。
SQL 提示:一個字段既有聯合索引又有單獨的索引,默認使用聯合索引??梢允褂?SQL 提示來優化數據庫操作,告訴它使用那個索引:
1、use index
,建議使用哪個索引
2、force index
,強制使用哪個索引
3、ignore index
,忽略哪個索引
select * from tb_user use index(idx_user_name) where name = 'zhangsan';
覆蓋索引:盡量使用覆蓋索引,即查詢使用了索引列,并且需要返回的列包含在查詢條件的列中,盡量減少使用
select *
,因為容易產生回表查詢。前綴索引:當字段類型為字符串(varchar、text)時,如果直接給字段建立索引,會導致建立的索引很大,浪費磁盤IO,影響查詢效率,此次可以對字符串的一部分前綴建立索引,來節省空間,提高查詢效率。
1、create index index_name on table_name (col_name(n));
n
表示對前多少個字符建立索引,即前綴長度。
2、前綴長度可以根據索引的選擇性來決定,選擇性是指不重復的索引值和表中記錄數的比值,選擇性越高查詢效率越高,選擇性最大為1。可以使用如下方式計算選擇性,進而確定前綴的長度(n):select count(distinct substring(col_name, 1, n)) / count(*) from table_name;
單列索引與聯合索引:如果有多個查詢條件,考慮對查詢字段建立索引時,建議創建聯合索引,而非單列索引,這樣可以一定程度避免回表查詢。
索引設計原則
- 針對數據量較大,且查詢比較頻繁的表建立索引。
- 針對經常作為查詢條件、排序、分組的字段建立索引。
- 盡量選擇區分度高的字段作為索引,盡量建立唯一索引,區分度越高使用索引的效率越高。
- 如果是字符串類型字段,而且比較長,可以考慮建立前綴索引。
- 盡量使用聯合索引,減少單列索引,聯合索引很多時候可以覆蓋索引、節省存儲空間、避免回表、提高查詢效率。
- 要控制索引數量,索引并不是越多越好,維護索引也需要耗費資源,也會影響增刪改的效率。
- 如果索引列不能存儲null值,請在建表時使用not null約束它;當優化器知道每列是否包含null值時,它可以更好的確定那個索引的查詢效率高。
SQL 優化
數據插入
-
insert 優化
:批量插入(每次最多500-1000條)、手動提交事務(避免頻繁的開啟、提交事務)、按照主鍵順序插入 -
大批量數據插入
:如果一次性需要插入大批量數據(萬級別),使用 insert 性能會比較低,此時可以使用load
指令進行插入:
# 客戶端連接服務端時,添加參數 --local-infile
mysql --local-infile -u root -p;
# 設置全局參數 local_infile = 1,開啟從本地加載文件導入數據的開關
set global local_infile =1;
# 執行 load 指令加載文件中的數據到表結構中
load data local infile '文件路徑' into table 'table_name' fields terminated by ',' lines terminated by '\n';
主鍵優化
-
數據的組織方式
:在 InnoDB 存儲引擎中,表數據都是根據主鍵順序組織存放的,這種存儲方式的表稱為索引組織表。 -
頁分裂
:頁可以為空,也可以填充一半,也可以填充100%,每個頁包含了2-N行數據(如果一行數據過多,會行溢出),根據主鍵排列。按照主鍵亂序插入時會導致頁分裂,即頁之間需要重新組合、排序,來保證主鍵的順序。 -
頁合并
:當刪除一行記錄時,其實并沒有進行物理刪除,只是這一行記錄被標記為刪除,并且它的空間允許被其它記錄使用。當一頁中刪除的記錄達到閾值(MERGE_THRESHOLD)時(默認為頁的50%),InnoDB 會開始尋找靠近的頁看是否可以將兩個頁合并來優化空間使用。 -
主鍵的設計原則
:
1、在滿足需求的情況下,盡量減小主鍵長度,減小磁盤IO。
2、插入數據時盡量按主鍵順序插入,選擇使用 AUTO_INCREMENT 自增主鍵。
3、盡量不使用 UUID 做主鍵,或者其它自然主鍵(身份證號),因為它們都是無序的,在插入數據時會導致頁分裂,而且它們的長度都比較長。
4、業務操作時,避免修改主鍵。
order by 優化
-
using filesor
:通過表的索引或者全表掃描,讀取滿足條件的行,然后在排序緩沖區完成排序,所有不是通過索引直接返回排序結果的排序都叫 using filesor。 -
using index
:通過有序索引按順序掃描直接返回數據,稱為 using index,這種方式不需要額外操作,效率高。 - 多字段排序時,會遵循最左前綴法則。
- 盡量使用覆蓋索引。
- 索引默認按字段值升序排列,如果查詢時用索引字段降序排列,則會產生 using filesor,所以查詢時字段的排序方式與字段索引的排序方式一致時才會使用 using index。
- 在大量數據排序時,如果不可避免的出現 using filesor,可以適當增加排序緩沖區大小(sort_buffer_size,默認256k)。
group by 優化
- 分組操作時可以使用索引字段來提高效率。
- 分組操作時,如果使用索引字段,那么也會遵循最左前綴法則。
limit 優化
- 一般分頁查詢時,可以通過創建覆蓋索引、結合子查詢,來進行優化以提高效率。
count 優化
- MyISAM 引擎會把一張表的總行數保存在磁盤上,當不加 where 添加執行 count(*) 時會直接返回總行數,效率高。
- InnoDB 引擎中,執行 count(*) 時會把數據一行一行讀出來然后累積計數,執行起來比較耗時。 沒有特別好的優化策略,可能需要我們自己計數。
- count() 是一個聚合函數,對于查詢返回的結果集,一行行判斷,如果不是 null 累積值加 1,否則不加,最后返回累積結果。
- count() 的幾種用法:
1、count(主鍵)
:InnoDB 引擎會遍歷整張表,把每一行的主鍵 id 取出,然后按行累加計數。
2、count(字段)
:沒有 not null 約束時,InnoDB 引擎會遍歷整張表,取出每一行的字段值,判斷如果不為 null 則累加計數。如果有 not null 約束,InnoDB 引擎會把表中每一行的字段取出直接累加計數。
3、count(1)
:InnoDB 引擎會遍歷整張表,但不取值,對返回的每一行用 1 替換,直接按行進行累加。1 可以用其它整數替換。
4、count(*)
:InnoDB 引擎對此種方式做了優化,會遍歷表但不會取出每一行,直接按行進行累加。
5、 效率:count(*) >= count(1) > count(主鍵) > count(字段)
update 優化
- InnoDB 的行鎖是針對索引加的鎖,不是針對整行記錄加的鎖,所以更新時條件字段需要有索引,否則會從行鎖升級為表鎖,并發性能會降低,可能阻塞其它更新操作。