MySQL查詢優化之explain 執行計劃 深入解析(精品)

相關文章

標題 鏈接
SQL優化 實戰 >>點擊查看<<正在編寫ing

一. 前言

在日常開發過程中,由於時間緊迫,項目組成員水平不一,會導致很多的SQL執行很慢. 此時我們一般會使用Explain 命令來查看這些SQL語句的執行計劃,來分析SQL的執行過程.查看該SQL語句有沒有使用上索引,有沒有做全表掃描 等等. 我們深入的瞭解MySQl基於開銷的優化器,還可以獲得很多可能被優化器考慮到的訪問策略的細節,以及當運行SQL語句時哪種策略預計會被優化器採用。

使用Explain關鍵字可以模擬優化器執行SQL語句,分析查詢語句或是結構的性能瓶頸。在select語句之前增加explaion關鍵字,MySQL會在查詢上設置一個標記,執行查詢會返回執行計劃的信息,而不是執行SQL。

二. Explain 用途

  1. 表的讀取順序如何
  2. 數據讀取操作有哪些操作類型
  3. 哪些索引可以使用
  4. 哪些索引被實際使用
  5. 表之間是如何引用
  6. 每張表有多少行被優化器查詢
    … …

三. Explain語法

explain SQL語句
例:

explain select * from user

四. 執行效果

4.1. 命令行

mysql> explain select * from userInfo where id = 1 \G
******************************************************
                      id: 1
      select_type: SIMPLE
                table: userInfo
         partitions: NULL
                type: const
possible_keys: PRIMARY
                 key: PRIMARY
          key_len: 4
                 ref: const
              rows: 1
          filtered: 100.00
            Extra: NULL
******************************************************

4.2. 可視化工具–HeidiSQL Portable 9.4

explain select * from user

在這裏插入圖片描述

五. Explain 字段詳解

5.1. 一覽全局字段

字段 解釋
id select查詢的序列號,包含一組數字,表示查詢中執行select子句或操作表的順序
select_type 查詢類型
tab 正在訪問哪個表
partitions 匹配的分區
type 訪問的類型
possible_keys 顯示可能應用在這張表中的索引,一個或多個,但不一定實際使用到
key 實際使用到的索引,如果爲NULL,則沒有使用索引
key_len 表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度
ref 顯示索引的哪一列被使用了,如果可能的話,是一個常數,哪些列或常量被用於查找索引列上的值
rows 根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需讀取的行數
filtered 查詢的錶行佔表的百分比
Extra 包含不適合在其它列中顯示但十分重要的額外信息

在這裏插入圖片描述

5.2. Id字段

  1. id列的編號是select的序列號,有幾個select就有幾個id,並且id的順序是按select出現的順序增長的。
  2. id越大執行優先級越高,id相同則從上往下執行,id爲NULL最後執行。

5.2.1. id相同

說明:
  從上至下 順序執行
腳本:

explain 
select t1.* , t2.* , t3.*
from actor t1 , film t2 , film_actor t3
where t3.film_id = t2.id and t3.actor_id = t1.id

執行結果:
在這裏插入圖片描述
解說:
  如圖所示,ID列的值全爲1,代表執行順序從t1表開始加載,依次爲t3,t2

5.2.2. id不相同

說明:
  如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行
腳本:

explain 
select t1.*
from actor t1
where t1.id = (select t2.actor_id from film_actor t2 where t2.id=3)

執行結果:
在這裏插入圖片描述
解說:
  如上圖所示 , t2表的id值爲2 , 所以執行順序將先從t2表開始加載,然後再執行t1表

5.2.3. id相同,又不相同

說明:

  1. 將id不相同的部分可以分爲一組, id值越大,越限執行
  2. id 相同的一組 , 從上至下順序執行

腳本:

explain
select t1.* from actor t1 left join film_actor t2 on t1.id = t2.actor_id
union
select t1.* from actor t1 right join film_actor t2 on t1.id = t2.actor_id

執行結果:
在這裏插入圖片描述
解說:

  1. id如果相同,可以認爲是一組,從上往下順序執行;
  2. 在所有組中,id值越大,優先級越高,越先執行

5.3. select_type字段

Select_type:查詢的類型,
要是用於區別:普通查詢、聯合查詢、子查詢等的複雜查詢

5.3.1.類型如下

