中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

用一條SQL插入跟更新執行流程以及日志系統原理

發布時間:2021-09-16 15:04:20 來源:億速云 閱讀:137 作者:chen 欄目:大數據

這篇文章主要介紹“用一條SQL插入跟更新執行流程以及日志系統原理”,在日常操作中,相信很多人在用一條SQL插入跟更新執行流程以及日志系統原理問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”用一條SQL插入跟更新執行流程以及日志系統原理”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

用一條SQL插入跟更新執行流程以及日志系統原理

查詢如何執行

平時我們使用數據庫,看到的通常都是一個整體。比如,你有個最簡單的表,表里只有一個ID字段,在執行下面這個查詢語句時:

mysql> select * from T where ID=10;

我們看到的只是輸入一條語句,返回一個結果,卻不知道這條語句在MySQL內部的執行過程。如何把這個過程拆解下來看后臺部分呢?下面我給出的是MySQL的基本架構示意圖,從中你可以清楚地看到SQL語句在MySQL的各個功能模塊中的執行過程。
用一條SQL插入跟更新執行流程以及日志系統原理
大體來說,MySQL可以分為Server層存儲引擎層兩部分。

Server層:包括連接器查詢緩存分析器優化器執行器等,涵蓋MySQL的大多數核心服務功能,以及所有的內置函數(如日期、時間、數學和加密函數等),所有跨存儲引擎的功能都在這一層實現,比如存儲過程觸發器視圖等。

存儲引擎層:負責數據的存儲和提取。其架構模式是插件式的,支持InnoDB、MyISAM、Memory等多個存儲引擎。現在最常用的存儲引擎是InnoDB,它從MySQL 5.5.5版本開始成為了默認存儲引擎。

也就是說,你執行create table建表的時候,如果不指定引擎類型,默認使用的就是InnoDB。不過,你也可以通過指定存儲引擎的類型來選擇別的引擎,比如在create table語句中使用engine=memory, 來指定使用內存引擎創建表。不同存儲引擎的表數據存取方式不同,支持的功能也不同。

從圖中不難看出,不同的存儲引擎共用一個Server層,也就是從連接器到執行器的部分。你可以先對每個組件的名字有個印象,接下來我會結合開頭提到的那條SQL語句,帶你走一遍整個執行流程,依次看下每個組件的作用。

連接器

第一步,你會先連接到這個數據庫上,這時候接待你的就是連接器。連接器負責跟客戶端建立連接、獲取權限、維持和管理連接。連接命令一般是這么寫的:

mysql -h 127.0.0.1 -P 3306 -u root -p
root

輸完命令之后,你就需要在交互對話里面輸入密碼。雖然密碼也可以直接跟在-p后面寫在命令行中,但這樣可能會導致你的密碼泄露。如果你連的是生產服務器,強烈建議你不要這么做。

連接命令中的mysql是客戶端工具,用來跟服務端建立連接。在完成經典的TCP握手后,連接器就要開始認證你的身份,這個時候用的就是你輸入的用戶名和密碼。

  • 如果用戶名或密碼不對,你就會收到一個Access denied for user的錯誤,然后客戶端程序結束執行。

  • 如果用戶名密碼認證通過,連接器會到權限表里面查出你擁有的權限。之后,這個連接里面的權限判斷邏輯,都將依賴于此時讀到的權限。

這就意味著,一個用戶成功建立連接后,即使你用管理員賬號對這個用戶的權限做了修改,也不會影響已經存在連接的權限。修改完成后,只有再新建的連接才會使用新的權限設置。

連接完成后,如果你沒有后續的動作,這個連接就處于空閑狀態,你可以在show processlist命令中看到它。文本中這個圖是show processlist的結果,其中的Command列顯示為Sleep的這一行,就表示現在系統里面有一個空閑連接。
用一條SQL插入跟更新執行流程以及日志系統原理

客戶端如果太長時間沒動靜,連接器就會自動將它斷開。這個時間是由參數wait_timeout控制的,默認值是8小時。

如果在連接被斷開之后,客戶端再次發送請求的話,就會收到一個錯誤提醒: Lost connection to MySQL server during query。這時候如果你要繼續,就需要重連,然后再執行請求了。

