MYSQL8-性能調優

explain

使用explain

用於分析sql語句的性能。案例分析:

mysql> show create table employees;

CREATE TABLE `employees` (
  `id` int(11) NOT NULL,
  `first_name` varchar(14) NOT NULL,
  `last_name` varchar(16) DEFAULT NULL,
  `hire_date` date NOT NULL,
  `card_no` char(18) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `first_name` (`first_name`),
  KEY `card_no` (`card_no`),
  KEY `first_name2` (`first_name`,`last_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

正常格式

mysql> EXPLAIN SELECT * FROM employees WHERE id = 1;
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | employees | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

JSON格式

mysql> EXPLAIN FORMAT=JSON SELECT * FROM employees WHERE id = 1;

{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "1.00"
    },
    "table": {
      "table_name": "employees",
      "access_type": "const",
      "possible_keys": [
        "PRIMARY"
      ],
      "key": "PRIMARY",
      "used_key_parts": [
        "id"
      ],
      "key_length": "4",
      "ref": [
        "const"
      ],
      "rows_examined_per_scan": 1,
      "rows_produced_per_join": 1,
      "filtered": "100.00",
      "cost_info": {
        "read_cost": "0.00",
        "eval_cost": "0.20",
        "prefix_cost": "0.00",
        "data_read_per_join": "160"
      },
      "used_columns": [
        "id",
        "first_name",
        "last_name",
        "hire_date",
        "card_no"
      ]
    }
  }
}

輸出對照表

以下數據來自官網:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html

Column JSON Name Meaning
id select_id The SELECT identifier
select_type None The SELECT type
table table_name The table for the output row
partitions partitions The matching partitions
type access_type The join type
possible_keys possible_keys The possible indexes to choose
key key The index actually chosen
key_len key_length The length of the chosen key
ref ref The columns compared to the index
rows rows Estimate of rows to be examined
filtered filtered Percentage of rows filtered by table condition
Extra None Additional information

JSON properties which are NULL are not displayed in JSON-formatted EXPLAIN output.

type(JSON Name: access_type)

當前查詢的連接類型。性能從最佳到最差

  • system:當前表中只有一條,const中的一種特例
  • const:主鍵或唯一索引
  • eq_ref:兩表通過主鍵或唯一鍵關聯
-- 官網案例
SELECT * FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;

-- 注意細節:結果項需要有索引纔是eq_ref,否則是ALL
SELECT ref_table.key_column FROM ref_table,other_table
  WHERE ref_table.key_column=other_table.column;
  • ref:使用非唯一索引掃描
  • fulltext:全文索引
  • ref_or_null:使用非唯一索引或null掃描
  • index_merge:合併索引
  • unique_subquery:
  • index_subquery
  • range
  • index
  • ALL

MySql自帶的全文索引只能用於數據庫引擎爲MYISAM的數據表,如果是其他數據引擎,則全文索引不會生效。

possible_keys(JSON Name: possible_keys)

可能使用的索引。

出現possible_keys有值,key顯示NULL的情況,這種情況是因爲表中數據不多,mysql認爲索引對此查詢幫助不大,選擇了全表查詢。要查看錶具有哪些索引,請使用。 SHOW INDEX FROM tbl_name

key(JSON Name:key)

當前查詢使用的索引。

key_len(JSON Name: key_length)

索引長度。

字段允許爲NULL加1字節,變長數據類型加2字節。

  • 字符串

長度和字符集有關,gbk(一個字符=2個字節),utf8(一個字符=3個字節),utf8mb4(一個字符=4個字節)

char(n):(2n/3n/4n)[+1] 字節
varchar(n):(2n/3n/n4)+2[+1] 字節
  • 數值類型
tinyint:1字節
smallint:2字節 
int:4字節 
bigint:8字節   
  • 時間類型
date:3字節 
timestamp:4字節 
datetime:8字節

索引最大長度是768字節,當字符串過長時,mysql會做一個類似左前綴索引的處理,將前半 部分的字符提取出來做索引。

ref(JSON Name: ref)

在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const,字段名

rows(JSON Name: rows)

MySQL認爲執行查詢必須檢查的行數。

filtered(JSON Name: filtered)

該filtered列指示將被表條件過濾的錶行的估計百分比。

最大值爲100,這表示未過濾行。值從100減小表示過濾量增加。 rows顯示檢查的估計行數,rows × filtered顯示將與下表連接的行數。例如,如果 rows爲1000且 filtered爲50.00(50%),則與下表連接的行數爲1000×50%= 500。

Extra (JSON Name:無)

這一列展示的是額外信息。常見的重要值如下:

  • Using index:使用覆蓋索引
  • Using where:使用where語句來處理結果,查詢的列未被索引覆蓋
  • Using index condition:查詢的列不完全被索引覆蓋,where條件中是一個前導列的範圍;
  • Using temporary:mysql需要創建一張臨時表來處理查詢。

出現這種情況一般是要進行優化的,首先是想到用索引來優化。

  • Using filesort:將用外部排序而不是索引排序,數據較小時從內存排序,否則需要在磁盤 完成排序。

這種情況下一般也是要考慮使用索引來優化的。

  • Select tables optimized away:使用某些聚合函數(比如max、min)來訪問存在索引的某個字段

索引

關於索引介紹的內容參考博客: https://www.cnblogs.com/yuanrw/p/10225659.html

數據結構

B+Tree

下圖爲B+Tree存儲的數據結構圖:

  • 非葉子節點不存放數據,只有葉子節點存放數據
  • 每個葉子節點有一個指針指向下一個節點,把所有的葉子節點串在了一起

BTree

B+樹和B樹的區別是:

  1. B樹的節點中沒有重複元素,B+樹有。
  2. B樹的中間節點會存儲數據指針信息,而B+樹只有葉子節點才存儲。
  3. B+樹的每個葉子節點有一個指針指向下一個節點,把所有的葉子節點串在了一起。

哈希

哈希索引,只有精確匹配索引所有列的查詢纔有效。對於每一行數據,存儲引擎都會對所有的索引列計算一個哈希碼。哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每個數據行的指針。如果多個列的哈希值相同,索引會以鏈表的方式存放多個指針記錄到同一個哈希條目中。
因爲索引自身只存儲對應的哈希值,所以索引的結構十分緊湊,哈希索引查找的速度非常快。但是哈希索引也有它的限制:

  • 哈希索引不是按照索引順序存儲的,無法用於排序。
  • 不支持部分索引列匹配查找。
  • 不支持範圍查找。

索引類型

以下內容來自CSDN-傑哥一號號-mysql中innodb和myisam對比及索引原理區別

InnoDB索引實現

CREATE TABLE `employees` (
  `Col1` int(11) NOT NULL,
  `Col2` int(11) NOT NULL,
  `Col3` varchar(16) NOT NULL,
 
  PRIMARY KEY (`Col1`),
  KEY `Col3` (`Col3`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8

主鍵索引(聚簇索引)

在這裏插入圖片描述

二級索引(輔助索引)

在這裏插入圖片描述

索引選擇技巧

  1. 它應該是UNIQUE和NOT NULL
  2. 選擇最小的可能鍵,因爲所有二級索引都會存儲主鍵。如果主鍵很大,整個索引也會佔用更多的空間
  3. 選擇一個單調遞增的值。物理行是根據主鍵進行排序的。如果選擇一個隨機鍵,需要做多次的行重排,這會導致新跟那個下降。
  4. 最好選擇一個主鍵。如果找不到任務主鍵,請添加一個AUTO_INCREMENT列。如果你不選擇任何內容,InnoDB會在內部生成一個帶有6字節行ID的隱藏主鍵索引。

MyISAM索引實現(選讀)

CREATE TABLE `employees` (
  `Col1` int(11) NOT NULL,
  `Col2` int(11) NOT NULL,
  `Col3` varchar(16) NOT NULL,
 
  PRIMARY KEY (`Col1`),
  KEY `Col2` (`Col2`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8

主鍵索引(聚簇索引)

在這裏插入圖片描述

二級索引(輔助索引)

在這裏插入圖片描述

數據類型

表佔用的存儲空間越小,有以下三個優點:

  1. 向磁盤寫入或讀取的數據就越少,查詢起來就越快
  2. 在處理查詢時,磁盤上的內容會被加載到主內存中。所以,表越小,佔用的主存空間就越小
  3. 被索引佔用的空間就越小

建議操作

  1. 如果要存儲員工編號,而其可能的最大值爲500000,則最佳數據類型爲mediumint(3個字節),如果將它存儲爲4個字節的int類型,則每一行都浪費了一個字節。
  2. 如果要存儲員工姓名,由於其長度不等,可能的最大長度爲20個字符,最好將其聲明爲varchar(20)類型。如果講員工姓名定義爲char(20)類型,但是隻有幾個人的姓名長爲20個字符,其餘的長度不到10個字符,就會浪費10個字符的空間。
  3. 在聲明varchar類型時,要考慮其長度。儘快varchar類型在磁盤上進行了優化,但這個類型的數據被加載到內存時會佔用全部的長度空間。

例如:將col存儲在類型爲varchar(255)的字段中,並且實際長度爲10,則它在磁盤上佔用10+1(用於存儲長度)個字節,但是在內存中會佔用全部的255個字節。

  1. 類型爲varchar的字段,需要用2個字節來存儲內容的實際長度。

在磁盤中如果實際長度小於255則使用1個字節來存放長度,超過255則使用2個字節。

  1. 如果不允許爲空,則設置爲NOT NULL。這樣做可以避免了測試每個值是否爲空的開銷,並且還節省了存儲空間-每列能節省一個字節。
  2. 如果字符串的長度是固定的,請存儲爲char而非varchar類型(因爲varchar需要一個或兩個字節來存儲字符串的長度)
  3. 如果這些值是固定的,則使用ENUM而非varchar類型。只需要使用1或2個字節
  4. 優先選擇整數類型而非字符串類型
  5. 優先利用前綴索引
    10.嘗試利用InnoDB壓縮

警告排查

使用 SHOW WARNINGS;

關注我

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章