MySQL - 索引

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 的行鎖是針對索引加的鎖,不是針對整行記錄加的鎖,所以更新時條件字段需要有索引,否則會從行鎖升級為表鎖,并發性能會降低,可能阻塞其它更新操作。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容