數據庫里面,長連接是指連接成功后,如果客戶端持續有請求,則一直使用同一個連接。短連接則是指每次執行完很少的幾次查詢就斷開連接,下次查詢再重新建立一個。

建立連接的過程通常是比較復雜的,所以我建議你在使用中要盡量減少建立連接的動作,也就是盡量使用長連接。

但是全部使用長連接后,你可能會發現,有些時候MySQL占用內存漲得特別快,這是因為MySQL在執行過程中臨時使用的內存是管理在連接對象里面的。這些資源會在連接斷開的時候才釋放。所以如果長連接累積下來,可能導致內存占用太大,被系統強行殺掉(OOM),從現象看就是MySQL異常重啟了。

怎么解決這個問題呢?你可以考慮以下兩種方案。

  1. 定期斷開長連接。使用一段時間,或者程序里面判斷執行過一個占用內存的大查詢后,斷開連接,之后要查詢再重連。

  2. 如果你用的是MySQL 5.7或更新版本,可以在每次執行一個比較大的操作后,通過執行 mysql_reset_connection來重新初始化連接資源。這個過程不需要重連和重新做權限驗證,但是會將連接恢復到剛剛創建完時的狀態。

查詢緩存

連接建立完成后,你就可以執行select語句了。執行邏輯就會來到第二步:查詢緩存

MySQL拿到一個查詢請求后,會先到查詢緩存看看,之前是不是執行過這條語句。之前執行過的語句及其結果可能會以key-value對的形式,被直接緩存在內存中。key是查詢的語句value是查詢的結果。如果你的查詢能夠直接在這個緩存中找到key,那么這個value就會被直接返回給客戶端。

如果語句不在查詢緩存中,就會繼續后面的執行階段。執行完成后,執行結果會被存入查詢緩存中。你可以看到,如果查詢命中緩存,MySQL不需要執行后面的復雜操作,就可以直接返回結果,這個效率會很高。

但是大多數情況下我會建議你不要使用查詢緩存,為什么呢?因為查詢緩存往往弊大于利

查詢緩存的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢緩存都會被清空。因此很可能你費勁地把結果存起來,還沒使用呢,就被一個更新全清空了。對于更新壓力大的數據庫來說,查詢緩存的命中率會非常低。除非你的業務就是有一張靜態表,很長時間才會更新一次。比如,一個系統配置表,那這張表上的查詢才適合使用查詢緩存。

好在MySQL也提供了這種按需使用的方式。你可以將參數query_cache_type設置成DEMAND,這樣對于默認的SQL語句都不使用查詢緩存。而對于你確定要使用查詢緩存的語句,可以用SQL_CACHE顯式指定,像下面這個語句一樣:

mysql> select SQL_CACHE * from T where ID=10;

PS : MySQL 8.0版本直接將查詢緩存的整塊功能刪掉了,也就是說8.0開始徹底沒有這個功能了。

分析器

如果沒有命中查詢緩存,就要開始真正執行語句了。首先,MySQL需要知道你要做什么,因此需要對SQL語句做解析

分析器先會做詞法分析。你輸入的是由多個字符串和空格組成的一條SQL語句,MySQL需要識別出里面的字符串分別是什么,代表什么。

MySQL從你輸入的select這個關鍵字識別出來,這是一個查詢語句。它也要把字符串“T”識別成“表名T”,把字符串“ID”識別成“列ID”。

做完了這些識別以后,就要做“語法分析”。根據詞法分析的結果,語法分析器會根據語法規則,判斷你輸入的這個SQL語句是否滿足MySQL語法。

如果你的語句不對,就會收到You have an error in your SQL syntax的錯誤提醒,比如下面這個語句select少打了開頭的字母“s”。

mysql> elect * from t where ID=1;ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that correspondsto your MySQL server version for the right syntax to use near 'elect * from t where ID=1' at line 1

一般語法錯誤會提示第一個出現錯誤的位置,所以你要關注的是緊接use near的內容。

優化器

經過了分析器,MySQL就知道你要做什么了。在開始執行之前,還要先經過優化器的處理。

