MySQL 入門(1):查詢和更新的內部實現

2021-01-07 IT168

  摘要

  在MySQL中,簡單的CURD是很容易上手的。

  但是,理解CURD的背後發生了什麼,卻是一件特別困難的事情。

  在這一篇的內容中,我將簡單介紹一下MySQL的架構是什麼樣的,分別有什麼樣的功能。然後再簡單介紹一下在我們執行簡單的查詢和更新指令的時候,背後到底發生了什麼。

  1 MySQL結構

  在這一小節中,我會先簡單的介紹一下各個部分的功能。隨後,將在第二、第三節中詳細介紹。

  先來看一張圖:

  簡單的來講一講:

  1.1 連接器

  連接器負責跟客戶端建立連接、獲取權限、維持和管理連接。

  在客戶端輸入了帳號密碼之後,如果此時帳號密碼驗證通過,連接器將會和客戶端建立一條TCP連接。這個連接將會在長時間無請求後被連接器自動斷開(默認是8小時)。

  此外,在連接建立後,如果管理員修改了這個帳戶的權限,也不會對當前的連接有任何的影響,當前連接所擁有的權限還是之前未修改前的權限。

  1.2 分析器

  分析器有兩個功能:詞法分析、語法分析。

  對於一個 SQL 語句,分析器首先進行詞法分析,對sql語句進行拆分,識別出各個字符串代表的含義。

  然後就是語法分析,分析器根據定義的語法規則判斷sql語句是否滿足 MySQL 語法。

  所以,如果我們看到You have an error in your SQL syntax這麼一段話,就可以知道這個錯誤是由分析器返回的。

  1.3 緩存

  這裡的緩存會保存之前的sql查詢語句和結果。你可以理解為這是一個map:key是查詢的sql語句,value是查詢的結果。

  並且,在官方手冊中,有這麼一句話:

  Queries must be exactly the same (byte for byte) to be seen as identical.

  也就是說,查詢語句必須得和之前完全一致,每一個字節都一樣,大小寫敏感,甚至不能多一個空格。

  但是,這裡的緩存是非常容易失效的。為了保證查詢的冪等性,當某一張表有數據更新後,這個表的緩存也將失效。

  所以,對於更新壓力大的資料庫來說,查詢緩存的命中率會非常低。建議只在讀多寫少的資料庫開啟緩存。

  但是,在MySQL8.0以後,已經刪除了緩存功能。

  1.4 優化器

  查詢優化器的任務是發現執行SQL查詢的最佳方案。大多數查詢優化器,包括MySQL的查詢優化器,總或多或少地在所有可能的查詢評估方案中搜索最佳方案。

  簡單來說,優化器就是尋找一個最快能夠查詢到數據的策略。

  1.5 執行器

  在通過了上述的過程後,Server層已經解析出了需要處理的數據是什麼,應該怎麼做。

  隨後會進行權限的判斷,如果當前的連接擁有目標表的權限,則會調用存儲引擎開放的接口,處理需要處理的數據。

  到這裡MySQL的基本架構就講完了。但是因為我省略了大部分的細節,只講了這麼一小部分,可能會導致你的疑問增加了。

  不過沒關係,我們接著往下看,用實際的例子來解釋這裡的每一部分,可能會更容易理解。

  2 查詢

  我們從這麼一條sql講起:

  select * from T where ID = 1;

  2.1 查找緩存

  首先,會調用分析器,進行詞法分析。

  此時,詞法分析發現這條sql語句是以select開頭的,並且在這條語句中沒有任何不確定的數據,所以會去緩存中查找是否保存了這條語句的結果作為緩存。

  但是關於上面的說法,有我個人推測的部分。我沒有在官方文檔中找到MySQL是何時查找緩存的,到底是在分析器之前還是分析器之後。

  但是在《高性能MySQL》這本書中提到了 「通過檢查sql語句是否以select」 開頭,所以我推測查找緩存是需要先經過簡單的詞法分析的。

  只有經過了詞法分析分析,MySQL才能知道這段語句是否是select語句,也能知道這條語句中有無一些不確定的數據(如當前時間等)。

  2.2 緩存未命中

  此時,如果緩存未命中,則繼續使用分析器進行語法分析。然後,根據這顆語法樹,來判斷這條sql語句是否符合MySQL語法的。

  注意,關於詞法分析和語法分析,如果你感興趣的話,可以看一看編譯原理相關的內容。

  然後來到了優化器。優化器就是在有多種查找方式的時候,自行選擇一個更好的查詢方式。

  例如,如果此時sql語句裡面有多個索引,會選擇一個合適的索引;又或者在關聯查詢的時候,選擇一個更好的方案。

  這一部分的內容我想在以後的文章中介紹,這裡我想重點講講下面的內容,關於MySQL中數據的結構。

  2.3 數據的結構

  在我們利用最後一步的執行器去進行數據的讀取和寫入的時候,其實是調用了MySQL中的存儲引擎進行數據的讀寫和寫入。

  回到我們的例子,我們要找的是在表T中ID為1的數據。但是,存儲引擎並不會返回這麼一條具體的數據,他返回的是包含這條數據的數據頁。

  這裡我補充一點點知識:

  資料庫使用頁管理,和我們作業系統是一樣的。因為我們現在的機器是馮諾依曼結構的,這是是一種將程序指令存儲器和數據存儲器合併在一起的存儲器結構。

  在這種結構中,具有一個特性,叫局部性原理。

  時間局部性(Temporal Locality):如果一個信息項正在被訪問,那麼在近期它很可能還會被再次訪問。程序循環、堆棧等是產生時間局部性的原因。

  空間局部性(Spatial Locality):在最近的將來將用到的信息很可能與正在使用的信息在空間地址上是臨近的。

  順序局部性(Order Locality):在典型程序中,除轉移類指令外,大部分指令是順序進行的。順序執行和非順序執行的比例大致是5:1。此外,對大型數組訪問也是順序的。指令的順序執行、數組的連續存放等是產生順序局部性的原因。

  簡單的來解釋就是如果一行數據被讀取了或者一條指令被執行了,那麼很大概率接下來CPU會繼續讀取或執行這個地址或者這個地址後面的數據和指令。

  在MySQL中也是一樣的,如果一次性讀取一個頁,那麼可能在接下來的讀寫中所操作的數據也在這個數據頁內,這樣可以使得磁碟IO的次數更少。

  回到我們剛剛說的內容,至於引擎是如何找到這個頁的,我想在後面索引相關的文章中再詳細解釋。這裡我們先簡單的理解為引擎能夠快速的找到這一行數據所在的頁,然後這一頁返回給執行器。

  此時,這一頁數據還會被保存在內存中。在之後還需要用到這些數據的時候,將會直接在內存中進行處理,並且MySQL的內存空間中可以存放很多個這樣的數據頁。也就是說,這個時候無論是查找還是修改,都可以在內存中進行,而不需要每次都進行磁碟IO。

  最後,會在合適的時候將這一頁數據寫回磁碟。至於是在什麼時候如何寫回磁碟的,我們接著往下看。

  3 更新

  在說完了如何查找數據之後,我們已經知道了一行數據是如何以頁的形式保存在內存中了。我們現在要解決的問題是:

  update語句是如何執行

  如何將執行後的新數據持久化在磁碟中

  這是一個很有意思的問題,我們來假設兩種情境:

  假設MySQL在更新之後只更新內存中的數據就返回,然後再某一時刻進行IO將數據頁持久化。這樣所有操作都是在內存中,可以想像此時的MySQL性能是特別高的。但是,如果在更新完內存又還沒有進行持久化的這段時間,MySQL宕機了,那麼我們的數據就丟失了。

  再來看另外一種情況:每次MySQL將內存中的頁更新好後,立刻進行IO,只有數據落盤後才返回。此時我們可以保證數據一定是正確的。但是,每一次的操作,都要進行IO,此時MySQL的效率變得非常低。

  所以我們來看看MySQL是如何做到保證性能的情況下,還保證數據不丟的。

  現在回到這條語句:

  update T set a = a + 1 where ID = 0;

  假設這條sql語句是正確的,存在名為ID,a的列在表T中,且存在ID為0的數據。

  此時經過連接器,分析器,分析器發現這是一條update語句,於是繼續語法分析,優化器,執行器。執行器判斷有權限,然後開表,引擎找到了包含了ID為0這行數據的數據頁,將這一頁數據保存在內存中。

  你可以發現,update語句,同樣也走了這麼一遍流程。

  然後重點來了,我們要介紹一下MySQL是如何保證數據一致性的。

  3.1 重做日誌

  這裡要介紹一個很重要的日誌模塊,稱為rodo log(重做日誌)。

  注意,重做日誌是InnoDB引擎特有的。

  重做日誌在更新數據的時候,會記錄在哪個數據頁更新了什麼數據,並且只要成功的在重做日誌記錄了這次更新,不需要將內存中的數據頁寫回磁碟,就可以認為這次更新已經完成了。

  MySQL裡有一個名詞,叫WAL技術,WAL的全稱是Write-Ahead-Logging,它的關鍵點就是先寫日誌,再寫磁碟,也就是說只要保證了日誌的落盤,數據就一定正確。此時只要保存了日誌,就算此時MySQL宕機了,沒有將數據頁寫回磁碟,也可以在之後利用日誌進行恢復。

  但是,InnoDB的redo log是固定大小的,比如可以配置為一組4個文件,每個文件的大小是1GB。固定大小也就造成了一個問題,redo log是會被寫滿的。

    所以,InnoDB採取了循環寫的方式。注意看,這裡有兩個指針。write_pos表示當前寫的位置,只要有記錄更新了,write_pos就會往後移動。而check_point表示檢查點,只要InnoDB將check_point指向的修改記錄更新到了磁碟中,check_point將會往後移動。

  換句話說,拿我們剛剛的update T set a = a + 1 where ID = 0;舉例,如果我們把這一行數據所在的內存頁更新好了,並且寫入了rodo log中,此時將返回修改成功的提示。然後在rodo log中表現為記錄了在某一個內存頁的更新記錄。

  注意,此時在磁碟中,數據a未改變,在內存中,a改為了a+1,在rodo log中記錄了這個內存頁的更新記錄,write_pos往後移動。

  此時,如果要把check_point往後移,那麼他就應該把記錄中這個內存頁的更新持久化到磁碟中,也就是說要把a+1寫回磁碟,此時無論是磁碟還是內存,a的數據都是a+1。只有成功的寫回了磁碟,check_point才可以往後移動。這個設計,使得rodo log是可以無限重複使用的。

  那麼問題來了,我們現在只是知道了write_pos會在數據更新之後往後移動,那麼check_point會在什麼時候移動呢?

  這裡涉及到了innodb_io_capacity這個參數,這個參數會告訴InnoDB你的磁碟讀寫速度怎麼樣,然後由他來控制check_point的移動。至於如何調優,我想在以後的文章中來介紹,在本文你就理解為,他會按照一定的速度,不斷推進。

  然後問題又來了,如果此時資料庫有大量的更新操作,而check_point推進的速度又是恆定的,那麼write_pos不斷往前推進,就一定會寫滿。這種情況是InnoDB要儘量避免的。因為出現這種情況的時候,整個系統就不能再接受更新了,所有的更新都會被堵住。如果你從監控上看,這時候更新數會跌為0。至於如何避免這種情況,我想等到調優的時候再來聊,這裡我們只是知道會有這麼一種情況。

  除此之外還有一種情況我想聊一聊,同樣是大量的更新操作。我們在前面已經提到過了,所有的操作都會在內存中完成,也就是說如果此時我要操作的數據,他們分布到了不同的數據頁中,那麼此時內存中就存儲了非常多的數據頁。這個時候,內存可能不足了。

  我們這裡補充一個概念,乾淨頁和髒頁。乾淨頁指的是從磁碟讀到內存中,沒有被修改過,你可以理解為只被查詢而沒有被更新過的數據頁。而髒頁是和磁碟中數據不一樣的數據頁,他被修改過。如果此時有大量的查詢或更新操作,那麼就需要有大量的內存空間,而此時內存空間已經有各種各樣的數據頁了。那麼我們應該怎麼辦呢?

  如果還有空閒空間,則直接將需要的數據頁讀取並存到空間空間內。

  如果沒有空閒空間了,則淘汰最近最少使用的乾淨頁,也就是說把這個乾淨頁的空間給用了。

  如果連乾淨頁也沒有了,那麼需要淘汰最近最少使用的髒頁。要怎麼淘汰呢,把髒頁寫回磁碟,也就是說更新髒頁的數據,使他變成了乾淨頁。

  然後問題又雙叕來了,如果此時我們因為內存空間不足而將這個髒頁寫回了磁碟,但是對這個髒頁的更新卻記錄在了redo log的不同位置,那麼在redo log需要更新這個頁的時候,怎麼辦呢?我們需不需要在刷新髒頁的時候,在redo log中也把對應的記錄刪掉或者怎麼樣呢?

  這個問題我希望你能思考一下,如果有了這個疑問我想你就理解了上面我說的關於redo log和髒頁的問題了。答案是在更新髒頁的時候,是不需要修改redo log的。redo log在check_point往前推進的時候,如果發現這個頁已經被刷回磁碟了,將會跳過這條記錄。

  3.2 歸檔日誌

  說了這麼多重做日誌,我們再來聊聊歸檔日誌。

  有幾個原因,redo log是循環使用的,也就是說新數據一定會覆蓋舊數據,我們沒辦法拿他來恢復太長時間的記錄。

  第二個原因是因為redo log是InnoDB引擎特有的,在別的引擎中,就沒有重做日誌了。

  所以在這裡我們聊聊引擎層必有的歸檔日誌binlog。

  歸檔日誌是追加寫的,在一個文件寫滿後就會切換到下一個文件繼續寫,會記錄每一條語句更改了什麼內容。

  也就是說,在進行故障恢復的時候,可以使用binlog一條一條的恢復記錄。

  那我們要怎麼保證binlog一定能保證數據一致性呢,我們來聊聊MySQL中的兩階段提交。

  還是以update T set a = a + 1 where ID = 0;為例:

  解釋一下:一直到更新內存中的數據頁,在上面都已經提到過了。然後是將數據頁的更新寫入redo log中。

  注意,這裡寫的redo log,並不是寫入了redo log的文件中,而是寫入了名為redo log的buffer中,也就是說此時並沒有使用磁碟IO,不會造成性能的降低。

  然後,進入了名為prepare的階段。

  然後,寫入bin log。注意,這裡說的寫入bin log,也同樣沒有持久化,也是寫入了buffer中。

  只有當這兩者都寫入成功了,才會到提交事務的階段。

  然後,有兩個參數很重要。

  這兩個參數決定了是否等待直到將redo log和bin log持久化之後再返回。

  sync_binlog和innodb_flush_log_at_trx_commit。

  先說說innodb_flush_log_at_trx_commit:

  當設置參數為1時,(默認為1),表示事務提交時必須調用一次 fsync 操作,最安全的配置,保障持久性。

  當設置參數為2時,則在事務提交時只做 write 操作,只保證將redo log buffer寫到系統的頁面緩存中,不進行fsync操作,因此如果MySQL資料庫宕機時,不會丟失事務,但作業系統宕機則可能丟失事務。

  當設置參數為0時,表示事務提交時不進行寫入redo log操作,這個操作僅在master thread 中完成,而在master thread中每1秒進行一次重做日誌的fsync操作,因此實例 crash 最多丟失1秒鐘內的事務。(master thread是負責將緩衝池中的數據異步刷新到磁碟,保證數據的一致性)。

  也就是說,如果我們設置為了1,在最後提交的時候,會調用fsync等待redo log持久化,才返回。

  再說說sync_binlog:

  sync_binlog=0的時候,表示每次提交事務都只write,不fsync。

  sync_binlog=1的時候,表示每次提交事務都會執行fsync。

  sync_binlog=N(N>1)的時候,表示每次提交事務都write,但累積N個事務後才fsync。但如果宕機了可能會丟失最後的N條語句。

  也就是說,如果我們設置為了1,最後提交的時候會和上面說到的一樣,等待系統的fsync。

  那麼,我們為什麼需要兩階段提交來保證數據的一致性呢?

  我們假設現在寫完了redo log,進入了prepare階段,但是還沒有寫bin log,此時資料庫宕機,那麼重啟後事務會回滾,不影響數據。

  再做一個假設,我們已經寫完了bin log,宕機了,再重啟後MySQL會判斷redo log是否已經有了commit標識,如果有,則提交;否則的話,去判斷bin log是否完整,如果是完整的,則提交,否則回滾。

  那麼,如果我們沒有將階段提交,會怎麼樣呢?

  假設我們先提交redo log,再提交bin log,此時邏輯和兩階段提交一樣,但是沒有了兩次驗證。那麼如果我們在redo log提交完了宕機了,那麼我們重啟後,可以根據redo log來恢復數據。但是因為我們在bin log中沒有更新,所以在未來如果使用bin log進行恢復,或者同步從庫的時候,將會導致數據不一致。(主從同步問題在以後的文章解釋)

  再做一個假設,先提交bin log,再提交redo log。那麼在恢復的時候這個數據是沒有被更新的,但是在未來使用bin log的時候,會發現這裡的數據不一致。

  所以說,兩階段提交是為了保證這兩個日誌是可以一致的。

  寫在最後

  首先,謝謝你能看到這裡。

  希望這篇文章能夠給你帶來幫助,讓你對MySQL的了解可以加深一些。當然了,文章篇幅有限,作者水平也有限,文章中很多地方的細節沒有展開講。很多知識點會在今後的文章中不斷進行補充。另外,如果你發現了作者不對的地方,還請不吝指正,謝謝你!

  其次,要特別感謝雄哥,給了我很多的幫助!另外,也特別感謝丁奇老師,我是以《MySQL實戰45講》作為主線進行學習的。