類型 描述
SIMPLE 簡單的select查詢中不好喊子查詢或者UNION
PRIMARY 查詢中若包含任何複雜的子部分,最外層查詢則被標記爲
SUBQUERY 在SELECT或WHERE列表中包含了子查詢
DERIVED(衍生) 在FROM列表中包含的子查詢被標記爲DERIVED(衍生). MySQL會遞歸執行這些子查詢, 把結果放在臨時表裏。
UNION 若第二個SELECT出現在UNION之後,則被標記爲UNION
UNION RESULT 從UNION表獲取結果的SELECT

5.3.2. SIMPLE

說明:
  簡單的select查詢中不好喊子查詢或者UNION
腳本:

explain 
select t1.* from actor t1 ,film_actor t2 , film t3
where t1.id = t2.actor_id and t3.id = t2.film_id

執行結果:
在這裏插入圖片描述

5.3.2. PRIMARYSUBQUERY

說明:
   PRIMARY: 查詢中若包含任何複雜的子部分,最外層查詢則被標記爲主查詢
   SUBQUERY: 在SELECT或WHERE列表中包含了子查詢
腳本:

explain 
select t1.*
from actor t1
where t1.id = (select t2.actor_id from film_actor t2 where t2.id=3)

執行結果:
在這裏插入圖片描述

5.3.3. DERIVED

說明:
  在FROM列表中包含的子查詢被標記爲DERIVED(衍生)
  MySQL會遞歸執行這些子查詢, 把結果放在臨時表裏。

MySQL5.7+ 進行優化了,增加了derived_merge(派生合併),默認開啓,可加快查詢效率

5.3.4. UNION RESULTUNION

說明:
  UNION:若第二個SELECT出現在UNION之後,則被標記爲UNION
  UNION RESULT:從UNION表獲取結果的SELECT
腳本:

explain
select t1.* from actor t1 left join film_actor t2 on t1.id = t2.actor_id
union
select t1.* from actor t1 right join film_actor t2 on t1.id = t2.actor_id

執行結果:
在這裏插入圖片描述

5.4. table字段

  顯示這一行的數據是關於哪張表的
腳本:

explain
select * from actor  

執行結果:
在這裏插入圖片描述

5.5. type

 type顯示的是訪問類型,是較爲重要的一個指標,結果值從最好到最壞依次是:
 system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

需要記憶的
  NULL>system>const>eq_ref>ref>range>index>ALL

 一般來說,得保證查詢至少達到range級別,最好能達到ref。

5.5.1. Systemconst

說明:
  System:表只有一行記錄(等於系統表),這是const類型的特列,平時不會出現,這個也可以忽略不計
  const:表示通過索引一次就找到了. const用於比較primary key或者unique索引。因爲只匹配一行數據,所以很快. 如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量
腳本:

explain
select * from (select * from actor t1 where t1.id = 1) s1 ;

執行結果:
在這裏插入圖片描述

5.5.2. eq_ref

說明:
   唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描
腳本:

explain
select * from actor t1 left join film_actor t2 on t2.id =  t1.id ;

執行結果:
在這裏插入圖片描述

5.5.3. ref

說明:
   非唯一性索引掃描,返回匹配某個單獨值的所有行. 本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而,它可能會找到多個符合條件的行,所以他應該屬於查找和掃描的混合體

表示的是聯合索引.

腳本:

explain
select count(distinct actor_id) from film_actor t1 where t1.actor_id = 1

執行結果:
在這裏插入圖片描述

5.5.4. Range

說明:

  1. 只檢索給定範圍的行,使用一個索引來選擇行。key 列顯示使用了哪個索引
  2. 一般就是在你的where語句中出現了between、<、>、in等的查詢
  3. 這種範圍掃描索引掃描比全表掃描要好,因爲它只需要開始於索引的某一點,而結束語另一點,不用掃描全部索引。

腳本:

explain
select t1.* from actor t1 where t1.id between 1 and 3

執行結果:
在這裏插入圖片描述

5.6. possible_keyskey

possible_keys:可能使用的key
Key:實際使用的索引。如果爲NULL,則沒有使用索引

查詢中若使用了覆蓋索引,則該索引和查詢的select字段重疊
這裏的覆蓋索引非常重要,後面會單獨的來講

解說:

其中key和possible_keys都可以出現null的情況(結婚邀請朋友的例子)

  1. 結婚時,邀請了自己認爲回來的朋友—> possible_keys
  2. 實際到場的朋友---->key