優化器是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連接順序。比如你執行下面這樣的語句,這個語句是執行兩個表的join:

mysql> select * from t1 join t2 using(ID)  where t1.c=10 and t2.d=20;
  • 既可以先從表t1里面取出c=10的記錄的ID值,再根據ID值關聯到表t2,再判斷t2里面d的值是否等于20。

  • 也可以先從表t2里面取出d=20的記錄的ID值,再根據ID值關聯到t1,再判斷t1里面c的值是否等于10。

這兩種執行方法的邏輯結果是一樣的,但是執行的效率會有不同,而優化器的作用就是決定選擇使用哪一個方案

優化器階段完成后,這個語句的執行方案就確定下來了,然后進入執行器階段。如果你還有一些疑問,比如優化器是怎么選擇索引的,有沒有可能選擇錯等等,沒關系,我會在后面的文章中單獨展開說明優化器的內容。

執行器

MySQL通過分析器知道了你要做什么,通過優化器知道了該怎么做,于是就進入了執行器階段,開始執行語句。

開始執行的時候,要先判斷一下你對這個表T有沒有執行查詢的權限,如果沒有,就會返回沒有權限的錯誤,如下所示(在工程實現上,如果命中查詢緩存,會在查詢緩存放回結果的時候,做權限驗證。查詢也會在優化器之前調用precheck驗證權限)。

mysql> select * from T where ID=10;ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

如果有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。

比如我們這個例子中的表T中,ID字段沒有索引,那么執行器的執行流程是這樣的:

  1. 調用InnoDB引擎接口取這個表的第一行,判斷ID值是不是10,如果不是則跳過,如果是則將這行存在結果集中;

  2. 調用引擎接口取下一行,重復相同的判斷邏輯,直到取到這個表的最后一行。

  3. 執行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結果集返回給客戶端。

至此,這個語句就執行完成了。

對于有索引的表,執行的邏輯也差不多。第一次調用的是取滿足條件的第一行這個接口,之后循環取滿足條件的下一行這個接口,這些接口都是引擎中已經定義好的。

你會在數據庫的慢查詢日志中看到一個rows_examined的字段,表示這個語句執行過程中掃描了多少行。這個值就是在執行器每次調用引擎獲取數據行的時候累加的。

在有些場景下,執行器調用一次,在引擎內部則掃描了多行,因此引擎掃描行數跟rows_examined并不是完全相同的。

PS:如果表T中沒有字段k,而你執行了這個語句 select * from T where k=1, 那肯定是會報“不存在這個列”的錯誤: “Unknown column ‘k’ in ‘where clause’”。該錯誤會在分析器爆出。

總結:

一條查詢語句的執行過程一般是經過連接器、分析器、優化器、執行器等功能模塊,最后到達存儲引擎

更新如何執行

一條更新語句的執行流程又是怎樣的呢?之前你可能經常聽DBA同事說,MySQL可以恢復到半個月內任意一秒的狀態,驚嘆的同時,你是不是心中也會不免會好奇,這是怎樣做到的呢?

我們還是從一個表的一條更新語句說起,下面是這個表的創建語句,這個表有一個主鍵ID和一個整型字段c:

mysql> create table T(ID int primary key, c int);

如果要將ID=2這一行的值加1,SQL語句就會這么寫:

mysql> update T set c=c+1 where ID=2;

前面我有跟你介紹過SQL語句基本的執行鏈路,這里我再把那張圖拿過來,你也可以先簡單看看這個圖回顧下。首先,可以確定的說,查詢語句的那一套流程,更新語句也是同樣會走一遍。
用一條SQL插入跟更新執行流程以及日志系統原理
你執行語句前要先連接數據庫,這是連接器的工作。

前面我們說過,在一個表上有更新的時候,跟這個表有關的查詢緩存會失效,所以這條語句就會把表T上所有緩存結果都清空。這也就是我們一般不建議使用查詢緩存的原因。

接下來,分析器會通過詞法和語法解析知道這是一條更新語句。優化器決定要使用ID這個索引。然后,執行器負責具體執行,找到這一行,然后更新。

