MySQL相關基礎與SQL索引優化分析
一、MySQL基本知識
1. MySQL簡介
1.1 什麼是MySQL
MySQL是一個開源的關係型數據庫,由MySQL AB公司開發,目前已被Oracle收購。其遵循GPL協議,用戶可根據需求定製化開發資金的MySQL。MySQL可移植性高,支持多種語言,例如:Java、PHP、C++、Python、Perl、Eiffel、Rubby等。其使用標準的SQL數據語言形式,可通過如下數據語言進行使用:
-
DQL:數據查詢語言,select、from、where;
-
DML:數據操作語言,insert、delete、update;
-
DDL:數據定義語言,create、alter、drop、truncate;
-
DCL:數據控制語言,grant、rollback、commit
MySQL常用的SQL說明:
SQL | 描述 | 備註 |
---|---|---|
show databases | 列出所有數據庫 | |
create database test01 | 創建數據庫test01 | |
create database test01 character set utf8 | 創建數據庫,並設置字符集爲utf-8 | |
show create database test01 | 查看數據庫字符集 | |
show variables like ’%char%‘ | 查詢參數 | |
set [字符集屬性]=utf8 | 設置字符集屬性爲utf8 | 僅臨時更改,如需徹底修改需改配置文件 |
alter database test01 character set ‘utf8’ | 修改數據庫字符集 | |
alter table table01 convert to character set ’utf8‘ | 修改數據表字符集 |
MySQL支持大型數據庫,支持5000萬條記錄的數據倉庫,32位系統表文件最大支持4GB,64位系統表文件最大支持8TB。在200萬條記錄下不加索引性能依舊較好。
注意:
字符集需要安裝之後立即修改,如果插入數據之後再進行修改則之前數據庫、表格及數據依然爲原編碼格式!
2. 安裝MySQL
2.1 Windows安裝
以5.7.28爲例:參考博文MySQL 5.7.28安裝最穩教程
2.2 Linux安裝
2.2.1 docker安裝
2.2.2 yum 安裝
yum install mysql
2.2.3 rpm安裝
rpm -qa|grep mysql
rpm -ivh MySQL-client-xxx.linu.xxx.rpm
rpm -ivh MySQL-server-xxx.linu.xxx.rpm
2.3 Linux下常用命令
tips:
- 查看MySQL所屬用戶:cat /etc/passwd/|grep mysql
- 查看MySQL所屬組:cat /etc/group/|grep mysql
- 查看MySQL當前服務狀態:service mysql status
- 啓動關閉MySQL: service mysql start、service mysql stop、service mysql restart
- 設置MySQL開機啓動:chkconfig mysql on、chkconfig --list|grep mysql、cat /etc/inittab或者netsysv圖形化操作
- 設置MySQL用戶名密碼:mysqladmin -u root -p 123456
- 進程查看:ps -ef|grep mysql
- 設置大小寫不敏感:show variables like ’%lower_case_table_names%‘,修改my.cnf下lower_case_table_names=1,即大小寫不敏感。
2.4 sql_mode
sql_mode定義了對MySQL中的語法校驗規則,其默認值是空值,這種情況下是可以進行一些非法操作的,生產環境下必須將該值設置爲嚴格模式,以下爲sql_mode常用的值。
參數 | 說明 |
---|---|
ONLY_FULL_GROUP_BY | 若select未選中group by的字段,則SQL不合法 |
NO_AUTO_VALUE_ON_ZERO | 自增長列可插入0或者null |
STRICT_TRANS_TABLES | 若一個值不能插入到事務表中,則中斷,對非事務表不做限制 |
NO_ZERO_IN_DATE | 嚴格模式下不允許日期和月份爲零 |
NO_ZERO_DATE | 日期不允許插入零 |
ERROR_FOR_DIVISION_BY_ZERO | insert或update中,若數據被零除,則報錯,如果未給出該模式,則返回null |
NO_AUTO_CREATE_USER | 禁止grant創建密碼爲空的用戶 |
NO_ENGINE_SUBSTITUTION | 存儲引擎被禁用則報錯,若未設置則使用默認的存儲引擎代替 |
2.4.1 查看和修改sql_mode
查看:select @@sql_mode
修改:set @@sql_mode=’’;
2.5 用戶管理
命令 | 說明 | 備註 |
---|---|---|
create user z3 identified by ’123456‘ | 創建名稱爲z3,密碼爲12346的用戶 | |
select host,user,password,select_priv,insert_priv,drop_priv from mysql.user | 查看用戶和權限信息 | |
set password = password(‘123456’) | 修改當前用戶密碼 | |
update mysql.user set password=password(‘123456’) where user = ‘z3’ | 修改其他用戶密碼 | 通過user表的修改需要flush privileges才能生效 |
update mysql.user set user = ‘li4’ where user =‘z3’ | 修改用戶名 | 通過user表的修改需要flush privileges才能生效 |
drop user li4 | 刪除用戶 | 不要通過user表刪除,系統會有殘留 |
host:表示連接類型
- %表示所有遠程通過TCP連接
- IP地址
- 機器名
- ::1 ipv6本機地址
- localhost
2.6 權限管理
命令 | 說明 |
---|---|
grant select,insert,delete,drop on mydb.* to z3@localhost | 給本地的z3用戶的mydb數據庫所有表授予增刪改查權限 |
grant all privileges on *.* to z3@’%’ identified by ‘123’ | 授予通過網絡登錄的z3用戶對所有庫的權限,密碼爲123 |
show grants | 查看權限 |
revoke all privileges on mysql.* from z3@localhost | 收回z3全庫全表所有權限 |
二、SQL優化
一般使用SQL操作數據庫時,當遇到SQL執行時間太長怎麼辦呢?一般我們都是從下面四個方面進行查擺問題一一優化:
- 查詢語句過於粗糙
- 索引失效,明明增加了單值索引或者複合索引,但是explain查看執行計劃卻發現依舊是全表掃描
- 系統設計問題導致join關聯查詢太多
- 硬件方面需要優化,各緩衝參數設置過小(比如order by所使用的sort_buffer_size)
在優化開始之前,我們需要了解MySQL的邏輯架構是怎樣的,也就是MySQL是如何處理我們的SQL語句的呢?
1. MySQL架構分析
上圖爲MySQL邏輯架構圖,其主要分爲連接層、服務層、引擎層及存儲層。
-
連接層,MySQL最上層,它是一些客戶端和連接服務,包含本地socket通信和基於客戶端連接的類似tcp/ip通信,主要完成連接處理、授權認證及相關安全方案,該層引入了線程池的概念爲安全認證客戶端提供線程,例如驅動連接。
-
服務層:
Management Services & Utilities 系統管理和控制工具 SQL Interface SQL接口:接收SQL返回查詢結果 Parser 解析器:驗證和解析SQL語句 Optimizer 查詢優化器:MySQL對SQL會根據優化器結果進行優化 Cache&Buffer 查詢緩存:提高查詢效率 -
引擎層
可插拔式引擎的選擇,負責MySQL中數據的存儲和查詢,目前MySQL最常用的兩種數據庫存儲引擎爲InnoDB與MyISAM,下表爲二者的區別。
特性 InnoDB MyISAM 是否支持事務 支持 不支持事務 是否支持外鍵 支持 不支持 索引類型 聚簇索引 非聚簇索引 是否保存表的行數 否 是(通過變量保存,避免select count(*) 時進行全表掃描) 全文索引 5.7以後支持 支持 是否可壓縮後查詢 否 是 鎖的粒度 行鎖 表鎖 主鍵是否必須 是(聚簇索引特性) 否 存儲文件 frm(表結構),ibd(數據文件) frm(表結構),myd(數據文件),myi(索引文件)
tips:
1、聚簇索引與非聚簇索引
聚簇索引:數據與索引是在同一B+Tree上的,非葉子節點存放的是索引(數據的指針),葉子節點存放的是數據,該索引類型必須有主鍵,數據可通過主鍵進行查詢,之後建立的索引爲輔助索引,輔助索引需要兩次查詢,先查詢到主鍵,然後通過主鍵查詢數據,所以主鍵最好設置爲自增,否則主鍵過大會帶來額外開銷;
非聚簇索引:B+Tree的非葉子節點和葉子節點存放的都是數據的引用地址,數據文件與索引文件是分開保存的。
2、InnoDB的行鎖是實現在索引上的,而不是鎖在物理上,如果索引失效則行鎖會退化爲表鎖。
- 存儲層
數據庫文件的數據存儲在文件系統上的方式,並通過該存儲方式與存儲引擎交互。
2. SQL執行分析
2.1 SQL查詢流程
通過對MySQL邏輯結構的分析,我們可以知道SQL查詢的流程大致爲:
- MySQL客戶端通過協議與MySQL服務器建立連接,發送查詢語句;
- MySQL檢查查詢緩存,緩存如果命中則直接返回,未命中則將語句交由解析器處理;
- 解析器通過關鍵字對SQL語句預處理,生成解析樹並驗證SQL語法,若語法通過則交由優化器;
- 優化器將SQL轉化成執行計劃並選擇最好的執行;
- 優化器將最終的查詢結果返回。
SQL手寫爲:
優化器優化之後的順序:
tips:
- show engines可查看所有數據庫引擎
- show variables like ‘%storage_engine%’ 可查看當前默認數據庫引擎
2.2 show profile
2.2.1 SQL執行週期
通過show profile可以查看SQL的詳細執行週期,可以作爲優化SQL最強有力的工具。
- 通過show variables like '%profiling%'查看是否開啓該功能;
- set profiling = 1;開啓show profile;
- show profile cpu,block io for query Query_id;查詢SQL詳細執行信息。
show profiles可以提供SQL執行的詳細信息,但是實際中我們更多使用explain對SQL進行解釋優化,查看SQL是否使用索引等等,下文更多是針對explain的使用進行介紹。
3. SQL索引優化
如何提高SQL執行速度?許多人第一反應就是增加索引,什麼是索引呢?如何增加索引且增加的索引不會因爲SQL問題導致失效呢?
我們知道MySQL底層使用B+樹實現,索引作爲數據的另一種表現形式同樣也會耗費存儲空間的。
如果單純的爲了提升某個字段的查詢效率而在全表字段上都增加索引,那麼無疑給存儲空間帶來了巨大負擔。而且索引增加之後雖然給查詢帶來了便利,但是在增加和刪除方面,爲了維護索引而帶來的工作量也是極爲繁重的,這說明了不經研究而隨意建立索引的方法是不可取的。
3.1 索引簡介
3.1 什麼是索引?
索引是幫助MySQL高效獲取數據的數據結構,所以索引可以簡單理解爲排好序的快速查找數據結構。
3.2 索引的優缺點
優點:
- 提高數據檢索的效率,降低數據庫的IO成本;
- 索引是一種排好序的數據結構,通過索引進行排序可以降低數據的排序成本。
劣勢:
- 降低更新表的速度,加了索引的字段在insert、update、delete時會帶來額外的開銷;
- 佔用多餘的空間。
3.2 適合創建索引的情況
- 主鍵自動建立唯一索引
- 頻繁作爲查詢條件的字段需要建立索引
- 查詢中與其他表關聯的字段,外鍵關係建立索引
- 組合索引性價比優於單值索引
- 查詢中排序的字段
- 查詢中統計或者分組的字段
3.3 不適合創建索引的情況
- 表記錄太少
- 經常增刪改的表或者字段
- where條件裏用不到的字段不創建索引
- 字段中重複內容較多,過濾性不好的不適合建立索引
3.4 explain性能參數分析
在日常工作中,使用最爲頻繁的就是通過explain+SQL模擬優化器執行SQL語句,從而知道MySQL是如何處理SQL語句的,並根據情況分析SQL的性能瓶頸以優化SQL。
那麼,通過explain我們可以獲得什麼信息呢?
- 表的讀取順序
- 數據讀取操作的操作類型
- 哪些索引可以使用
- 哪些索引被實際使用
- 表之間的引用
- 每張表有多少行被優化器查詢
3.4.1 explain參數
我們通過explain+SQL的方式可以獲得如下所示的列表:
可以看出主要參數即:id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra。
以下針對第一行參數進行詳細分析:
-
id:select查詢的序列號,包含一組數字,表示查詢中select子句或操作表的順序,主要分以下三種情況:
- id相同,執行順序由上至下;
- id不同,如果是子查詢,id的序號會較大,此時優先級越高,也就越先被執行;
- id相同不同,同時存在時先根據不同定優先級,再由上至下進行執行;
-
select_type:查詢的類型,主要用於區別普通查詢、聯合查詢、子查詢等的複雜查詢,具體參數如下:
- SIMPLE:簡單的select查詢,不包含子查詢或者UNION;
- PRIMARY:查詢中若包含其他子查詢,則最外層會被標記爲PRIMARY;
- SUBQUERY:select或者where中包含了子查詢;
- DERIVED:From中包含的子查詢,臨時表;
- UNION:第二個select出現在UNION之後則會被標記爲UNION;
- UNION RESULT:從UNION中獲取結果的select;
-
table:顯示改行的數據來源於哪張表
-
partitions:是否分區
-
type:優化器定義的訪問類型,從最好到最差依次是scerria(system>const>ref_eq>ref>range>index>all),具體含義如下:
- system:表中只有一行記錄,相當於const類型的特例,平時不會出現;
- const:通過索引一次就找到了,const用於比較primary key或者unique索引;
- eq_ref:唯一性索引,對於每個索引鍵,表中只有一條記錄與之匹配,簡單地說是const是直接按主鍵或唯一鍵讀取,eq_ref用於聯表查詢的情況,按聯表的主鍵或唯一鍵聯合查詢;
- ref:非唯一性索引,返回匹配某個值的所有行(一值找多行);
- range:索引給定範圍的行,例如where中的between、in,這種範圍索引較全表掃描要好,因爲範圍決定了不用全表掃描;
- index:full index,全表索引掃描,效果與all的區別就是index掃描的是全表的索引,而all掃描的是數據;
- all:全表數據掃描,匹配到之後返回結果。
一般來說,保證查詢達到range或者ref效果爲佳。
-
possible_keys:顯示可能應用在這張表中的索引,一個或多個。查詢所涉及字段若存在索引,則索引將被列出,但不一定被使用
-
key:查詢中實際使用的索引,如果沒有則爲null,查詢中如果使用了覆蓋索引,則索引和查詢的字段一一吻合
-
key_len:表示索引使用到的字節數,可通過該值計算查詢中使用的索引長度。在不損失精度的情況下,該值越小越好。該值爲索引最大可能長度,並非實際使用長度。
-
ref:顯示索引哪一列被使用了,哪些列或者常數被用於索引進行查詢(最好是常數)。也就是說key 列是實際使用的 index , 但 index 可能建立在數據表的若干列上。ref 列列出具體哪些列或常數被使用了。
-
rows:根據表統計信息及索引使用情況大致估算除所需要讀取的行數
-
filtered:返回結果的行佔需要讀到的行(rows列的值)的百分比,因爲對於join操作,前一個表的結果集大小直接影響了循環的次數
-
Extra:包含不適合在其他列中顯示但十分重要的額外信息,其主要會有以下幾個常數:
參數 說明 Using filesort MySQL會對數據使用外部的文件索引排序,而不是按照表內索引順序讀取,文件的建立刪除會耗費大量資源,出現該項一定要優化 Using temporary 使用臨時表保存中間結果,常見於order by、group by,臨時表的創建與刪除會耗費大量資源,出現該項一定要優化 Using index 相應的select中使用了覆蓋索引,避免訪問冗餘數據,效果不錯,同時出現Using where則表明索引被用來執行索引鍵的查找,若沒有出現Using where 則表明索引用於讀取數據而非執行查找 Using where 表明使用了where過濾 Using join buffer 使用了連接緩存,可在my.cnf中增加緩存大小 impossible where where的值總是false,不能獲取任何值 select tables optimized away 在沒有GROUPBY子句的情況下,基於索引優化MIN/MAX操作或者對於MyISAM存儲引擎優化COUNT(*)操作,不必等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化。 distinct 優化distinct,找到第一匹配值即停止
以上就是對explain參數的分析,通過對這些參數的學習,將使得我們以後在SQL優化方面無論遇到什麼問題都能對症下藥,迎刃而解。
3.5 索引失效情況分析
有時候我們明明在字段上建立了索引,但是explain之後發現優化器並沒有使用到索引,那什麼情況下會導致索引失效?寫SQL語句時應該注意些什麼才能避免索引失效呢?
3.5.1 索引失效
以下是常見導致索引失效的幾種情況:
- 最左前綴法則:建立的複合索引在使用時沒有遵循最左前綴法則,例如索引使用了(a,b,c)三個字段,但是使用的時候a沒有使用到,索引失效;
- 在索引字段上進行計算,函數處理,類型轉換等二次操作;
- 範圍條件右邊的列索引失效,即範圍之後全失效,例如索引爲(id,age,name),使用時如果出現where id=xx and age>10 and name=xx,則age之後索引失效;
- 儘量使用覆蓋索引,避免select *,即複合索引的字段與查詢字段一一吻合;
- !=或<>會導致索引失效;
- is null,is not null會導致索引失效;
- 使用like模糊查詢時如果%出現在最左邊則索引失效,解決辦法就是使用覆蓋索引;
- 字符串不加單引號;
- 多次使用or會導致索引失效。
以上就是會造成MySQL索引失效的一些情況,爲了避免這些情況的出現而造成索引失效,建議:
- 對於單值索引,儘量選擇針對查詢過濾性更好的字段來建立索引,例如性別只有男、女、未知的三類就沒有必要建立索引;
- 複合索引過濾性最好的字段要放在最前面;
- 複合索引儘可能包含查詢字段中where子句中更多的字段;
- 通過分析統計信息和調整SQL查詢的寫法來選擇建立合適的索引。
完!如有不妥歡迎批評指正!