MySQLExplain_2

@(MySQL學習)

一. 哪張原數據表中記錄了Cardinality信息

--
-- 在information_schema.STATISTICS中記錄了相關的信息
--
mysql> use information_schema;
Database changed

mysql> show create table STATISTICS\G
*************************** 1. row ***************************
       Table: STATISTICS
Create Table: CREATE TEMPORARY TABLE `STATISTICS` (
  `TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
  `TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '',  -- 表所在的庫
  `TABLE_NAME` varchar(64) NOT NULL DEFAULT '',  -- 表名
  `NON_UNIQUE` bigint(1) NOT NULL DEFAULT '0',
  `INDEX_SCHEMA` varchar(64) NOT NULL DEFAULT '', 
  `INDEX_NAME` varchar(64) NOT NULL DEFAULT '',  -- 索引名
  `SEQ_IN_INDEX` bigint(2) NOT NULL DEFAULT '0', -- 索引的序號
  `COLUMN_NAME` varchar(64) NOT NULL DEFAULT '',
  `COLLATION` varchar(1) DEFAULT NULL,
  `CARDINALITY` bigint(21) DEFAULT NULL,   -- 這裏我們找到了Cardinality
  `SUB_PART` bigint(3) DEFAULT NULL,
  `PACKED` varchar(10) DEFAULT NULL,
  `NULLABLE` varchar(3) NOT NULL DEFAULT '',
  `INDEX_TYPE` varchar(16) NOT NULL DEFAULT '',
  `COMMENT` varchar(16) DEFAULT NULL,
  `INDEX_COMMENT` varchar(1024) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)


--
-- 之前我們可以通過 show index from table_name的方式查看索引
--

mysql>  show index from employees.salaries\G
*************************** 1. row ***************************
        Table: salaries
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1       -- 索引序號爲1
  Column_name: emp_no
    Collation: A
  Cardinality: 286271  -- Cardinality值
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 2. row ***************************
        Table: salaries
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 2         -- 索引序號爲2
  Column_name: from_date
    Collation: A
  Cardinality: 2760952   -- Cardinality值
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
2 rows in set (0.00 sec)

--
-- 現在可以通過STATISTICS表查看某張表的信息
--
mysql> select * from STATISTICS where table_name='salaries'\G
*************************** 1. row ***************************
TABLE_CATALOG: def
 TABLE_SCHEMA: employees
   TABLE_NAME: salaries
   NON_UNIQUE: 0
 INDEX_SCHEMA: employees
   INDEX_NAME: PRIMARY
 SEQ_IN_INDEX: 1        -- 索引序號爲1
  COLUMN_NAME: emp_no
    COLLATION: A
  CARDINALITY: 286271   -- Cardinality值
     SUB_PART: NULL
       PACKED: NULL
     NULLABLE: 
   INDEX_TYPE: BTREE
      COMMENT: 
INDEX_COMMENT: 
*************************** 2. row ***************************
TABLE_CATALOG: def
 TABLE_SCHEMA: employees
   TABLE_NAME: salaries
   NON_UNIQUE: 0
 INDEX_SCHEMA: employees
   INDEX_NAME: PRIMARY
 SEQ_IN_INDEX: 2          -- 索引序號爲2
  COLUMN_NAME: from_date
    COLLATION: A
  CARDINALITY: 2760952    -- Cardinality值
     SUB_PART: NULL
       PACKED: NULL
     NULLABLE: 
   INDEX_TYPE: BTREE
      COMMENT: 
INDEX_COMMENT: 
2 rows in set (0.00 sec)

---
---  可以看出,上面兩個方法得到的Cardinality的值是相等
---  結論就是information_schema.STATISTICS這張表記錄了Cardinality信息
---
  • 檢查表的索引創建的情況,判斷該索引是否有創建的必要
--
-- 1. 表的信息如table_schema, table_name, table_rows等
--    在information_schema.TABLES中
--
mysql> show create table TABLES\G  
*************************** 1. row ***************************
       Table: TABLES