與查詢流程不一樣的是,更新流程還涉及兩個重要的日志模塊,它們正是我們今天要討論的主角:redo log(重做日志)和 binlog(歸檔日志)。binlog內容查看,如果接觸MySQL,那這兩個詞肯定是繞不過的,我后面的內容里也會不斷地和你強調。不過話說回來,redo log和binlog在設計上有很多有意思的地方,這些設計思路也可以用到你自己的程序里。

重要的日志模塊:redo log

不知道你還記不記得《孔乙己》這篇文章,酒店掌柜有一個粉板,專門用來記錄客人的賒賬記錄。如果賒賬的人不多,那么他可以把顧客名和賬目寫在板上。但如果賒賬的人多了,粉板總會有記不下的時候,這個時候掌柜一定還有一個專門記錄賒賬的賬本

如果有人要賒賬或者還賬的話,掌柜一般有兩種做法:

  • 一種做法是直接把賬本翻出來,把這次賒的賬加上去或者扣除掉;

  • 另一種做法是先在粉板上記下這次的賬,等打烊以后再把賬本翻出來核算。

在生意紅火柜臺很忙時,掌柜一定會選擇后者,因為前者操作實在是太麻煩了。首先,你得找到這個人的賒賬總額那條記錄。你想想,密密麻麻幾十頁,掌柜要找到那個名字,可能還得帶上老花鏡慢慢找,找到之后再拿出算盤計算,最后再將結果寫回到賬本上。

這整個過程想想都麻煩。相比之下,還是先在粉板上記一下方便。你想想,如果掌柜沒有粉板的幫助,每次記賬都得翻賬本,效率是不是低得讓人難以忍受?

同樣,在MySQL里也有這個問題,如果每一次的更新操作都需要寫進磁盤,然后磁盤也要找到對應的那條記錄,然后再更新,整個過程IO成本、查找成本都很高。為了解決這個問題,MySQL的設計者就用了類似酒店掌柜粉板的思路來提升更新效率。

而粉板和賬本配合的整個過程,其實就是MySQL里經常說到的WAL技術,WAL的全稱是Write-Ahead Logging,它的關鍵點就是先寫日志,再寫磁盤,也就是先寫粉板,等不忙的時候再寫賬本。

具體來說,當有一條記錄需要更新的時候,InnoDB引擎就會先把記錄寫到redo log(粉板)里面,并更新內存,這個時候更新就算完成了。同時,InnoDB引擎會在適當的時候,將這個操作記錄更新到磁盤里面,而這個更新往往是在系統比較空閑的時候做,這就像打烊以后掌柜做的事。

如果今天賒賬的不多,掌柜可以等打烊后再整理。但如果某天賒賬的特別多,粉板寫滿了,又怎么辦呢?這個時候掌柜只好放下手中的活兒,把粉板中的一部分賒賬記錄更新到賬本中,然后把這些記錄從粉板上擦掉,為記新賬騰出空間。

與此類似,InnoDB的redo log是固定大小的,比如可以配置為一組4個文件,每個文件的大小是1GB,那么這塊粉板總共就可以記錄4GB的操作。從頭開始寫,寫到末尾就又回到開頭循環寫,如下面這個圖所示。
用一條SQL插入跟更新執行流程以及日志系統原理
write pos :是當前記錄的位置,一邊寫一邊后移,寫到第3號文件末尾后就回到0號文件開頭。
checkpoint:是當前要擦除的位置,也是往后推移并且循環的,擦除記錄前要把記錄更新到數據文件。

write pos和checkpoint之間的是粉板上還空著的部分,可以用來記錄新的操作。如果write pos追上checkpoint,表示粉板滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint推進一下。

有了redo log,InnoDB就可以保證即使數據庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為crash-safe。記錄這個頁 做了什么改動

要理解crash-safe這個概念,可以想想我們前面賒賬記錄的例子。只要賒賬記錄記在了粉板上或寫在了賬本上,之后即使掌柜忘記了,比如突然停業幾天,恢復生意后依然可以通過賬本和粉板上的數據明確賒賬賬目。

重要的日志模塊:binlog

