mysql-高性能索引策略

来源:http://www.cnblogs.com/happyflyingpig/archive/2017/10/16/7655762.html
-Advertisement-
Play Games

獨立索引: 獨立索引是指索引列不能是表達式的一部分,也不能是函數的參數 例1: SELECT actor_id FROM actor WHERE actor_id+1=5 --這種寫法,就算在actor_id上建立了索引,也不起效 例2: SELECT .... WHERE TO_DAYS(CURR ...


獨立索引:

  獨立索引是指索引列不能是表達式的一部分,也不能是函數的參數

  例1:

SELECT actor_id FROM actor WHERE actor_id+1=5 --這種寫法,就算在actor_id上建立了索引,也不起效

  例2:

SELECT .... WHERE TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <= 10 --這也是一種錯誤的寫法

 

多列索引(聯合索引)&選擇合適的索引列順序:

  多列索引(Multiple-Column Indexes)也稱為複合索引(composite index),也即同時對多個列建立索引。

  什麼時候用多列索引?

  • 當出現伺服器對多個索引做相交操作時(通常有多個AND條件),通常意味著需要一個包含所有相關列的多列索引,而不是多個獨立的單列索引。
  • 當伺服器需要對多個索引做聯合操作時(通常有多個OR條件),通常需要耗費大量CPU和記憶體資源在演算法的緩存、排序和合併操作上。特別是當其中有些索引的選擇性不高,需要合併掃描返回大量數據的時候。

  多列索引的生效規則:

  比如(a,b,c),abc都是拍好序的,在任意一段a的下麵b都是排好序的,任何一段b下麵c都是拍好序的。多列索引的生效原則是從前往後依次使用生效,如果中間某個索引沒有使用,那麼斷點前面的索引部分起作用,斷點後面的索引沒有起作用;

還需註意:(a,b,c)多列索引和 (a,c,b)是不一樣的,看上面的圖也看得出來關係順序是不一樣的;

分析幾個實際例子來加強理解

 

(0)select * from mytable where a=3 and b=5 and c=4;   --abc三個索引都在where條件裡面用到了,而且都發揮了作用
(1)select * from mytable where  c=4 and b=6 and a=3;  --這條語句列出來只想說明 mysql沒有那麼笨,where裡面的條件順序在查詢之前會被mysql自動優化,效果跟上一句一樣
(2)select * from mytable where a=3 and c=7;  --a用到索引,b沒有用,所以c是沒有用到索引效果的
(3)select * from mytable where a=3 and b>7 and c=3;  --a用到了,b也用到了,c沒有用到,這個地方b是範圍值,也算斷點,只不過自身用到了索引
(4)select * from mytable where b=3 and c=4;  --因為a索引沒有使用,所以這裡 bc都沒有用上索引效果
(5)select * from mytable where a>4 and b=7 and c=9;  --a用到了  b沒有使用,c沒有使用
(6)select * from mytable where a=3 order by b;  --a用到了索引,b在結果排序中也用到了索引的效果,前面說了,a下麵任意一段的b是排好序的
(7)select * from mytable where a=3 order by c;  --a用到了索引,但是這個地方c沒有發揮排序效果,因為中間斷點了,使用 explain 可以看到 filesort
(8)select * from mytable where b=3 order by a;  --b沒有用到索引,排序中a也沒有發揮索引效果

  對於如何選擇索引的列順序有一個經驗法則:將選擇性最高的列放到索引最前列。(參考①)

  當不需要考慮排序和分組時,將選擇性最高的列放到前面通常是很好的。這時候索引的作用只是用於優化WHERE條件的查找

 

首碼索引和索引的選擇性:

  首碼索引能有效減小索引文件的大小,提高索引的速度。但是首碼索引也有它的壞處:

  1.不能再OORDER BY 或 GROUP BY 中使用首碼索引;

  2.也不能把他們用作覆蓋索引(Covering index)。

  建立首碼索引的語法:

  ALTER TABLE table_name ADD KEY(column_name(prefix_length));

  示例:

  ALTER TABLE city ADD KEY(cityname(7));

  什麼叫做索引的選擇性呢?①

  所謂索引的選擇性(Selectivity),是指不重覆索引值(也叫作基數,Cardinality)與表記錄數(#T)的比值

  Selectivity = Cardinality / #T

  顯然選擇性的取值範圍為(0,1],選擇性越高的索引值價值越大

   SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles;

  1. +-------------+
  2. | Selectivity |
  3. +-------------+
  4. | 0.0379      |
  5. +-------------+

 

  比如employees表只有一個索引<emp_no>,那麼如果我們想按名字搜索一個人,就只能全表掃描了:

EXPLAIN SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido';

+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table     | type | possible_keys | key  | key_len | ref  | rows   | Extra     |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+
| 1  | SIMPLE      | employees | ALL  | NULL          | NULL | NULL    | NULL | 300024 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+--------+-------------+

 

這樣全表掃描效率很低,所以我們考慮到把名字建立索引,有兩種選擇,建<first_name>或<first_name,last_name>,看下兩個索引的選擇性:

SELECT count(DISTINCT(first_name))/count(*) AS Selectivity FROM employees;
+-------------+
| Selectivity |
+-------------+
|      0.0042 |
+-------------+
SELECT count(DISTINCT(concat(first_name, last_name)))/count(*) AS Selectivity FROM employees;
+-------------+
| Selectivity |
+-------------+
|      0.9313 |
+-------------+

 

  從結果看顯然<first_name>選擇性太低,<first_name,last_name>選擇性好。但是first_name和last_name加起來長度為30,有沒有兼顧長度和選擇性的辦法?可以考慮用first_name和last_name的前幾個字元建立索引,例如<first_name, left(last_name, 3)>,看看其選擇性:

SELECT count(DISTINCT(concat(first_name, left(last_name, 3))))/count(*) AS Selectivity FROM employees;
+-------------+
| Selectivity |
+-------------+
|      0.7879 |
+-------------+

 

  選擇性還不錯,但離0.9313還是有點距離,那麼把last_name首碼加到4:

SELECT count(DISTINCT(concat(first_name, left(last_name, 4))))/count(*) AS Selectivity FROM employees;
+-------------+
| Selectivity |
+-------------+
|      0.9007 |
+-------------+

 

  這時選擇性已經很理想了,而這個索引的長度只有18,比<first_name, last_name>短了接近一半,我們把這個首碼索引 建上:

ALTER TABLE employees
ADD INDEX `first_name_last_name4` (first_name, last_name(4));

 

  此時再執行一遍按名字查詢,比較分析一下與建索引前的結果:

SHOW PROFILES;
+----------+------------+---------------------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                                           |
+----------+------------+---------------------------------------------------------------------------------+
|       87 | 0.11941700 | SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido' |
|       90 | 0.00092400 | SELECT * FROM employees.employees WHERE first_name='Eric' AND last_name='Anido' |
+----------+------------+---------------------------------------------------------------------------------+

   性能的提升是顯著的,查詢速度提高了120多倍。

聚簇索引

覆蓋索引

冗餘索引和覆蓋索引

未使用的索引

  應該刪除未被使用的索引。有兩個工具可以幫助定位未使用的索引。

  1.在Percona Server或者MariaDB中先打開userstates伺服器變數(預設是關閉的),然後讓伺服器正常運行一段時間,再通過查詢INFORMATION_SCHEMA.INDEX_STATISTICS就能查到每個索引的使用頻率。

  2.在Percona Toolkit中的pt-index-usage,該工具可以讀取查詢日誌,並對日誌中的每條查詢進行EXPLAIN操作,然後列印出關於索引和查詢的報告

索引和鎖  

  InnoDB只有在訪問行的時候才會對其枷鎖,而索引能夠減少InnoDB訪問的行數,從而減少鎖的數量  

  InnoDB在二級索引上使用共用(讀)鎖,但訪問主鍵索引需要排他(寫)鎖。這消除了覆蓋索引的可能性,並且使得SELECT FOR UPDATE比LOCK IN SHARE MODE 或非鎖定查詢要慢很多

InnoDB的主鍵選擇與插入優化

  在使用InnoDB存儲引擎時,如果沒有特別的需要,請永遠使用一個與業務無關的自增欄位作為主鍵。為什麼呢?

  因為InnoDB使用聚集索引,數據記錄本身被存於主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點內(大小為一個記憶體頁或磁碟頁)的各條數據記錄按主鍵順序存放,因此每當有一條新的記錄插入時,MySQL會根據其主鍵將其插入適當的節點和位置,如果頁面達到裝載因數(InnoDB預設為15/16),則開闢一個新的頁(節點)。

  如果表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁。如下圖所示:

                            

 

  這樣就會形成一個緊湊的索引結構,近似順序填滿。由於每次插入時也不需要移動已有數據,因此效率很高,也不會增加很多開銷在維護索引上。

  如果使用非自增主鍵(如果身份證號或學號等),由於每次插入主鍵的值近似於隨機,因此每次新紀錄都要被插到現有索引頁得中間某個位置:

                          

  此時MySQL不得不為了將新記錄插到合適位置而移動數據,甚至目標頁面可能已經被回寫到磁碟上而從緩存中清掉,此時又要從磁碟上讀回來,這增加了很多開銷,同時頻繁的移動、分頁操作造成了大量的碎片,得到了不夠緊湊的索引結構,後續不得不通過OPTIMIZE TABLE來重建表並優化填充頁面。

  因此,只要可以,請儘量在InnoDB上採用自增欄位做主鍵。

參考文獻:

 [1] Baron Schwartz等 著,寧海元等 譯 ;《高性能MySQL》(第3版); 電子工業出版社 ,2013

 [2] 張洋blog, http://blog.codinglabs.org/articles/theory-of-mysql-index.html

 [3]匿名blog, http://www.cnblogs.com/codeAB/p/6387148.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 每次看到馬路對面摩托羅拉的大牌子,都想起谷歌125億美元收購摩托羅拉移動,後來又以29億美元賣給聯想的事情。谷歌所做的決策都比較考慮長遠利益,在這串交易中,谷歌獲得了摩托羅拉最有價值的幾千項專利,穩健了Android的生態。谷歌曾經在美國一個地區占用巨大的面積建立了太陽能發電站,人稱“烤鳥”項目。因 ...
  • 今天遇到一個案例,在使用命令修改一個測試伺服器(SQL Server 2014標準版)的伺服器排序規則時,遇到了下麵錯誤信息 (具體賬號信息脫敏處理,隨機生成一個賬號密碼) The Windows account sa does not exist and cannot be provisioned... ...
  • 一查詢數值型數據: SELECT * FROM tb_name WHERE sum > 100; 查詢謂詞:>,=,<,<>,!=,!>,!<,=>,=< 二查詢字元串 SELECT * FROM tb_stu WHERE sname = '小劉' SELECT * FROM tb_stu WHER ...
  • 約束的概念:確保在列中輸入有效的值並維護表之間的關係。 Primary key約束 功能:primary key(主鍵約束),一個表中只能有一個,不能有空值,不能有重覆值. 創建表時定義約束:欄位名 數據類型[長度] primary key Unique約束功能:unique(唯一約束), 指定在同 ...
  • 經常有用戶遇到安裝近乎的時候,會卡在資料庫嚮導頁面。安裝環境也做了比對,沒有問題,Windows伺服器、Mysql5.0+版本的資料庫、.net framework也安裝正確了。環境沒問題,那是不是資料庫不對啊。然後又用可視化工具本地、異地遠程都能正常訪問資料庫,可仍然是卡在數據安裝的嚮導頁面,這究 ...
  • 近排自己學習了一款軟體finereport開發報表模塊,自己總結瞭如何瞭解需求,分析需求,再進行實踐應用開發,最後進行測試數據的準確性,部署報表到項目對應的模塊中顯示。 一、需求(根據需求文檔分析) 1.條件塊: 2.數據塊(一部分): 3.數據取值: 數據源全部來自EAS。通過“物料收發事物彙總” ...
  • 存儲在資料庫中的所有數據值均正確的狀態。如果資料庫中存儲有不正確的數據值,則該資料庫稱為已喪失數據完整性。 詳細釋義 詳細釋義 資料庫中的數據是從外界輸入的,而數據的輸入由於種種原因,會發生輸入無效或 錯誤信息。保證輸入的數據符合規定,成為了 資料庫系統,尤其是多用戶的 關係資料庫系統首要關註的問題 ...
  • java.sql.SQLSyntaxErrorException: ORA-00904: "column": 標識符無效 首先查看無效的列是不是orcale關鍵字 , 如果不是 , 查看與column欄位相關的所有內容 , 引用是否正確 儘量不要用select 中的欄位別名當做 where 或者 o ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...