Create Table: CREATE TEMPORARY TABLE `TABLES` (
  `TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
  `TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '',   -- 表所在的庫
  `TABLE_NAME` varchar(64) NOT NULL DEFAULT '',     -- 表名
  `TABLE_TYPE` varchar(64) NOT NULL DEFAULT '',
  `ENGINE` varchar(64) DEFAULT NULL,
  `VERSION` bigint(21) unsigned DEFAULT NULL,
  `ROW_FORMAT` varchar(10) DEFAULT NULL,
  `TABLE_ROWS` bigint(21) unsigned DEFAULT NULL,    -- 表的記錄數
  `AVG_ROW_LENGTH` bigint(21) unsigned DEFAULT NULL,
  `DATA_LENGTH` bigint(21) unsigned DEFAULT NULL,
  `MAX_DATA_LENGTH` bigint(21) unsigned DEFAULT NULL,
  `INDEX_LENGTH` bigint(21) unsigned DEFAULT NULL,
  `DATA_FREE` bigint(21) unsigned DEFAULT NULL,
  `AUTO_INCREMENT` bigint(21) unsigned DEFAULT NULL,
  `CREATE_TIME` datetime DEFAULT NULL,
  `UPDATE_TIME` datetime DEFAULT NULL,
  `CHECK_TIME` datetime DEFAULT NULL,
  `TABLE_COLLATION` varchar(32) DEFAULT NULL,
  `CHECKSUM` bigint(21) unsigned DEFAULT NULL,
  `CREATE_OPTIONS` varchar(255) DEFAULT NULL,
  `TABLE_COMMENT` varchar(2048) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

--
-- 2. information.STATISTICS中存在 table_schema 和 table_name 信息
--
mysql> show create table STATISTICS\G
*************************** 1. row ***************************
       Table: STATISTICS
Create Table: CREATE TEMPORARY TABLE `STATISTICS` (
  `TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
  `TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '',  -- 表所在的庫
  `TABLE_NAME` varchar(64) NOT NULL DEFAULT '',  -- 表名
  `NON_UNIQUE` bigint(1) NOT NULL DEFAULT '0',
  `INDEX_SCHEMA` varchar(64) NOT NULL DEFAULT '', 
  `INDEX_NAME` varchar(64) NOT NULL DEFAULT '',   -- 索引名
  `SEQ_IN_INDEX` bigint(2) NOT NULL DEFAULT '0', 
  `COLUMN_NAME` varchar(64) NOT NULL DEFAULT '',
  `COLLATION` varchar(1) DEFAULT NULL,
  `CARDINALITY` bigint(21) DEFAULT NULL, 
  `SUB_PART` bigint(3) DEFAULT NULL,
  `PACKED` varchar(10) DEFAULT NULL,
  `NULLABLE` varchar(3) NOT NULL DEFAULT '',
  `INDEX_TYPE` varchar(16) NOT NULL DEFAULT '',
  `COMMENT` varchar(16) DEFAULT NULL,
  `INDEX_COMMENT` varchar(1024) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)


--
-- 3. 將TABLES 和 STATISTICS 表中的table_schema和table_name相關聯
--    通過Cardinality和table_rows 計算,即可得到對應索引名的 選擇性
--

--
-- 3.1 因爲存在複合索引,所以我們要取出複合索引中seq最大的哪個值
--     這樣取出的cardinality值纔是最大的
--
mysql> select 
    ->     table_schema, table_name, index_name,
    ->     max(seq_in_index)  -- 取出最大的seq號後,選出index_name等信息
    -> from
    ->     STATISTICS
    -> group by table_schema , table_name , index_name\G

--  -----------省略其他輸出-----------
*************************** 10. row ***************************
     table_schema: burn_test
       table_name: test_index_2
       index_name: idx_mul_ab   -- 這個是上次測試複合索引建立的index
max(seq_in_index): 2            -- 取出了最大的seq
--  -----------省略其他輸出-----------

--
--  3.2 得到了最大的seq,從而可以取出對應的cardinality
--

mysql> select 
    ->     table_schema, table_name, index_name, cardinality
    -> from
    ->     STATISTICS
    -> where
    ->     (table_schema , table_name, index_name, seq_in_index) in 
    ->        (select 
    ->             table_schema, table_name, 
    ->             index_name, max(seq_in_index)
    ->         from
    ->             STATISTICS
    ->         group by table_schema , table_name , index_name)\G

*************************** 1. row ***************************
table_schema: burn_test
  table_name: Orders
  index_name: PRIMARY
 cardinality: 5
*************************** 2. row ***************************
table_schema: burn_test
  table_name: Orders_MV
  index_name: product_name
 cardinality: 3
*************************** 3. row ***************************
table_schema: burn_test
  table_name: child
  index_name: par_ind
 cardinality: 0
*************************** 4. row ***************************
table_schema: burn_test
  table_name: parent
  index_name: PRIMARY
 cardinality: 1
*************************** 5. row ***************************
table_schema: burn_test
  table_name: t4
  index_name: PRIMARY
 cardinality: 4

--  -----------省略其他輸出-----------

--
-- 3.3 最後通過table_schema和table_name 讓上述的信息和TABLES表進行關聯
--

SELECT 
     t.TABLE_SCHEMA,t.TABLE_NAME,INDEX_NAME, CARDINALITY, TABLE_ROWS, 
     CARDINALITY/TABLE_ROWS AS SELECTIVITY  -- 得到選擇性
FROM
    TABLES t,  -- 查詢的表一,TABLES
	(
		SELECT     
			table_schema,
			table_name,
			index_name,
			cardinality
		FROM STATISTICS 
		WHERE (table_schema,table_name,index_name,seq_in_index) IN (
		SELECT 
			table_schema,
			table_name,
			index_name,
			MAX(seq_in_index)
		FROM
			STATISTICS
		GROUP BY table_schema , table_name , index_name )
	) s   -- 查詢的表二,就是上面3.2的查詢結果
WHERE
    t.table_schema = s.table_schema   -- 通過庫關聯
        AND t.table_name = s.table_name  -- 再通過表變量
        AND t.table_schema = 'employees'   -- 指定某一個庫名
ORDER BY SELECTIVITY;

+--------------+--------------+------------+-------------+------------+------------+
| TABLE_SCHEMA | TABLE_NAME   | index_name | cardinality | TABLE_ROWS | SELECTIVITY |
+--------------+--------------+------------+-------------+------------+------------+
| employees    | dept_emp     | dept_no    |           8 |     330400 |     0.0000 |
| employees    | salaries     | PRIMARY    |      286271 |    2760952 |     0.1037 |
| employees    | dept_manager | dept_no    |           9 |         24 |     0.3750 |
| employees    | titles       | PRIMARY    |      296887 |     440887 |     0.6734 |
| employees    | dept_emp     | PRIMARY    |      298761 |     330400 |     0.9042 |
| employees    | titles       | PRIMARY    |      440166 |     440887 |     0.9984 |
| employees    | salaries     | PRIMARY    |     2760952 |    2760952 |     1.0000 |
| employees    | dept_manager | PRIMARY    |          24 |         24 |     1.0000 |
| employees    | titles       | PRIMARY    |      440887 |     440887 |     1.0000 |
| employees    | departments  | PRIMARY    |           9 |          9 |     1.0000 |
| employees    | employees    | PRIMARY    |      298124 |     298124 |     1.0000 |
| employees    | dept_emp     | PRIMARY    |      330400 |     330400 |     1.0000 |
| employees    | dept_manager | PRIMARY    |          24 |         24 |     1.0000 |
| employees    | departments  | dept_name  |           9 |          9 |     1.0000 |
+--------------+--------------+------------+-------------+------------+------------+

--
--  通過最後一列的SELECTIVITY是否接近1,判斷該索引創建是否合理
--  注意:
--  Cardinality和table_rows的值,都是通過隨機採樣,預估得到的
--  當analyze前後,Cardinality值相差較多,說明該索引是不應該被創建的(頁上的記錄數值分佈不平均)
--
--  推薦 SELECTIVITY 15% 以上是適合的

--
-- 索引使用情況
--

mysql> select * from x$schema_index_statistics limit 1\G
*************************** 1. row ***************************
  table_schema: employees
    table_name: employees
    index_name: PRIMARY       --  索引名字
 rows_selected: 300024        --  讀取的記錄數
select_latency: 370177723990  --  使用該索引讀取時總的延遲時間370毫秒(單位是皮秒)
 rows_inserted: 0             --  插入的行數
insert_latency: 0
  rows_updated: 0             --  更新的行數
update_latency: 0
  rows_deleted: 0
delete_latency: 0
1 row in set (0.00 sec)

-- 結合 之前的SELECTIVITY和這裏的數值,可以更好的判斷索引是否合理
-- 重啓後數據歸0

索引是要排序的,建立索引越多,排序以及維護成本會很大,插入數據的速度會變慢,所以索引建立的多,不是僅僅是浪費空間,還會降低性能,增加磁盤IO

注意:MySQL5.6的版本STATISTICS數據存在問題,截止5.6.28仍然存在,官方定性爲Bug

作業一:在MySQL5.6中使用mysql.innodb_index_stats得到索引的選擇性(SELECTIVITY)


二. MySQL5.6安裝sys庫

shell > git clone https://github.com/mysql/mysql-sys.git
shell > ls | grep sys_56.sql
sys_56.sql # 這個就是我們要安裝的到mysql5.6的sys

shell> mysql -u root -S /tmp/mysql.sock_56 < sys_56.sql  # 直接導入即可
mysql> select version();
+------------+
| version()  |
+------------+
| 5.6.27-log |
+------------+
1 row in set (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| burn_test          |
| burn_test_56       |
| mysql              |
| performance_schema |
| sys                |  -- 新安裝的sys庫,但是這個裏面只有88個記錄,因爲5.7中增加了幾張表,有101個記錄
| test               |
+--------------------+
7 rows in set (0.00 sec)

三. Explain(二)

1. Explain輸出介紹

含義
id 執行計劃的id標誌
select_type SELECT的類型
table 輸出記錄的表
partitions 符合的分區,[PARTITIONS]
type JOIN的類型
possible_keys 優化器可能使用到的索引
key 優化器實際選擇的索引
key_len 使用索引的字節長度
ref 進行比較的索引列
rows 優化器預估的記錄數量
filtered 根據條件過濾得到的記錄的百分比[EXTENDED]
extra 額外的顯示選項

(1). id

從上往下理解,不一定 id 序號大的先執行

可以簡單的理解爲 id 相等的從上往下看,id 相等的從下往上看。但是在某些場合也不一定適用

(2). select_type

select_type 含義
SIMPLE 簡單SELECT(不使用UNION或子查詢等)
PRIMARY 最外層的select
UNION UNION中的第二個或後面的SELECT語句
DEPENDENT UNION UNION中的第二個或後面的SELECT語句,依賴於外面的查詢
UNION RESULT UNION的結果
SUBQUERY 子查詢中的第一個SELECT
DEPENDENT SUBQUERY 子查詢中的第一個SELECT,依賴於外面的查詢
DERIVED 派生表的SELECT(FROM子句的子查詢)
MATERIALIZED 物化子查詢
UNCACHEABLE SUBQUERY 不會被緩存的並且對於外部查詢的每行都要重新計算的子查詢
UNCACHEABLE UNION 屬於不能被緩存的 UNION中的第二個或後面的SELECT語句
  • MATERIALIZED
    • 產生中間臨時表(實體)
    • 臨時表自動創建索引並和其他表進行關聯,提高性能
    • 和子查詢的區別是,優化器將可以進行MATERIALIZED的語句自動改寫成join,並自動創建索引

(3). table

  • 通常是用戶操作的用戶表
  • <unionM, N> UNION得到的結果表
  • 派生表,由id=N的語句產生
  • 由子查詢物化產生的表,由id=N的語句產生

####(4). type

按照圖上箭頭的順序來看,成本(cost)是從小到大

explain_type

####(5). extra

explain_extra

  • Using filesort:可以使用複合索引將filesort進行優化。提高性能
  • Using index:比如使用覆蓋索引
  • Using where: 使用where過濾條件

Extra的信息是可以作爲優化的提示,但是更多的是優化器優化的一種說明

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