前面我們講過,MySQL整體來看,其實就有兩塊:一塊是Server層,它主要做的是MySQL功能層面的事情;還有一塊是引擎層,負責存儲相關的具體事宜。上面我們聊到的粉板redo log是InnoDB引擎特有的日志,而Server層也有自己的日志,稱為binlog(歸檔日志)。 Binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新后都有。

我想你肯定會問,為什么會有兩份日志呢?
因為最開始MySQL里并沒有InnoDB引擎。MySQL自帶的引擎是MyISAM,但是MyISAM沒有crash-safe的能力,binlog日志只能用于歸檔。而InnoDB是另一個公司以插件形式引入MySQL的,既然只依靠binlog是沒有crash-safe能力的,所以InnoDB使用另外一套日志系統——也就是redo log來實現crash-safe能力。

這兩種日志有以下三點不同

  1. redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,所有引擎都可以使用。

  2. redo log是物理日志,記錄的是在某個數據頁上做了什么修改;binlog是邏輯日志,記錄的是這個語句的原始邏輯,比如給ID=2這一行的c字段加1

  3. redo log是循環寫的,空間固定會用完;binlog是可以追加寫入的。追加寫是指binlog文件寫到一定大小后會切換到下一個,并不會覆蓋以前的日志。

有了對這兩個日志的概念性理解,我們再來看執行器和InnoDB引擎在執行這個簡單的update語句時的內部流程。

  1. 執行器先找引擎取ID=2這一行。ID是主鍵,引擎直接用樹搜索找到這一行。如果ID=2這一行所在的數據頁本來就在內存中,就直接返回給執行器;否則,需要先從磁盤讀入內存,然后再返回。

  2. 執行器拿到引擎給的行數據,把這個值加上1,比如原來是N,現在就是N+1,得到新的一行數據,再調用引擎接口寫入這行新數據。

  3. 引擎將這行新數據更新到內存中,同時將這個更新操作記錄到redo log里面,此時redo log處于prepare狀態。然后告知執行器執行完成了,隨時可以提交事務。

  4. 執行器生成這個操作的binlog,并把binlog寫入磁盤。

  5. 執行器調用引擎的提交事務接口,引擎把剛剛寫入的redo log改成提交(commit)狀態,更新完成。

這里我給出這個update語句的執行流程圖,圖中淺色框表示是在InnoDB內部執行的深色框表示是在執行器中執行的

用一條SQL插入跟更新執行流程以及日志系統原理
你可能注意到了,最后三步看上去有點,將redo log的寫入拆成了兩個步驟:prepare和commit,這就是兩階段提交

兩階段提交
為什么必須有兩階段提交呢?這是為了讓兩份日志之間的邏輯一致。要說明這個問題,我們得從文章開頭的那個問題說起:怎樣讓數據庫恢復到半個月內任意一秒的狀態?

前面我們說過了,binlog會記錄所有的邏輯操作,并且是采用追加寫的形式。如果你的DBA承諾說半個月內可以恢復,那么備份系統中一定會保存最近半個月的所有binlog,同時系統會定期做整庫備份。這里的定期取決于系統的重要性,可以是一天一備,也可以是一周一備。

當需要恢復到指定的某一秒時,比如某天下午兩點發現中午十二點有一次誤刪表,需要找回數據,那你可以這么做:

  • 首先,找到最近的一次全量備份,如果你運氣好,可能就是昨天晚上的一個備份,從這個備份恢復到臨時庫;

  • 然后,從備份的時間點開始,將備份的binlog依次取出來,重放到中午誤刪表之前的那個時刻。

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

好了,說完了數據恢復過程,我們回來說說,為什么日志需要兩階段提交。這里不妨用反證法來進行解釋。

由于redo log和binlog是兩個獨立的邏輯,如果不用兩階段提交,要么就是先寫完redo log再寫binlog,或者采用反過來的順序。我們看看這兩種方式會有什么問題。

