主頁 > 資料庫 > MySQL 日志系統之 redo log 和 binlog

MySQL 日志系統之 redo log 和 binlog

2020-09-24 02:15:30 資料庫

之前我們了解了一條查詢陳述句的執行流程,并介紹了執行程序中涉及的處理模塊,一條查詢陳述句的執行程序一般是經過連接器、分析器、優化器、執行器等功能模塊,最后到達存盤引擎,

那么,一條 SQL 更新陳述句的執行流程又是怎樣的呢?

首先我們創建一個表 user_info,主鍵為 id,創建陳述句如下:

CREATE TABLE `T` (
  `ID` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

插入一條資料:

INSERT INTO T VALUES ('2', '1');

如果要將 ID=2 這一行的 c 的值加 1,SQL 陳述句為:

UPDATE T SET c = c + 1 WHERE ID = 2;

前面介紹過 SQL 陳述句基本的執行鏈路,這里把那張圖拿過來,因為,更新陳述句同樣會走一遍查詢陳述句走的流程,

MySQL基本架構示意圖

  1. 通過連接器,客戶端與 MySQL 建立連接
  2. update 陳述句會把 T 表上的所有查詢快取結果清空
  3. 分析器會通過詞法分析和語法分析識別這是一條更新陳述句
  4. 優化器會決定使用 ID 這個索引(聚簇索引)
  5. 執行器負責具體執行,找到匹配的一行,然后更新
  6. 更新程序中還會涉及 redo log(重做日志)和 binlog(歸檔日志)的操作

其中,這兩種日志默認在資料庫的 data 目錄下,redo log 是 ib_logfile0 格式的,binlog 是 xxx-bin.000001 格式的,

接下來讓我們分別去研究下日志模塊中的 redo log 和 binlog,

日志模塊:redo log

在 MySQL 中,如果每一次的更新操作都需要寫進磁盤,然后磁盤也要找到對應的那條記錄,然后再更新,整個程序 IO 成本、查找成本都很高,為了解決這個問題,MySQL 的設計者就采用了日志(redo log)來提升更新效率,

而日志和磁盤配合的整個程序,其實就是 MySQL 里的 WAL 技術,WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日志,再寫磁盤,

具體來說,當有一條記錄需要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log(redolog buffer)里面,并更新記憶體(buffer pool),這個時候更新就算完成了,同時,InnoDB 引擎會在適當的時候(如系統空閑時),將這個操作記錄更新到磁盤里面(刷臟頁),

redo log 是 InnoDB 存盤引擎層的日志,又稱重做日志檔案,redo log 是回圈寫的,redo log 不是記錄資料頁更新之后的狀態,而是記錄這個頁做了什么改動,

redo log 是固定大小的,比如可以配置為一組 4 個檔案,每個檔案的大小是 1GB,那么日志總共就可以記錄 4GB 的操作,從頭開始寫,寫到末尾就又回到開頭回圈寫,如下圖所示,

redolog回圈寫

圖中展示了一組 4 個檔案的 redo log 日志,checkpoint 是當前要擦除的位置,擦除記錄前需要先把對應的資料落盤(更新記憶體頁,等待刷臟頁),write pos 到 checkpoint 之間的部分可以用來記錄新的操作,如果 write pos 和 checkpoint 相遇,說明 redolog 已滿,這個時候資料庫停止進行資料庫更新陳述句的執行,轉而進行 redo log 日志同步到磁盤中,checkpoint 到 write pos 之間的部分等待落盤(先更新記憶體頁,然后等待刷臟頁),

有了 redo log 日志,那么在資料庫進行例外重啟的時候,可以根據 redo log 日志進行恢復,也就達到了 crash-safe,

redo log 用于保證 crash-safe 能力,innodb_flush_log_at_trx_commit 這個引數設定成 1 的時候,表示每次事務的 redo log 都直接持久化到磁盤,這個引數建議設定成 1,這樣可以保證 MySQL 例外重啟之后資料不丟失,

日志模塊:binlog

MySQL 整體來看,其實就有兩塊:一塊是 Server 層,它主要做的是 MySQL 功能層面的事情;還有一塊是引擎層,負責存盤相關的具體事宜,redo log 是 InnoDB 引擎特有的日志,而 Server 層也有自己的日志,稱為 binlog(歸檔日志),

binlog 屬于邏輯日志,是以二進制的形式記錄的是這個陳述句的原始邏輯,依靠 binlog 是沒有 crash-safe 能力的,

binlog 有兩種模式,statement 格式的話是記 sql 陳述句,row 格式會記錄行的內容,記兩條,更新前和更新后都有,

sync_binlog 這個引數設定成 1 的時候,表示每次事務的 binlog 都持久化到磁盤,這個引數也建議設定成 1,這樣可以保證 MySQL 例外重啟之后 binlog 不丟失,

為什么會有兩份日志呢?

因為最開始 MySQL 里并沒有 InnoDB 引擎,MySQL 自帶的引擎是 MyISAM,但是 MyISAM 沒有 crash-safe 的能力,binlog 日志只能用于歸檔,而 InnoDB 是另一個公司以插件形式引入 MySQL 的,既然只依靠 binlog 是沒有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系統——也就是 redo log 來實作 crash-safe 能力,

redo log 和 binlog 區別:

  1. redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實作的,所有引擎都可以使用,
  2. redo log 是物理日志,記錄的是在某個資料頁上做了什么修改;binlog 是邏輯日志,記錄的是這個陳述句的原始邏輯,
  3. redo log 是回圈寫的,空間固定會用完;binlog 是可以追加寫入的,追加寫是指 binlog 檔案寫到一定大小后會切換到下一個,并不會覆寫以前的日志,

有了對這兩個日志的概念性理解后,再來看執行器和 InnoDB 引擎在執行這個 update 陳述句時的內部流程,

  1. 執行器先找引擎取 ID=2 這一行,ID 是主鍵,引擎直接用樹搜索找到這一行,如果 ID=2 這一行所在的資料頁本來就在記憶體中,就直接回傳給執行器;否則,需要先從磁盤讀入記憶體,然后再回傳,
  2. 執行器拿到引擎給的行資料,把這個值加上 1,比如原來是 N,現在就是 N+1,得到新的一行資料,再呼叫引擎介面寫入這行新資料,
  3. 引擎將這行新資料更新到記憶體(InnoDB Buffer Pool)中,同時將這個更新操作記錄到 redo log 里面,此時 redo log 處于 prepare 狀態,然后告知執行器執行完成了,隨時可以提交事務,
  4. 執行器生成這個操作的 binlog,并把 binlog 寫入磁盤,
  5. 執行器呼叫引擎的提交事務介面,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成,

下圖為 update 陳述句的執行流程圖,圖中灰色框表示是在 InnoDB 內部執行的,綠色框表示是在執行器中執行的,

update陳述句執行流程

其中將 redo log 的寫入拆成了兩個步驟:prepare 和 commit,這就是兩階段提交(2PC),

兩階段提交(2PC)

MySQL 使用兩階段提交主要解決 binlog 和 redo log 的資料一致性的問題,

redo log 和 binlog 都可以用于表示事務的提交狀態,而兩階段提交就是讓這兩個狀態保持邏輯上的一致,下圖為 MySQL 二階段提交簡圖:

MySQL二階段提交圖

兩階段提交原理描述:

  1. InnoDB redo log 寫盤,InnoDB 事務進入 prepare 狀態,
  2. 如果前面 prepare 成功,binlog 寫盤,那么再繼續將事務日志持久化到 binlog,如果持久化成功,那么 InnoDB 事務則進入 commit 狀態(在 redo log 里面寫一個 commit 記錄)

備注: 每個事務 binlog 的末尾,會記錄一個 XID event,標志著事務是否提交成功,也就是說,recovery 程序中,binlog 最后一個 XID event 之后的內容都應該被 purge,

日志相關問題

怎么進行資料恢復?

binlog 會記錄所有的邏輯操作,并且是采用追加寫的形式,當需要恢復到指定的某一秒時,比如今天下午二點發現中午十二點有一次誤刪表,需要找回資料,那你可以這么做:

  • 首先,找到最近的一次全量備份,從這個備份恢復到臨時庫
  • 然后,從備份的時間點開始,將備份的 binlog 依次取出來,重放到中午誤刪表之前的那個時刻,

這樣你的臨時庫就跟誤刪之前的線上庫一樣了,然后你可以把表資料從臨時庫取出來,按需要恢復到線上庫去,

redo log 和 binlog 是怎么關聯起來的?

redo log 和 binlog 有一個共同的資料欄位,叫 XID,崩潰恢復的時候,會按順序掃描 redo log:

  • 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
  • 如果碰到只有 parepare、而沒有 commit 的 redo log,就拿著 XID 去 binlog 找對應的事務,

MySQL 怎么知道 binlog 是完整的?

一個事務的 binlog 是有完整格式的:

  • statement 格式的 binlog,最后會有 COMMIT
  • row 格式的 binlog,最后會有一個 XID event

在 MySQL 5.6.2 版本以后,還引入了 binlog-checksum 引數,用來驗證 binlog 內容的正確性,對于 binlog 日志由于磁盤原因,可能會在日志中間出錯的情況,MySQL 可以通過校驗 checksum 的結果來發現,所以,MySQL 是有辦法驗證事務 binlog 的完整性的,

redo log 一般設定多大?

redo log 太小的話,會導致很快就被寫滿,然后不得不強行刷 redo log,這樣 WAL 機制的能力就發揮不出來了,

如果是幾個 TB 的磁盤的話,直接將 redo log 設定為 4 個檔案,每個檔案 1GB,

資料寫入后的最終落盤,是從 redo log 更新過來的還是從 buffer pool 更新過來的呢?

實際上,redo log 并沒有記錄資料頁的完整資料,所以它并沒有能力自己去更新磁盤資料頁,也就不存在由 redo log 更新過去資料最終落盤的情況,

  1. 資料頁被修改以后,跟磁盤的資料頁不一致,稱為臟頁,最終資料落盤,就是把記憶體中的資料頁寫盤,這個程序與 redo log 毫無關系,
  2. 在崩潰恢復場景中,InnoDB 如果判斷到一個資料頁可能在崩潰恢復的時候丟失了更新,就會將它讀到記憶體,然后讓 redo log 更新記憶體內容,更新完成后,記憶體頁變成臟頁,就回到了第一種情況的狀態,

redo log buffer 是什么?是先修改記憶體,還是先寫 redo log 檔案?

在一個事務的更新程序中,日志是要寫多次的,比如下面這個事務:

begin;
INSERT INTO T1 VALUES ('1', '1');
INSERT INTO T2 VALUES ('1', '1');
commit;

這個事務要往兩個表中插入記錄,插入資料的程序中,生成的日志都得先保存起來,但又不能在還沒 commit 的時候就直接寫到 redo log 檔案里,

因此就需要 redo log buffer 出場了,它就是一塊記憶體,用來先存 redo 日志的,也就是說,在執行第一個 insert 的時候,資料的記憶體被修改了,redo log buffer 也寫入了日志,

但是,真正把日志寫到 redo log 檔案,是在執行 commit 陳述句的時候做的,

以下是我截取的部分 redo log buffer 的源代碼:

/** redo log buffer */
struct log_t{
	char		pad1[CACHE_LINE_SIZE];
	lsn_t		lsn;		
	ulint		buf_free;   // buffer 內剩余空間的起始點的 offset
#ifndef UNIV_HOTBACKUP
	char		pad2[CACHE_LINE_SIZE];
	LogSysMutex	mutex;		
	LogSysMutex	write_mutex;	
	char		pad3[CACHE_LINE_SIZE];
	FlushOrderMutex	log_flush_order_mutex;
#endif /* !UNIV_HOTBACKUP */
	byte*		buf_ptr;    // 隱性的 buffer
	byte*		buf;        // 真正操作的 buffer
	bool		first_in_use;	
	ulint		buf_size;   // buffer大小
	bool		check_flush_or_checkpoint;
	UT_LIST_BASE_NODE_T(log_group_t) log_groups;

#ifndef UNIV_HOTBACKUP
	/** The fields involved in the log buffer flush @{ */
	ulint		buf_next_to_write;
	volatile bool	is_extending;	
	lsn_t		write_lsn;	/*!< last written lsn */
	lsn_t		current_flush_lsn;
	lsn_t		flushed_to_disk_lsn;
	ulint		n_pending_flushes;
	os_event_t	flush_event;	
	ulint		n_log_ios;	
	ulint		n_log_ios_old;	
	time_t		last_printout_time;

	/** Fields involved in checkpoints @{ */
	lsn_t		log_group_capacity; 
	lsn_t		max_modified_age_async;
	lsn_t		max_modified_age_sync;
	lsn_t		max_checkpoint_age_async;
	lsn_t		max_checkpoint_age;
	ib_uint64_t	next_checkpoint_no;
	lsn_t		last_checkpoint_lsn;
	lsn_t		next_checkpoint_lsn;
	mtr_buf_t*	append_on_checkpoint;
	ulint		n_pending_checkpoint_writes;
	rw_lock_t	checkpoint_lock;
#endif /* !UNIV_HOTBACKUP */
	byte*		checkpoint_buf_ptr;
	byte*		checkpoint_buf;	
	/* @} */
};

redo log buffer 本質上只是一個 byte 陣列,但是為了維護這個 buffer 還需要設定很多其他的 meta data,這些 meta data 全部封裝在 log_t 結構體中,

總結

這篇文章主要介紹了 MySQL 里面最重要的兩個日志,即物理日志 redo log(重做日志)和邏輯日志 binlog(歸檔日志),還講解了有與日志相關的一些問題,

另外還介紹了與 MySQL 日志系統密切相關的兩階段提交(2PC),兩階段提交是解決分布式系統的一致性問題常用的一個方案,類似的還有 三階段提交(3PC) 和 PAXOS 演算法,

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/115800.html

標籤:MySQL

上一篇:MyCat教程三:安裝及配置介紹

下一篇:MySQL——基本概念

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more