5.7. key_len

說明:
Key_len表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好
key_len顯示的值爲索引字段的最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的

注意:
更具底層使用的不同存儲引擎,受影響的行數這個指標可能是一個估計值,也可能是一個精確值. 即使受影響的行數是一個估計值(例如 當使用InnoDB存儲引擎管理表存儲時),通常情況下這個估計只是一個足以使優化器租出一個有充分依據 的決定

  • key_len表示索引使用的字節數,
  • 根據這個值,就可以判斷索引使用情況,特別是在組合索引的時候,判斷所有的索引字段是否都被查詢用到。
  • char和varchar跟字符編碼也有密切的聯繫,
  • latin1佔用1個字節,gbk佔用2個字節,utf8佔用3個字節。(不同字符編碼佔用的存儲空間不同)

5.7.1 字符類型

在這裏插入圖片描述

以上這個表列出了所有字符類型,但真正建所有的類型常用情況只是CHAR、VARCHAR

5.7.2 字符類型-索引字段爲char類型+不可爲Null時

腳本:

CREATE TABLE `s1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(10) NOT NULL,
  `addr` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

explain select * from s1 where name='enjoy';

執行結果:
在這裏插入圖片描述
解說:

name這一列爲char(10),字符集爲utf-8佔用3個字節
Keylen=10*3

5.7.3 字符類型-索引字段爲char類型+允許爲Null時

腳本:

CREATE TABLE `s2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(10) DEFAULT NULL,
  `addr` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
explain select * from s2 where name='enjoyedu';

執行結果:
在這裏插入圖片描述
解說:

name這一列爲char(10),字符集爲utf-8佔用3個字節,外加需要存入一個null值
Keylen=10*3+1(null) 結果爲31
結果允許爲null時,需要+1

5.7.4 索引字段爲varchar類型+不可爲Null時

腳本:

CREATE TABLE `s3` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  `addr` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
explain select * from s3 where name='enjoyeud';

執行結果:
在這裏插入圖片描述

解說:

Keylen=varchar(n)變長字段+不允許Null=n*(utf8=3,gbk=2,latin1=1)+2

5.7.5 索引字段爲varchar類型+允許爲Null時

腳本:

CREATE TABLE `s4` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  `addr` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
explain select * from s4 where name='enjoyeud';

執行結果:
在這裏插入圖片描述

解說:

Keylen=varchar(n)變長字段+允許Null=n*(utf8=3,gbk=2,latin1=1)+1(NULL)+2

5.7.6 數值類型

在這裏插入圖片描述