相關焦點

  • MySQL環境配置和入門講解!
    =或者<>不等於<小於>大於<=、>=小於等於或者大於等於OR或AND與BETWEEN…AND…在….和….之間IN、NOT IN在或者不在三、安裝MySQL下面是以Ubuntu18為例安裝MySQL資料庫。3.1 官網下載進入MySQL資料庫的官網。
  • MySQL環境配置和10分鐘快速入門
    =或者<>不等於<小於>大於<=、>=小於等於或者大於等於OR或AND與BETWEEN…AND…在….和….之間IN、NOT IN在或者不在三、安裝MySQL下面是以Ubuntu18為例安裝MySQL資料庫。3.1 官網下載進入MySQL資料庫的官網。
  • 入門MySQL——查詢語法練習
    其實MySQL官網給出了多個示例資料庫供大家實用查詢,下面我們以最常用的員工示例資料庫為準,詳細介紹各自常用的查詢語法。1.員工示例資料庫導入官方文檔員工示例資料庫介紹及下載連結:https://dev.mysql.com/doc/employee/en/employees-installation.html 同樣的,為了方便大家,我這裡將員工庫的資料庫備份分享給大家,大家也可以下載我這份數據,然後再解壓導入進你們本地庫就可以了。
  • 重新學習Mysql資料庫1:無廢話MySQL入門
    該系列博文會告訴你如何從入門到進階,從sql基本的使用方法,從MySQL執行引擎再到索引、事務等知識,一步步地學習MySQL相關技術的實現原理,更好地了解如何基於這些知識來優化sql,減少SQL執行時間,通過執行計劃對SQL性能進行分析,再到MySQL的主從複製、主備部署等內容,以便讓你更完整地了解整個MySQL方面的技術體系,形成自己的知識框架。
  • MySQL全面瓦解—子查詢和組合查詢
    from型子查詢:把內層的查詢結果當成臨時表,供外層sql再次查詢,所以支持的是表子查詢。  where或having型子查詢:指把內部查詢的結果作為外層查詢的比較條件,支持標量子查詢(單列單行)、列子查詢(單列多行)、行子查詢(多列多行)。
  • mysql結果去重和條件查詢where的使用
    mysql執行結果這樣我們就查詢出了不同的ip出現的結果。mysql結果去重和條件查詢where的使用執行完畢提示有156行受到影響,說明有156條數據,我們也可以用統計函數count進行得出需要的數據
  • MySQL資料庫接口的VC實現與應用(1)
    眾所周知,所有的網上信息都是儲存在網站資料庫中的,這些信息的查詢、更新等操作的功能則是由資料庫伺服器提供的,顯然,資料庫伺服器的性能將直接關係到網站的生存。網站搭建中用的最多的資料庫伺服器是oracle和MySQL,前者功能強大,屬於旗艦型資料庫伺服器,但前期投入太大;後者功能不斷完善,簡單易用而又不失性能,並且可以免費獲得。
  • 一條查詢SQL在MySQL中是怎麼執行的
    Server層:包含連接器、查詢緩存、分析器、優化器、執行器等,涵蓋了MySQL的大部分核心服務功能,以及所有的內置函數,所有的跨存儲引擎的功能都在這一層實現,比如存儲過程、觸發器、視圖等,存儲引擎層:負責數據的存儲和提取,其架構模式是插件式的,支持InnoDB、MyISAM、Memory等多個存儲引擎。
  • 入門MySQL——用戶與權限
    他們可以登錄 MySQL,只允許進行不需要權限的操作,如使用 SHOW 語句查詢所有存儲引擎和字符集的列表等。如果兩個用戶具有相同的用戶名和不同的主機名,MySQL 會將他們視為不同的用戶,並允許為這兩個用戶分配不同的權限集合。
  • 拿什麼拯救你,我的MySQL子查詢
    mysql的子查詢一直被人所詬病,主要原因是mysql子查詢奉行的是「由外到內」的政策。
  • (進階篇)PHP+Mysql+jQuery實現查詢和列表框選擇操作
    本文講解如何通過ajax查詢mysql數據,並將返回的數據顯示在待選列表中,再通過選擇最終將選項加入到已選區,可以用在許多後臺管理系統中。本文列表框的操作依賴jquery插件:Multiselect。,和一個列表框,以及相關按鈕。
  • MySQL 事務預編譯查詢和Perl DBI簡化
    許多WEB應用程式內部通常重複運行帶不同自變量的相同資料庫查詢,或以全有或全無塊的形式執行一組相關查詢。要滿足這些要求,大多數資料庫系統(包括MySQL)支持預編譯查詢與事務,多數腳本語言(如PHP和Perl)也擁有內置函數訪問這些資料庫特性。
  • Mysql使用exists的子查詢
    形式:where exists ( 任何子查詢 )含義:該子查詢如果「有數據結果」, 則該exists()的結果為「true」>該子查詢如果「沒有數據結果」,則該exists()的結果為「false」特別注意:1,該子查詢,如果其僅僅是為了得到「有沒有數據」的結果,則通常此時對主查詢就失去應用意義;2,實際上,該子查詢,通常都需要在子查詢(內部)中來使用主查詢(外部)的某些欄位作為條件數據,這樣才能具有一定的實用意義
  • MySQL資料庫測試題
    MySQL資料庫入門1、假設要按照分頁(每頁顯示10條)的形式獲取test表中的數據,在MySql中創建book表,並添加id欄位和title欄位?varchar(32),title varchar(50)];9、"truncate table user;"當前sql在mysql5.1
  • mysql 慢查詢命令
    如何查找MySQL中查詢慢的SQL語句更多如何在mysql查找效率慢的SQL語句呢?二、檢測mysql中sql語句的效率的方法1、通過查詢日誌(1)、Windows下開啟MySQL慢查詢MySQL在Windows系統中的配置文件一般是是my.ini
  • 如何基於Python實現MySQL查詢的API設計,附上完整腳本
    如果是基於Shell的方式,很容易出現一個使用瓶頸,那就是如果通過shell去查看一個表的數據,那麼輸出是沒有規範的格式的,Shell執行是最簡單最基本的調用模式,我們也可以利用資料庫服務端的特性來輸出相應的數據格式,但是基於資料庫版本的差異,有些低版本是不支持輸出一些格式的,所以使用Shell來輸出SQL查詢的結果顯然不是一個通用而且優雅的實現方式。
  • mysql查詢——mysql中數據累加的方法
    下圖是一張3月份的銷售數據表(sales),其中包含欄位序號Id、日期Date、銷量Sales,現在需要編寫一個查詢語句,統計3月1日到每日的累計銷量。方法一:通過自定義變量實現select date,sales,@cum_sales:=@cum_sales+sales as cum_salesfrom sales,(select @cum_sales:=0)corder by date asc;方法二:通過將聚合函數sum作為窗口函數實現(mysql8.0及以上版本可用
  • MySQL如何完成一次查詢?
    那麼從發出一條sql指令到返回結果mysql都做了什麼事情呢?mysql完成一次查詢過程是比較複雜的,在說明查詢過程前先介紹一下它的基礎概念和結構原理來幫助理解。下面從四個方面介紹,分別是mysql語句,mysql結構原理,mysql查詢過程,最後設置幾個有趣問題。
  • 教你快速實現 MySQL查詢結果的分頁顯示
    在mysql中利用select語句的一個特性就可以很方便地實現查詢結果的分頁,select語句的語法: [LIMIT [offset,] rows] [PROCEDURE procedure_name] ] LIMIT子句可以用來限制由SELECT語句返回過來的數據數量,它有一個或兩個參數,如果給出兩個參數,第一個參數指定返回的第一行在所有數據中的位置,從0開始(注意不是1),第二個參數指定最多返回行數
  • MySQL查詢語句中的IN 和Exists 對比分析
    網上查了一下相關資料,大體可以歸納為:外部表小,內部表大時,適用Exists;外部表大,內部表小時,適用IN。那我就困惑了,因為我的SQL語句裡面,外表只有1W級別的數據,內表有30W級別的數據,按網上的說法應該是Exists的效率會比IN高的,但我的結果剛好相反!!「沒有調查就沒有發言權」!