type列 其實很關鍵。 解釋如下:
type列
這一列表示關聯類型或訪問類型,即MySQL決定如何查找表中的行。
依次從最優到最差分別爲:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
NULL:mysql能夠在優化階段分解查詢語句,在執行階段用不着再訪問表或索引。例如:在索引列中選取最小值,可以單獨查找索引來完成,不需要在執行時訪問表
const, system
:mysql能對查詢的某部分進行優化並將其轉化成一個常量(可以看show warnings 的結果)。用於 primary key 或 unique key 的所有列與常數比較時,所以表最多有一個匹配行,讀取1次,速度比較快。
結果是:
EXPLAIN SELECT COUNT(1) FROM actor; 走 PRIMARY 索引: Using index
—— 爲什麼 possible_keys 爲空?
EXPLAIN SELECT COUNT(*) FROM actor; 同上
EXPLAIN SELECT COUNT(*) FROM actor WHERE id > 1; 基本同上,Extra 是 Using where; Using index
EXPLAIN SELECT COUNT(name) FROM actor; 因爲 name上面沒有索引, 所以是走 全表掃描。
EXPLAIN SELECT COUNT(name) FROM actor_no_key; 同上,因爲 name上面沒有索引, 所以是走 全表掃描。
EXPLAIN SELECT COUNT(1) FROM actor_no_key; 因爲addresss字段存在唯一索引 index_addresss, 所以是 Using index, 不過 key_len 是 138 , 有點長..
EXPLAIN SELECT COUNT(id) FROM actor_no_key; 因爲 id 上面沒有索引, 所以是走 全表掃描。
EXPLAIN SELECT COUNT(*) FROM actor_no_key; 同EXPLAIN SELECT COUNT(1) FROM actor_no_key;
EXPLAIN SELECT COUNT(name) FROM actor_no_key WHERE id > 0; 因爲 id 上面沒有索引, 所以是走回表過濾(where 條件就相當於是過濾條件),(過濾 和掃描是什麼區別? 可以認爲過濾前需要掃描一下)。
注意 filtered 是 33.33 , 爲什麼其他的都是 100 ? 可能是因爲數據只有三行的原因, 然後因爲 1/3 = 33.33 % ? 但是新增了幾行,發行 還是33.33 ..
EXPLAIN SELECT COUNT(addresss) FROM actor_no_key WHERE id > 1; 同上
EXPLAIN SELECT COUNT(1) FROM actor_no_key; 同EXPLAIN SELECT COUNT(1) FROM actor_no_key;
2
license_record2 表有 12503260 行數據, --
實際是12696243
CREATE TABLE `license_record2` (
`id` bigint(20) NOT NULL COMMENT '主鍵',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
`created_by` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '創建人',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新時間',
`updated_by` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '更新人',
`app_id` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT 'APP_ID',
`device_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'DEVICE_ID',
`model_id` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT 'model_id',
`manufacturer_id` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '製造商ID',
`user_id` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'user_id',
`brand_name` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '包名-',
`status` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT '狀態',
`expire_date` datetime DEFAULT NULL COMMENT '到期時間',
`deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否刪除',
`status_code` tinyint(4) NOT NULL DEFAULT '1' COMMENT '授權狀態,0、失敗1、成功2、撤回',
`ip_address` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'ip地址',
`sign` varchar(2) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `app_id` (`app_id`,`device_id`,`manufacturer_id`,`model_id`,`brand_name`,`status_code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC;
注意索引: `app_id`, `device_id`, `manufacturer_id`, `model_id`, `brand_name`, `status_code`
EXPLAIN SELECT * FROM lic_s_0.`license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' and model_id = 'PA2170' and status_code = 1 ORDER BY updated_at desc limit 1 ;
可以看到const,const,const,const, 竟然有四個 const! Extra 是Using index condition; Using filesort, 爲什麼是Using index condition; 而不是 Using index ? 因爲SELECT * 並不能走覆蓋索引。 爲什麼Using filesort ? 因爲有 order by。。
雖然 limit 1 , 但是還是進行了排序。 why , 因爲 limit 語句必須在 order by 後面執行, 所以 ,
繼續
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' and model_id = 'PA2170' and brand_name = 'huawei' and status_code = 1 ;
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' and model_id = 'PA2170' ;
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' ;
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' ;
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' ;
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' and model_id = 'PA2170' and status_code = 1 ;
EXPLAIN SELECT app_id FROM `license_record2` where device_id = '00000000-016a-76e5-0000-000052c2195a' and status_code = 2 limit 1 ;
EXPLAIN SELECT app_id FROM `license_record2` where device_id = '00000000-016a-76e5-0000-000052c2195a' and status_code = 2 limit 1 ;
EXPLAIN SELECT app_id FROM `license_record2` where status_code = 2 limit 1
發現
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' and model_id = 'PA2170' and brand_name = 'huawei' and status_code = 1 ;
竟然提示沒有匹配的row 即行
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' and model_id = 'PA2170' ;
直接走了 index索引,ref 爲4個const
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' ;
直接走了 index索引,ref 爲3
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' ;
直接走了 index索引,ref 爲2
可以看到 key 都是一樣的, type 都是ref,key_len 快速的變小,同時 ref 從4 到3 到現在的2,
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' ;
同樣的直接走了 index索引,不過爲啥這次掃描的rows 這麼多? 大概因爲 相同app_id 爲 20210525104140854 的有 12540行
EXPLAIN SELECT app_id FROM `license_record2` WHERE app_id = '20210525104140854' and device_id = '00000000-0691-d628-0000-00004394fc4f' and manufacturer_id = 'vivo' and model_id = 'PA2170' and status_code = 1 ;
走了 index索引,ref 爲4個const, 同時 使用了Using where; —— why ? 因爲查詢字段雖然有status_code ,但沒有 brand_name , 斷開了也不行, 無法走覆蓋索引, 只能回表過濾查詢, 即Using where
EXPLAIN SELECT app_id FROM `license_record2` where device_id = '00000000-016a-76e5-0000-000052c2195a' and status_code = 2 limit 1 ;
雖然 複合索引包括了device_id 、 status_code , key 也似乎用上了app_id, 但是 ref 爲null,type爲index,key_len 很長,掃描的rows 有12503240, 使用了回表過濾: Using where;
同時感覺所用了索引, Using index —— 但,不知道實際執行的時候會不會使用索引,感覺應該不會。
注意到 possible_keys ref 都是空, 說明什麼?
EXPLAIN SELECT app_id FROM `license_record2` where device_id = '00000000-016a-76e5-0000-000052c2195a' and status_code = 2 limit 1 ;
同上, 重複了
EXPLAIN SELECT app_id FROM `license_record2` where status_code = 2 limit 1
同上, 少了device_id 字段並沒有什麼影響。
EXPLAIN select count(r.app_id) from license_record2 r where r.deleted != 1
去掉 where條件:
where 條件導致不走索引!
少了1, 有一行被刪除!
參考:
https://cloud.tencent.com/developer/article/1093229
https://blog.51cto.com/ajisun/5222707