Explain詳解與索引優化實踐

来源:https://www.cnblogs.com/ZekiChen/archive/2020/02/13/12295702.html
-Advertisement-
Play Games

1、何為explain執行計劃? 使用explain關鍵字可以模擬優化器執行SQL語句,從而知道MySQL是如何使用索引來處理你的SQL查詢語句以及連接表,可以分析查詢語句或是結構的性能瓶頸,幫助我們選擇更好的索引和寫出更優化的查詢語句。(說白了,就是優化SQL的工具) 2、如何使用explain? ...


1、何為explain執行計

使用explain關鍵字可以模擬優化器執行SQL語句,從而知道MySQL是如何使用索引來處理你的SQL查詢語句以及連接表,可以分析查詢語句或是結構的性能瓶頸,幫助我們選擇更好的索引和寫出更優化的查詢語句。(說白了,就是優化SQL的工具

2、如何使用explain?

在你的SQL查詢語句前加上 explain 即可,如explain select * from table,MySQL會在查詢上設置一個標記,執行查詢時,會返回執行計劃的信息,而不是執行這條SQL(如果 from 中包含子查詢,仍會執行該子查詢,將結果放入臨時表)。

3、使用explain的例子

需要使用三張表,分別為 actor 演員表,film 電影表,film_actor 電影-演員關聯表。

CREATE TABLE `actor` (
  `id` int(11) NOT NULL COMMENT '主鍵id',
  `name` varchar(45) DEFAULT NULL COMMENT '演員名稱',
  `update_time` datetime DEFAULT NULL COMMENT '修改時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into `actor` (`id`, `name`, `update_time`) values('1','a','2020-02-11 22:56:00');
insert into `actor` (`id`, `name`, `update_time`) values('2','b','2020-02-11 22:56:00');
insert into `actor` (`id`, `name`, `update_time`) values('3','c','2020-02-11 22:56:00');

CREATE TABLE `film` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `name` varchar(10) DEFAULT NULL COMMENT '電影名稱',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

insert into `film` (`id`, `name`) values('3','film0');
insert into `film` (`id`, `name`) values('1','film1');
insert into `film` (`id`, `name`) values('2','film2');

CREATE TABLE `film_actor` (
  `id` int(11) NOT NULL COMMENT '主鍵id',
  `film_id` int(11) NOT NULL COMMENT '電影id',
  `actor_id` int(11) NOT NULL COMMENT '演員id',
  `remark` varchar(255) DEFAULT NULL COMMENT '備註',
  PRIMARY KEY (`id`),
  KEY `idx_film_actor_id` (`film_id`,`actor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

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('2','1','2',NULL);
insert into `film_actor` (`id`, `film_id`, `actor_id`, `remark`) values('3','2','1',NULL);

執行完以上SQL後,三張表數據對應如下:

下麵展示explain中每個列的信息:

(1)id列

id列的編號是select語句的序列號,有幾個 select 就有幾個id,並且id的序號是按 select 出現的順序而增長的(id越大,對應的select語句越先執行,如果id相等,則從上往下執行,id為NULL最後執行)。

MySQL將select查詢分為簡單查詢(SIMPLE)和複雜查詢(PRIMARY)。

複雜查詢分為三類:簡單子查詢、派生表(from語句中的子查詢)、union查詢。

1)簡單子查詢

執行SQL語句:EXPLAIN SELECT (SELECT 1 FROM actor LIMIT 1) FROM film

2)from子句中的子查詢

執行SQL語句:EXPLAIN SELECT id FROM (SELECT id FROM film) AS der

分析:這個查詢執行時有個臨時表別名為der,外部select查詢引用了這個臨時表。

3)union查詢

執行SQL語句:EXPLAIN SELECT 1 UNION ALL SELECT 1

分析:union結果總是放在一個匿名臨時表中,臨時表不在SQL中出現,因此它的id為NULL。(不推薦使用union,性能不高)

(2)select_type列

這一列表示對應行是簡單還是複雜查詢,如果是複雜查詢,又是上述三種複雜查詢中的哪一種。

1)SIMPLE:簡單查詢。查詢不包含子查詢和union。

執行SQL語句:EXPLAIN SELECT * FROM film WHERE id=2

2)PRIMARY:複雜查詢中最外層的select。

3)SUBQUERY:包含在select中的子查詢(不在from子句中)。

4)DERIVED:包含在from子句中的子查詢。MySQL會將結果存放在一個臨時表中,也稱為派生表(DERIVED的英文含義)。

執行SQL語句:EXPLAIN SELECT (SELECT 1 FROM actor WHERE id=1) FROM (SELECT * FROM film WHERE id=1) der

5)UNION:在union中的第二個和隨後的select。

6)UNION RESULT:從union臨時表檢索結果的select。

執行SQL語句:EXPLAIN SELECT 1 UNION ALL SELECT 1

(3)table列

這一列表示explain的一行正在訪問哪個表。

當from子句中有子查詢時,table列是<DERIVED N>格式,表示當前查詢依賴id=N的查詢,於是先執行id=N的查詢。

當有union時,UNION RESULT的table列的值為<union 1,2>,1和2表示參與union的select行id。

(4)type列

(溫馨提示:以下部分理論有可能解釋完還是懵逼,沒關係,繼續往下看,有實踐例子)

這一列表示關聯類型或訪問類型,即MySQL決定如何查找表中的行,查找數據記錄的大概範圍。

SQL語句查詢效率從最優到最差依次為:system > const > eq_ref > ref > range > index > ALL

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

NULL:MySQL能夠在SQL語句執行之前(即優化階段)分析分解查詢語句,在執行階段用不著再訪問表或索引。例如:在索引列中選取最小值,可以單獨查找索引來完成,不需要在執行時訪問表,出現的頻率不高。

const,system:MySQL能夠對查詢的某部分進行優化並將其轉化成一個常量(可以看show warnings的結果)。用於主鍵索引或唯一索引的所有列與常數比較時,表最多有一個匹配行,讀取1次,速度比較快。system是const的特例,表裡只有一條記錄匹配時為system。

執行SQL語句:EXPLAIN EXTENDED SELECT * FROM (SELECT * FROM film WHERE id=1) tmp

分析:上面的子查詢SELECT * FROM film WHERE id = 1語句where後面id使用的是主鍵索引查詢,主鍵是唯一的,所以查詢結果一定是只有一條記錄,對於明確知道結果集只有一條記錄的查詢,它的type為const類型,性能已經非常高了;而第一個select複雜查詢的表只有一條記錄,所以結果也肯定只有一條記錄(第二個select子查詢之前表中可能是多條記錄),這種特例它的type為system類型,性能最高。

執行SQL語句:EXPLAIN EXTENDED SELECT * FROM (SELECT * FROM film WHERE id=1) tmp;  SHOW WARNINGS;

分析:用explain extended查看執行計劃會比explain多一列filtered,該列給出一個百分比的值,這個值和rows列一起使用,可以估計出那些將要和explain中的前一個表進行連接的行的數目,前一個表就是指explain的id列的值比當前表的id小的表。explain extended還可以搭配show warnings一起使用,它可以給出一個優化建議,真正執行時是執行優化建議的那條SQL,但是如果是很複雜的SQL,它優化出來的結果可能都沒你原先的SQL性能高。

eq_ref:主鍵索引或唯一索引的所有部分被連接使用,最多只會返回一條符合條件的記錄。這可能是在const之外最好的連接類型了,簡單的select查詢不會出現這種type。

執行SQL語句:EXPLAIN SELECT * FROM film_actor LEFT JOIN film ON film_actor.film_id=film.id

分析:有兩條記錄,說明有2次查詢, id相等,則從上往下執行,說明第1條先執行查詢film_actor表,第2條左連接查詢film表。左連接film表並關聯film.id,由於film.id是唯一索引,film表只能關聯一行記錄,所以第2條select的type為eq_ref。

ref:相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一索引的首碼部分,索引要和某個值相比較,可能會找到多條符合條件的記錄。

① 簡單select查詢,name是普通索引(非唯一索引)

執行SQL語句:EXPLAIN SELECT * FROM film WHERE NAME="film1"

② 關聯表查詢,idx_film_actor_id是film_id和actor_id的聯合索引,這裡使用了film_actor的索引左邊首碼部分 film_id。

執行SQL語句:EXPLAIN SELECT * FROM film LEFT JOIN film_actor ON film.id=film_actor.film_id

range:範圍掃描通常出現在in(),between,>,<,>=等操作中。使用一個索引來檢索給定範圍的行。

執行SQL語句:EXPLAIN SELECT * FROM actor WHERE id>1

index: 掃描全表索引,這通常會比ALL快一些。(index是從索引中讀取的,而ALL是從硬碟中讀取)

執行SQL語句:EXPLAIN SELECT * FROM film;(film表所有欄位都加了索引)

ALL: 即全表掃描,意味著MySQL需要從頭到尾去查找所需要的行(不走索引)。通常情況下這需要增加索引來優化了。

執行SQL語句:EXPLAIN SELECT * FROM actor;(actor表有一個欄位沒加索引)

(5)possible_keys列

這一列顯示查詢可能使用哪些索引來查找。

explain時可能出現possible_key有列,而key顯示NULL的情況,這種情況是因為表中數據不多,MySQL認為索引對此查詢幫助不大,選擇了全表查詢。

如果該列是NULL,則沒有相關的索引。在這種情況下,可以通過檢查where子句是否可以創造一個適當的索引來提高查詢性能,然後用explain查看效果。

(6)key列

這一列顯示MySQL實際採用哪個索引來優化對該表的訪問。

如果沒有使用索引,則該列是NULL。如果想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用force index、ignore index。

(7)key_len列

這一列顯示了MySQL在索引里使用的位元組數,通過這個值可以算出具體使用了索引中的哪些列。

舉例來說,film_actor表的聯合索引idx_film_actor_id由film_id和actor_id兩個int列組成,並且每個int是4位元組。通過下麵結果中的key_len=4可推斷出只使用了第一個列flim_id來執行索引查找。

執行SQL語句:EXPLAIN SELECT * FROM film_actor WHERE film_id=2

key_len計算規則如下:

① 字元串

  • char(n):n位元組長度
  • varchar(n):2位元組存儲字元串長度,如果是UTF-8,則長度為3n+2

② 數值類型

  • tinyint:1位元組
  • smallint:2位元組
  • int:4位元組
  • bigint:8位元組

③ 時間類型

  • date:3位元組
  • timestamp:4位元組
  • datetime:8位元組

④ 如果欄位允許為NULL,需要1位元組記錄是否為NULL

(8)ref列

這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const(常量)、欄位名(例:film.id)。

(9)rows列

這一列是MySQL估計要讀取並檢測的行數,註意這個不是結果集里的行數。

(10)Extra列 

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

Using index: 查詢的列被索引覆蓋,並且where篩選條件是索引的前導列(類似聯合索引的最左首碼原則),是性能高的表現。一般是使用了覆蓋索引(即索引包含了所有查詢的欄位)。對於InnoDB來說,如果是普通索引性能會有不少提高。

執行SQL語句:EXPLAIN SELECT film_id FROM film_actor WHERE film_id=1

Using where:查詢的列不完全被索引覆蓋,where篩選條件非索引的前導列。(不走索引,性能較低)

執行SQL語句:EXPLAIN SELECT * FROM actor WHERE name='a'

 Using where; Using index:查詢的列被索引覆蓋,並且where篩選條件是索引列之一但不是索引的前導列,意味著無法直接通過索引來查找符合條件的數據。

執行SQL語句:EXPLAIN SELECT film_id FROM film_actor WHERE actor_id=1

NULL:查詢的列未被索引覆蓋,並且where篩選條件是索引的前導列,意味著用到了索引,但是部分欄位未被索引覆蓋,必須通過“回表”來實現,不是純粹地用到了索引,也不是完全沒用到索引。

執行SQL語句:EXPLAIN SELECT * FROM film_actor WHERE film_id=1

Using index condition:MySQL 5.6版本開始加入的新特性,與Using where類似,查詢的列不完全被索引覆蓋,where條件中是一個前導列的範圍。

執行SQL語句:EXPLAIN SELECT * FROM film_actor WHERE film_id>1

Using temporary:MySQL需要創建一張臨時表來處理查詢。出現這種情況一般是要進行優化的,首先要想到用索引來優化。

① actor.name沒有索引,此時創建了一張臨時表來distinct。(distinct:去除查詢結果中的重覆記錄)

執行SQL語句:EXPLAIN SELECT DISTINCT NAME FROM actor

② film.name建立了idx_name索引,此時查詢時extra是Using index,沒有用臨時表。

執行SQL語句:EXPLAIN SELECT DISTINCT NAME FROM film

Using filesort:MySQL會對結果使用一個外部索引排序,而不是按照索引次序從表裡讀取行。此時MySQL會根據連接類型瀏覽所有符合條件的記錄,並保存排序關鍵字和行指針,然後排序關鍵字並按順序檢索行信息。這種情況下一般也是要考慮使用索引來優化。

① actor.name未創建索引,會瀏覽actor整個表,保存排序關鍵字name和對應的id,然後排序name並檢索行記錄。

執行SQL語句:EXPLAIN SELECT * FROM actor ORDER BY name

② film.name建立了idx_name索引,此時查詢時extra是Using index,因為索引底層數據結構已經是排好序的。

執行SQL語句:EXPLAIN SELECT * FROM film ORDER BY name

4、索引優化最佳實踐

使用了 employees 員工表:

CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `name` varchar(24) NOT NULL COMMENT '員工姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '員工年齡',
  `position` varchar(20) NOT NULL COMMENT '員工職位',
  `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間',
  PRIMARY KEY (`id`),
  KEY `idx_name_age_position` (`name`,`age`,`position`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

insert into `employees` (`id`, `name`, `age`, `position`, `hire_time`) values('1','LiLei','22','manager','2020-02-13 14:22:55');
insert into `employees` (`id`, `name`, `age`, `position`, `hire_time`) values('2','HanMeimei','23','dev','2020-02-13 14:22:57');
insert into `employees` (`id`, `name`, `age`, `position`, `hire_time`) values('3','Lucy','23','dev','2020-02-13 14:22:59');

(1)全值匹配

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name='LiLei'

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name='LiLei' AND age=22

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name='LiLei' AND age=22 AND position='manager'

(2)索引最左首碼原則 

如果索引了多列,要遵循最左首碼原則。指的是查詢從索引的最左前列開始並且不跳過索引中的列。

提問:為什麼聯合索引要想命中索引必須採用最左首碼原則?(命中索引:即是否用到了索引)

以下索引優化規則很多都可以結合下麵這張圖思考,聯合索引底層的索引數據結構圖(B+樹),索引的排序首先按10002排序,接著是Staff,最後才是1996-08-03,如果不先拿第一個欄位10002去比較,根本沒法比較,導致無法命中索引。

提問:以下SQL命中索引?

① EXPLAIN SELECT * FROM employees WHERE age = 22 AND position = 'manager';
② EXPLAIN SELECT * FROM employees WHERE position = 'manager';
③ EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';
④ EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' AND position = 'manager';

分析:

①中的where條件後面age=22不是索引的最左前列,後面就不用看了,沒有命中索引,②也是如此。

③中的name是索引idx_name_age_position的最左前列,命中索引。

④中的name命中索引,position沒有命中索引,因為跳過索引中的age列,中間斷了,age列還是需要全表掃描。

(3)不要在索引列上做任何操作(如計算、函數、自動或手動類型轉換),否則會導致索引失效而轉向全表掃描

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE LEFT(name, 3)='LiLei'

(4)存儲引擎不能使用索引中範圍條件右邊的列

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name='LiLei' AND age>22 AND position='manager'

分析:長度為78,name為74,age是int類型,所以為4,即只有name和age命中索引,position沒有命中索引,因為它屬於age範圍條件右邊的索引列。

(5)儘量使用覆蓋索引(只訪問索引的查詢,索引列包含查詢列),減少 select * 語句

執行SQL語句:EXPLAIN SELECT name,age FROM employees WHERE name='LiLei'

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name='LiLei'

 (6)MySQL在使用不等於(!= 或者 <>)的時候無法使用索引,會導致全表掃描

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name != 'LiLei'

(7)is null,is not null也無法使用索引

執行SQL語句:

EXPLAIN SELECT * FROM employees WHERE name IS NULL

(8)like以通配符開頭('$abc'),MySQL索引會失效導致全表掃描

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name LIKE '%Lei'

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name LIKE 'Lei%'

提問:如何解決like '%字元串%' 索引沒有命中?

① 使用覆蓋索引,查詢欄位必須是建立覆蓋索引欄位

執行SQL語句:EXPLAIN SELECT name,age,position FROM employees WHERE name LIKE '%Lei%'

② 當覆蓋索引指向的欄位是varchar(380)及以上的欄位時,覆蓋索引會失效!

(9)字元串不加單引號,索引失效(內部會做一個字元串轉換函數)

執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name=1000

(10)少用or或in,用它查詢時,非主鍵欄位的索引會失效,主鍵索引有時生效,有時不生效,跟數據量有關,具體還得看MySQL的查詢優化結果

 執行SQL語句:EXPLAIN SELECT * FROM employees WHERE name='LiLei' OR name='Hanmeimei'

總結

like KK% 相當於等於常量,%KK 和 %KK% 相當於範圍。

 


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

-Advertisement-
Play Games
更多相關文章
  • API:application program interface ABI:application binary interface linux系統的ABI文件是ELF格式的 windows系統的ABI文件是exe,msi格式的 系統級開發語言:c/c++ ​ 作品:httpd,vsftpd,ngi ...
  • ARM 前幾天剛發佈了 Cortex-M 家族最新一款內核 - Cortex-M55 以及首款面向 Cortex-M 系列的 microNPU - Ethos-U55。Cortex-M55 是第一款面向 AI/ML 的 Cortex-M 內核,痞子衡也專門為此寫過一篇小文 《為AI/ML而生(Cor... ...
  • 上一篇文章學習了用戶及文件相關許可權,本篇繼續學習防火牆技術。 ...
  • 一、 使用Java操作Redis前,請先運行Redis服務與下載Redis驅動,以maven工程為例,引入如下jar <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0 ...
  • 這是高級開發者面試時經常被問的問題。實際我們在平時的開發中,經常會遇到的,在用SQLyog等工具創建表時,就有一個引擎項要你去選。如下圖: Mysql的存儲引擎有這麼多種,實際我們在平時用的最多的莫過於InnoDB和MyISAM了。 所有如果面試官問道mysql有哪些存儲引擎,你只需要告訴這兩個常用 ...
  • 大數據技術體系的知識量是比較大的,而且涉及到的內容也具有一定的難度,對於初學者的知識結構還是有一定要求的。通常來說,要想學習大數據技術,需要具有一定的數學和電腦基礎,如果具有一定的統計學基礎會更好一些。 ...
  • 問題概述 "新冠期間"遠程辦公,需要重新搭建一套ClouderaManager(CM)開發環境,一位測試同事發現HBase的RegionServer無法啟動,在CM界面上啟動總是失敗,觀察一下日誌,也沒有什麼明顯的報錯。我就專門看了一下。 排查思路 1. 因為有opentsdb在讀寫Hbase Re ...
  • 創建 test 測試表 CREATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `c1` varchar(10) DEFAULT NULL, `c2` varchar(10) DEFAULT NULL, `c3` varchar(10) ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...