仍然用前面的update語句來做例子。假設當前ID=2的行,字段c的值是0,再假設執行update語句過程中在寫完第一個日志后,第二個日志還沒有寫完期間發生了crash,會出現什么情況呢?

  1. 先寫redo log后寫binlog。假設在redo log寫完,binlog還沒有寫完的時候,MySQL進程異常重啟。由于我們前面說過的,redo log寫完之后,系統即使崩潰,仍然能夠把數據恢復回來,所以恢復后這一行c的值是1。但是由于binlog沒寫完就crash了,這時候binlog里面就沒有記錄這個語句。因此,之后備份日志的時候,存起來的binlog里面就沒有這條語句。然后你會發現,如果需要用這個binlog來恢復臨時庫的話,由于這個語句的binlog丟失,這個臨時庫就會少了這一次更新,恢復出來的這一行c的值就是0,與原庫的值不同。

  2. 先寫binlog后寫redo log。如果在binlog寫完之后crash,由于redo log還沒寫,崩潰恢復以后這個事務無效,所以這一行c的值是0。但是binlog里面已經記錄了把c從0改成1這個日志。所以,在之后用binlog來恢復的時候就多了一個事務出來,恢復出來的這一行c的值就是1,與原庫的值不同。

可以看到,如果不使用兩階段提交,那么數據庫的狀態就有可能和用它的日志恢復出來的庫的狀態不一致。

1 prepare階段 --> 2 寫binlog --> 3 commit
當在2之前崩潰時
重啟恢復:后發現沒有commit,回滾。備份恢復:沒有binlog 。一致
當在3之前崩潰
重啟恢復:雖沒有commit,但滿足prepare和binlog完整,所以重啟后會自動commit。備份:有binlog. 一致

你可能會說,這個概率是不是很低,平時也沒有什么動不動就需要恢復臨時庫的場景呀?

其實不是的,不只是誤操作后需要用這個過程來恢復數據。當你需要擴容的時候,也就是需要再多搭建一些備庫來增加系統的讀能力的時候,現在常見的做法也是用全量備份加上應用binlog來實現的,這個不一致就會導致你的線上出現主從數據庫不一致的情況。

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

總結

介紹了MySQL里面最重要的兩個日志,即物理日志redo log邏輯日志binlog

redo log用于保證crash-safe能力。innodb_flush_log_at_trx_commit這個參數設置成1的時候,表示每次事務的redo log都直接持久化到磁盤。這個參數我建議你設置成1,這樣可以保證MySQL異常重啟之后數據不丟失。

binlog還不能去掉。一個原因是,redolog只有InnoDB有,別的引擎沒有。另一個原因是,redolog是循環寫的,不持久保存,binlog的歸檔這個功能,redolog是不具備的。

sync_binlog這個參數設置成1的時候,表示每次事務的binlog都持久化到磁盤。這個參數我也建議你設置成1,這樣可以保證MySQL異常重啟之后binlog不丟失。

介紹了與MySQL日志系統密切相關的兩階段提交。兩階段提交是跨系統維持數據邏輯一致性時常用的一個方案,即使你不做數據庫內核開發,日常開發中也有可能會用到。

一天一備跟一周一備的對比
在一天一備的模式里,最壞情況下需要應用一天的binlog。比如,你每天0點做一次全量備份,而要恢復出一個到昨天晚上23點的備份。一周一備最壞情況就要應用一周的binlog了。系統的對應指標就是 提到的RTO(恢復目標時間)。當然這個是有成本的,因為更頻繁全量備份需要消耗更多存儲空間,所以這個RTO是成本換來的,就需要你根據業務重要性來評估了。

到此,關于“用一條SQL插入跟更新執行流程以及日志系統原理”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

sql
AI

土默特右旗| 吕梁市| 平舆县| 海原县| 馆陶县| 鄢陵县| 呼玛县| 白朗县| 扎赉特旗| 松滋市| 阜城县| 潮州市| 班戈县| 松原市| 徐州市| 进贤县| 神农架林区| 贡觉县| 大新县| 盈江县| 年辖:市辖区| 鲁山县| 黄浦区| 龙井市| 永嘉县| 宣武区| 额敏县| 桃江县| 开原市| 油尖旺区| 衡阳县| 彭泽县| 满洲里市| 曲松县| 峡江县| 南木林县| 四会市| 浑源县| 汝阳县| 阆中市| 贺兰县|