CREATE TABLE `numberKeyLen ` (
`c0`  int(255) NOT NULL ,
`c1`  tinyint(255) NULL DEFAULT NULL ,
`c2`  smallint(255) NULL DEFAULT NULL ,
`c3`  mediumint(255) NULL DEFAULT NULL ,
`c4`  int(255) NULL DEFAULT NULL ,
`c5`  bigint(255) NULL DEFAULT NULL ,
`c6`  float(255,0) NULL DEFAULT NULL ,
`c7`  double(255,0) NULL DEFAULT NULL ,
PRIMARY KEY (`c0`),
INDEX `index_tinyint` (`c1`) USING BTREE ,
INDEX `index_smallint` (`c2`) USING BTREE ,
INDEX `index_mediumint` (`c3`) USING BTREE ,
INDEX `index_int` (`c4`) USING BTREE ,
INDEX `index_bigint` (`c5`) USING BTREE ,
INDEX `index_float` (`c6`) USING BTREE ,
INDEX `index_double` (`c7`) USING BTREE 
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT
;


EXPLAIN
select * from  numberKeyLen where c1=1

EXPLAIN
select * from  numberKeyLen where c2=1

EXPLAIN
select * from  numberKeyLen where c3=1


EXPLAIN
select * from  numberKeyLen where c4=1

EXPLAIN
select * from  numberKeyLen where c5=1

EXPLAIN
select * from  numberKeyLen where c6=1

EXPLAIN
select * from  numberKeyLen where c7=1

5.7.7 日期和時間

在這裏插入圖片描述
datetime類型在5.6中字段長度是5個字節
datetime類型在5.5中字段長度是8個字節

CREATE TABLE `datatimekeylen ` (
`c1`  date NULL DEFAULT NULL ,
`c2`  time NULL DEFAULT NULL ,
`c3`  year NULL DEFAULT NULL ,
`c4`  datetime NULL DEFAULT NULL ,
`c5`  timestamp NULL DEFAULT NULL ,
INDEX `index_date` (`c1`) USING BTREE ,
INDEX `index_time` (`c2`) USING BTREE ,
INDEX `index_year` (`c3`) USING BTREE ,
INDEX `index_datetime` (`c4`) USING BTREE ,
INDEX `index_timestamp` (`c5`) USING BTREE 
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT
;


EXPLAIN
SELECT  * from datatimekeylen where c1 = 1

EXPLAIN
SELECT  * from datatimekeylen where c2 = 1

EXPLAIN
SELECT  * from datatimekeylen where c3 = 1

EXPLAIN
SELECT  * from datatimekeylen where c4 = 1

EXPLAIN
SELECT  * from datatimekeylen where c5 = 1

5.7.8 總結

5.7.8.1 字符類型

  1. 變長字段需要額外的2個字節(VARCHAR值保存時只保存需要的字符數,另加一個字節來記錄長度(如果列聲明的長度超過255,則使用兩個字節),所以VARCAHR索引長度計算時候要加2),固定長度字段不需要額外的字節。
  2. 而NULL都需要1個字節的額外空間,所以索引字段最好不要爲NULL,因爲NULL讓統計更加複雜並且需要額外的存儲空間。
  3. 複合索引有最左前綴的特性,如果複合索引能全部使用上,則是複合索引字段的索引長度之和,這也可以用來判定複合索引是否部分使用,還是全部使用。

5.6.7.2 整數/浮點數/時間類型的索引長度

NOT NULL=字段本身的字段長度
NULL=字段本身的字段長度+1(因爲需要有是否爲空的標記,這個標記需要佔用1個字節)

datetime類型在5.6中字段長度是5個字節,datetime類型在5.5中字段長度是8個字節

5.8 Ref

說明:
   顯示索引的哪一列被使用了,如果可能的話,是一個常數。哪些列或常量被用於查找索引列上的值
腳本:

explain 
select * from actor t1 , film_actor t2 where t1.id = t2.actor_id and t2.film_id=2

執行結果:
在這裏插入圖片描述
解說:
  由key_len可知t1表的actor_id被充分使用,
  actor_id匹配t2表的idfilm_id匹配了一個常量,即 1

   其中 【mysql_explain.t2.actor_id】 爲 【數據庫.表.列】

5.8 Rows

說明:
   根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數
腳本:

explain 
select * from actor t1 , film_actor t2 where t1.id = t2.actor_id and t2.film_id=2

執行結果:
在這裏插入圖片描述

5.9 Extra

 包含不適合在其它列中顯示但十分重要的額外信息

5.9.1 選項描述

描述
Using filesort 說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成的排序操作稱爲"文件排序"
Using temporary 使了用臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和分組查詢 group by。
Using index 表示相應的select操作中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯!   如果同時出現using where,表明索引被用來執行索引鍵值的查找;   如果沒有同時出現using where,表明索引用來讀取數據而非執行查找動作
Using where 使用了where條件
Using join buffer 使用了連接緩存
impossible where where子句的值總是false,不能用來獲取任何元素
distinct 一單mysql找到了與形相聯合匹配的行,就不在搜索了

說明:

  1. 由於Markdown 編輯格式問題, 描述不是很全
  2. 在下面各個值說明中,比較完整

5.9.2 Using filesort

說明:
  說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。
  MySQL中無法利用索引完成的排序操作稱爲“文件排序”

當發現有Using filesort 後,實際上就是發現了可以優化的地方

腳本:

explain 
select * from film_actor t1 where t1.film_id=1 order by t1.actor_id

執行結果:
在這裏插入圖片描述
解說:
  上圖其實是一種索引失效的情況,後面會講,可以看出查詢中用到了個聯合索引,索引分別爲actor_id,film_id

5.9.3 Using temporary

說明:
   使用了臨時表保存中間結果,MySQL在對結果排序時使用臨時表,常見於排序order by 和分組查詢group by
腳本:

explain 
select * from film_actor t1 where t1.film_id=1 group by t1.remark

執行結果:
在這裏插入圖片描述
解說:
  尤其發現在執行計劃裏面有using filesort而且還有Using temporary的時候,特別需要注意

5.9.3 Using index

說明:
表示相應的select操作中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯!
如果同時出現using where,表明索引被用來執行索引鍵值的查找
腳本:

explain 
select * from film t1 where t1.name="我的快樂購"

執行結果:
在這裏插入圖片描述
如果沒有同時出現using where,表明索引用來讀取數據而非執行查找動作
  
腳本:

explain 
select * from film t1

執行結果:
在這裏插入圖片描述

5.9.3.1 覆蓋索引

  1. 覆蓋索引(Covering Index),一說爲索引覆蓋。
  2. 理解方式一:就是select的數據列只用從索引中就能夠取得,不必讀取數據行,MySQL可以利用索引返回select列表中的字段,而不必根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋。
  3. 理解方式二:索引是高效找到行的一個方法,但是一般數據庫也能使用索引找到一個列的數據,因此它不必讀取整個行。畢竟索引葉子節點存儲了它們索引的數據;當能通過讀取索引就可以得到想要的數據,那就不需要讀取行了。一個索引包含了(或覆蓋了)滿足查詢結果的數據就叫做覆蓋索引

注意:
 如果要使用覆蓋索引,一定要注意select列表中只取出需要的列,不可select *,
因爲如果將所有字段一起做索引會導致索引文件過大,查詢性能下降。

 所以,千萬不能爲了查詢而在所有列上都建立索引,會嚴重影響修改維護的性能。

5.9.4 Using whereusing join buffer

說明:
   Using where:表明使用where過濾
   using join buffer:使用了連接緩存

很容易理解,不做案例演示

腳本:

-- 查詢MySQl 默認的join_buffer_size
show VARIABLES like '%join_buffer_size%'

執行結果:
在這裏插入圖片描述

5.9.5 impossible where

說明:
   where子句的值總是false,不能用來獲取任何元組
腳本:

-- where 條件的字句總是爲false
explain 
select * from film t1 where 1=2

-- 
explain 
select * from film t1 where t1.name="隨機填入" and t1.name="隨機填入"

執行結果:
在這裏插入圖片描述

### 5.9.6 `distinct`

說明:
   一旦mysql找到了與行相聯合匹配的行,就不再搜索了
腳本:

explain 
select  distinct  t1.id from actor t1 ,film_actor t2 , film t3 where t1.id = t2.actor_id and t2.film_id = t3.id

執行結果:
在這裏插入圖片描述
解說:
  

5.9.7 Select tables optimized away

說明:
   SELECT操作已經優化到不能再優化了(MySQL根本沒有遍歷表或索引就返回數據了)
腳本:

explain 
select  min(t1.id) from actor t1 

執行結果:
在這裏插入圖片描述


案例腳本

1. 建表腳本


-- actor建表語句:
CREATE TABLE `actor` (
  `id` int(11) NOT NULL,
  `name` varchar(45) DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

-- film建表語句:
CREATE TABLE `film` (
  `id` int(11) NOT NULL,
  `name` varchar(10) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8

-- film_actor建表語句:
CREATE TABLE `film_actor` (
  `id` int(11) NOT NULL,
  `film_id` int(11) NOT NULL,
  `actor_id` int(11) NOT NULL,
  `remark` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_film_actor_id` (`film_id`,`actor_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2. 插入數據sql


-- actor 表
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (3, '麗麗', '2020-02-28 22:50:57');
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (1, '媛媛', '2020-02-28 22:49:53');
INSERT INTO `actor` (`id`, `name`, `update_time`) VALUES (2, '銳銳', '2020-02-28 22:50:44');

-- film 表
INSERT INTO `film` (`id`, `name`) VALUES (3, '授課老師看到');
INSERT INTO `film` (`id`, `name`) VALUES (1, '我的快樂購');
INSERT INTO `film` (`id`, `name`) VALUES (2, '哈哈杜拉拉');


-- film_actor 表
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`, `remark`) VALUES (5, 3, 2, NULL);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`, `remark`) VALUES (6, 3, 2, NULL);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`, `remark`) VALUES (2, 2, 1, NULL);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`, `remark`) VALUES (4, 2, 3, NULL);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`, `remark`) VALUES (1, 1, 1, NULL);
INSERT INTO `film_actor` (`id`, `film_id`, `actor_id`, `remark`) VALUES (3, 1, 3, NULL);


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