mysql 排序、查詢執行流程、幻讀

文章目錄

  • MySQL的 ORDER BY 執行流程
    • 示例表和查詢語句
    • 執行流程
      • 全字段排序
      • Rowid 排序
      • 全字段排序 VS rowid排序
      • 聯合索引優化
      • 覆蓋索引優化
    • 小結
    • 思考題
      • 問題
      • 執行過程中是否需要排序?
      • 如何在數據庫端實現不排序?
      • 實現分頁需求
  • 使用`ORDER BY RAND()`
    • 內存臨時表與磁盤臨時表
    • 隨機選擇算法的優化
    • 實際應用和進一步優化
    • 小結
    • 思考題
    • 回答
      • 示例優化:
  • 誤區
      • 案例一:條件字段函數操作
      • 案例二:隱式類型轉換
      • 案例三:隱式字符編碼轉換
      • 總結優化策略
      • 行動建議
  • 為什么單行查詢在MySQL中執行緩慢
      • 背景
      • 第一類:查詢長時間不返回
        • 等待MDL鎖
        • 等待表Flush
        • 等待行鎖
      • 第二類:查詢執行慢
        • 全表掃描
        • 一致性讀導致慢查詢
      • 解決查詢慢的策略
      • 小結
      • 思考題
  • 幻讀
      • 幻讀的定義
      • 幻讀的問題
      • 解決幻讀的策略
      • 幻讀的業務影響
      • 總結

MySQL的 ORDER BY 執行流程

在開發應用時,經常需要根據指定字段排序來顯示結果。以下是關于 MySQL 中 ORDER BY 的執行流程及影響因素的總結。

示例表和查詢語句

假設有一個市民表,定義如下:

CREATE TABLE `t` (`id` int(11) NOT NULL,`city` varchar(16) NOT NULL,`name` varchar(16) NOT NULL,`age` int(11) NOT NULL,`addr` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`),KEY `city` (`city`)
) ENGINE=InnoDB;

查詢城市為“杭州”的市民名字,并按姓名排序返回前1000個結果的SQL語句:

SELECT city, name, age FROM t WHERE city='杭州' ORDER BY name LIMIT 1000;

執行流程

全字段排序

當執行一個包含ORDER BY的查詢時,如果不使用適當的索引,MySQL會使用全字段排序:

  1. 初始化排序緩沖區sort_buffer被用來存儲需要排序的列。
  2. 從索引中檢索數據:基于city索引檢索滿足條件的記錄,然后加載相應的字段到sort_buffer
  3. 執行排序:在sort_buffer中對數據進行排序。
  4. 輸出結果:根據排序結果輸出前1000條數據。

Rowid 排序

當單行數據量過大時,MySQL可能采用rowid排序以節約內存:

  1. 排序緩沖區較小:只存儲關鍵的排序字段和主鍵id。
  2. 數據檢索和排序:檢索并排序關鍵字段,使用主鍵再次查詢以獲取完整數據。
  3. 結果輸出:根據排序后的id順序輸出結果。

全字段排序 VS rowid排序

在內存足夠的情況下優先選擇全字段排序,否則rowid排序

體現了MySQL的一個設計思想: 如果內存夠,就要多利用內存,盡量減少磁盤訪問。

聯合索引優化

  1. 創建聯合索引
ALTER TABLE t ADD INDEX city_user(city, name);
  1. 查詢流程簡化:利用聯合索引 city_user(city, name),天然保證按 name 遞增排序,避免排序操作,流程如下:
    • 從索引(city, name)中找到第一個滿足 city=‘杭州’ 條件的記錄。
    • 取出該記錄中的 city、name、age 直接返回。
    • 重復上述步驟,直到取到1000條記錄或不滿足條件時停止。

覆蓋索引優化

  1. 創建覆蓋索引
ALTER TABLE t ADD INDEX city_user_age(city, name, age);
  1. 查詢流程進一步簡化:利用覆蓋索引避免回表操作,流程如下:
    • 從索引(city, name, age)中找到第一個滿足 city=‘杭州’ 條件的記錄,直接取出并返回。
    • 重復上述步驟,直到取到1000條記錄或不滿足條件時停止。

小結

MySQL 的排序操作有多種實現方式,具體選擇取決于查詢條件和索引情況。為了提高性能,建議合理利用聯合索引和覆蓋索引,減少排序操作和磁盤訪問。

思考題

問題

假設表中已有聯合索引 city_name(city, name),查詢杭州和蘇州兩個城市的市民名字,并按名字排序,顯示前100條記錄:

SELECT * FROM t WHERE city IN ('杭州', '蘇州') ORDER BY name LIMIT 100;

執行過程中是否需要排序?

由于查詢條件包含多個城市,合并后可能無序,無法利用單一的聯合索引順序,MySQL 需要對結果集進行排序。

如何在數據庫端實現不排序?

每個城市單獨查詢

可以創建聯合索引 city_name(city, name) 并結合分區查詢:

(SELECT * FROM t WHERE city = '杭州' ORDER BY name LIMIT 100)
UNION ALL
(SELECT * FROM t WHERE city = '蘇州' ORDER BY name LIMIT 100)
ORDER BY name LIMIT 100;

實現分頁需求

對于第101頁,即查詢第10000至10099條記錄:

(SELECT * FROM t WHERE city = '杭州' ORDER BY name LIMIT 10000, 100)
UNION ALL
(SELECT * FROM t WHERE city = '蘇州' ORDER BY name LIMIT 10000, 100)
ORDER BY name LIMIT 10000, 100;

這種方式避免了單次大數據量排序,提高查詢效率。

本文主要講述了如何在MySQL中實現隨機選擇單詞的功能,并分析了使用ORDER BY RAND()導致的性能問題,提出了幾種優化方法。

使用ORDER BY RAND()

在初步嘗試中,可以使用SELECT word FROM words ORDER BY RAND() LIMIT 3;來隨機選擇三個單詞。這種方法雖簡單,但隨著數據量的增大,性能問題逐漸顯現:

  • MySQL需要在內存中創建臨時表并對其進行隨機排序,這一過程涉及大量的數據讀取和排序操作。
  • 根據EXPLAIN命令的輸出,此查詢需要使用臨時表和文件排序,顯著增加了查詢的資源消耗。

內存臨時表與磁盤臨時表

文章進一步解釋了MySQL在處理ORDER BY RAND()時可能使用的兩種臨時表:

  1. 內存臨時表:如果臨時表的大小未超過tmp_table_size配置的限制,則使用內存中的臨時表。
  2. 磁盤臨時表:如果臨時表大小超過限制,則轉為在磁盤上創建臨時表,這會進一步降低性能,由internal\_tmp\_disk\_storage\_engine控制。

隨機選擇算法的優化

文章提出了兩種優化隨機選擇單詞的方法:

  1. 隨機算法1

    • 查詢表的主鍵ID的最大和最小值。
    • 生成一個介于最大和最小ID之間的隨機數。
    • 查詢ID大于或等于該隨機數的第一條記錄。
    • 這種方法雖然快速,但不夠均勻,特別是當ID存在空洞時。
  2. 隨機算法2

    • 計算表中的總行數。
    • 生成一個隨機數來選擇行號。
    • 使用LIMIT語句直接跳過前N行,返回第N+1行。
    • 這種方法提供了更均勻的隨機性,但性能開銷大于算法1,盡管依然優于ORDER BY RAND()

實際應用和進一步優化

對于實際應用,尤其是行數較多的表,推薦使用隨機算法2,并在應用層進行SQL語句的構建和執行。此外,對于隨機選擇多個單詞的需求,可以通過生成多個隨機行號并分別查詢來實現,盡管這會增加數據庫的訪問次數。

小結

在設計數據庫交互時,考慮查詢的性能影響是非常關鍵的。直接使用ORDER BY RAND()可能引起嚴重的性能問題,尤其是在數據量較大的情況下。通過理解MySQL的內部工作原理和利用優化的查詢方法,可以顯著提高應用的響應速度和整體性能。

思考題

  • 如何進一步減少隨機算法3的掃描行數?
  • 是否有可能通過改進SQL結構或使用特定的數據庫功能(如更復雜的索引策略)來優化隨機選擇的效率?

回答

要減少隨機算法3的掃描行數,并優化隨機選擇的效率,可以采取以下措施:

  1. 使用單查詢優化

    • 在一次查詢中獲取多個隨機行。例如:
      SELECT * FROM words WHERE id >= @X ORDER BY id LIMIT 3;
      
    • 這樣可以減少多次掃描。
  2. 利用表統計信息

    • 獲取表的總行數并計算隨機偏移量。
      SELECT COUNT(*) INTO @C FROM words;
      SET @Y = FLOOR(@C * RAND());
      SELECT * FROM words LIMIT @Y, 1;
      
    • 更準確地生成隨機數以減少空洞。
  3. 索引優化

    • 創建復合索引,提高查詢速度。
  4. 應用層優化

    • 緩存部分單詞表,應用層隨機選擇,減少數據庫訪問。
    • 預加載單詞列表到應用層進行隨機選擇。
  5. 利用數據庫特性

    • 使用數據庫特定的優化功能,如隨機選擇函數。

示例優化:

使用單查詢獲取多個隨機行,并利用表統計信息:

SELECT COUNT(*) INTO @C FROM words;
SET @Y1 = FLOOR(@C * RAND());
SET @Y2 = FLOOR(@C * RAND());
SET @Y3 = FLOOR(@C * RAND());
SELECT * FROM words WHERE id >= @Y1 LIMIT 1;
SELECT * FROM words WHERE id >= @Y2 LIMIT 1;
SELECT * FROM words WHERE id >= @Y3 LIMIT 1;

這樣可以減少掃描行數,提高效率。

誤區

案例一:條件字段函數操作

  • 問題描述:在使用函數處理索引字段(如month(t_modified))時,可能會破壞值的有序性,優化器會放棄走樹搜索。MySQL無法利用索引進行快速查找,導致性能下降。
  • 解決方案:避免在查詢條件中對索引字段使用函數。改用基于字段本身的范圍查詢,例如使用t_modified BETWEEN '2016-7-1' AND '2016-7-31'

案例二:隱式類型轉換

  • 問題描述:當查詢條件中的數據類型不匹配字段類型時(如整數與字符串比較tradeid = 110717),會觸發類型轉換,從而導致索引失效。
  • 解決方案:確保查詢條件中的數據類型與數據庫字段的類型一致,避免隱式類型轉換。

案例三:隱式字符編碼轉換

  • 問題描述:不同表之間的字段字符編碼不一致(如utf8utf8mb4),在進行連接查詢時無法使用索引。

這個設定很好理解,utf8mb4是utf8的超集。類似地,在程序設計語言里面,做自動類型轉換的時候,為了避免數據在轉換過程中由于截斷導致數據錯誤,也都是“按數據長度增加的方向”進行轉換的。

  • 解決方案
    • 調整編碼:調整相關字段的字符編碼,使其一致。
    • 顯式轉換:在查詢時使用CONVERT函數顯式進行字符編碼轉換。

總結優化策略

  1. 避免在索引字段上使用函數:使用索引字段本身而非經過函數處理的結果作為查詢條件。
  2. 保持數據類型一致:在查詢中使用與數據庫字段相同的數據類型。
  3. 統一字符編碼:確保關聯查詢中涉及的字段具有相同的字符編碼,或在查詢中指定正確的字符編碼轉換。

行動建議

  • 在業務代碼升級時,對可能出現的新SQL語句進行EXPLAIN分析,是一個避免性能問題的好習慣。
  • 共享實際遇到的性能問題及其解決方案,可以幫助團隊成員學習并提高處理類似問題的能力。

為什么單行查詢在MySQL中執行緩慢

背景

  • 常見的理解是,復雜查詢或大數據量返回會導致性能問題,但某些單行查詢同樣會非常緩慢。
  • 排除數據庫整體負載高的情況,本文專注于單行查詢的特定性能問題。

第一類:查詢長時間不返回

等待MDL鎖
  • 問題:查詢被表的元數據鎖(MDL)阻塞。
  • 診斷:使用 SHOW PROCESSLIST 查看狀態為 Waiting for table metadata lock
  • 解決:通過 sys.schema_table_lock_waits 找到阻塞的線程ID并 KILL 掉。
等待表Flush
  • 問題:查詢被 FLUSH TABLES 操作阻塞。
  • 診斷SHOW PROCESSLIST 顯示狀態為 Waiting for table flush
  • 解決:找到并終止執行 FLUSH TABLES 的會話。
等待行鎖
  • 問題:查詢被另一個事務持有的行鎖阻塞。
  • 診斷SHOW PROCESSLIST 顯示狀態為 Waiting for lock.
  • 解決:使用 sys.innodb_lock_waits 查找持有鎖的事務,并 KILL 掉。

第二類:查詢執行慢

全表掃描
  • 問題:查詢沒有使用索引,導致全表掃描。
  • 示例SELECT * FROM t WHERE c=50000 LIMIT 1;
  • 解決:添加適當的索引。
一致性讀導致慢查詢
  • 問題:一致性讀導致掃描大量回滾日志(undo log)。
  • 示例SELECT * FROM t WHERE id=1;
  • 復現:一個事務進行大量更新,導致查詢需要回滾大量操作。
  • 解決:優化事務設計,避免生成大量的undo log。

解決查詢慢的策略

  1. 使用索引:確保查詢條件使用適當的索引。
  2. 優化事務設計:避免長事務和大事務,減少鎖爭用。
  3. 監控和診斷工具:使用 SHOW PROCESSLISTsys.innodb_lock_waits 等工具診斷問題。

小結

即使是簡單的單行查詢也可能因多種原因導致性能問題。了解和識別這些問題的原因是關鍵,合理使用工具和策略可以顯著提升查詢效率。

思考題

考慮查詢SELECT * FROM t WHERE c = 5 FOR UPDATE;,分析它如何加鎖以及鎖釋放的時機,討論可能的性能影響。

幻讀

幻讀的定義

幻讀是在數據庫事務處理中遇到的一種現象,它發生在一個事務(Transaction)在執行過程中進行兩次相同的查詢,卻得到了不同的結果。這種情況通常是由于在這兩次查詢間有其他事務插入或修改了數據所致。

幻讀在“當前讀”下才會出現

幻讀的問題

幻讀主要問題在于它違反了事務的隔離性,特別是在可重復讀(Repeatable Read)隔離級別下。在這個級別,預期事務能夠多次讀取同樣的數據行并得到相同的結果,但幻讀破壞了這一期望。

在上文中提到的例子中,盡管在事務開始時鎖定了d=5的行(id=5),事務中途對其他行(如id=0或id=1)的修改和新增行(id=1新增),導致了幻讀,因為這些修改和添加的數據行在后續的查詢中被看到了,違反了預期的事務隔離性。

解決幻讀的策略

為了解決幻讀問題,MySQL的InnoDB存儲引擎引入了間隙鎖(Gap Locks)的概念,除了對查詢到的數據行加鎖外,還對數據行之間的間隙加鎖。這種鎖策略不僅鎖定數據行,也鎖定行之間的間隙,防止其他事務在這些間隙中插入新的行。間隙鎖和行鎖合稱next-key lock,每個next-key lock是前開后閉區間。

例如,如果事務A查詢了所有d=5的數據行并對其加鎖,通過間隙鎖機制,事務B將無法插入一個新的d=5的行,因為這將需要在已鎖定的間隙中插入數據,而這是被禁止的。

幻讀的業務影響

幻讀可能會導致數據不一致性,影響業務邏輯的正確執行。例如,如果一個事務基于查詢結果執行操作(如計算總和或更新數據),由于幻讀導致的數據變化可能會使得最終結果不正確或事務失敗。

總結

在處理數據庫事務時,了解和應對幻讀是確保數據一致性和事務隔離性的關鍵。通過使用適當的隔離級別和理解數據庫的鎖機制,可以有效地管理和減少幻讀帶來的問題。在設計數據庫操作和事務邏輯時,合理選擇隔離級別和理解其對應的鎖策略對于開發穩定可靠的應用程序至關重要。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/17717.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/17717.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/17717.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ANDROID OLLVM 混淆配置

安裝環境 MacOSGITCMAKENDK - 21.1.6352462 步驟 1. 編譯項目 此項目版本較低 https://github.com/obfuscator-llvm/obfuscator ,我們使用 https://github.com/heroims/obfuscator 進行編譯 git clone https://github.com/heroims/obfuscator.gitcd obfuscator…

曼城四連冠,劍南春與萬千球迷共同見證“榮耀時刻”

執筆 | 洪大大 編輯 | 揚 靈 5月19日,英超2023-2024賽季第38輪比賽全面開打,憑借隊員的出色發揮,曼城最終以3-1戰勝西漢姆聯,成功捧起了英超聯賽的獎杯,成為英格蘭足球頂級聯賽100多年歷史上第一支成就四連冠的豪門…

事務報錯沒有顯示回滾導致DDL阻塞引發的問題

在業務開發過程中,顯示的開啟事務并且在事務處理過程中對不同的情況進行顯示的COMMIT或ROLLBACK,這是一個完整數據庫事務處理的閉環過程。 這種在應用開發邏輯層面去handle的事務執行的結果,既確保了事務操作的數據完整性,又遵循了…

簡單句語法

簡單句是指包含一個主語和一個謂語的句子,它表達一個完整的思想。簡單句是構成更復雜句子的基礎。 簡單句的兩種基本結構 簡單句可以分為兩種基本結構: 主謂結構: 描述主語所做的動作或行為,也就是 “做什么”。 主系結構: 描述主語的狀態…

Python2和Python3對utf8的實現方式有什么區別?

# -*- coding: utf8 -*- 是一個特殊的文件頭部注釋,通常出現在Python 2的源代碼文件的開頭。這個注釋告訴Python解釋器,該源文件使用的是UTF-8編碼。這對于包含非ASCII字符(例如中文字符、特殊符號等)的Python源代碼文件來說非常重…

探索未來設計新境界,PSAI插件 藝術創作神器來襲!

想象一下,如果有一個工具,能夠讓你的設計工作變得既簡單又高效,那會是怎樣的體驗?現在,夢想成真了! 這是一款革命性的PSAI設計插件,專為創意人士打造。它將徹底改變你的設計流程,讓你…

【OpenCV】像素信息統計

介紹了計算像素均值、方差的API,以及統計像素信息的方法。相關API: minMaxLoc()mean()meanStdDev() 代碼: #include "iostream" #include "opencv2/opencv.hpp"using namespace std; using namespace cv;int main(int…

談談如何建立可落地的數字化轉型戰略

數字化轉型戰略是指將數字技術集成到企業或組織的所有領域,從根本上改變其運營方式以及為客戶提供價值的方式。它涉及采用新技術并重新思考現有業務流程,以提高效率、生產力和客戶滿意度。 成功的數字化轉型戰略需要采用涉及人員、流程和技術的整體方法。…

【全開源】JAVA同城搬家系統源碼小程序APP源碼

JAVA同城搬家系統源碼 特色功能: 強大的數據處理能力:JAVA提供了豐富的數據結構和算法,以及強大的并發處理能力,使得系統能夠快速地處理大量的貨物信息、司機信息、訂單信息等,滿足大規模物流的需求。智能路徑規劃&a…

香橙派 AIPro開發板上手測評

前言 最近拿到了一個新玩具:香橙派 AIPro。一個只比銀行卡大一點點的開發板能帶給我們多少驚喜呢?接下來就跟我一起來體驗下這塊開發板的魅力。 一、硬件配置 CPU:配備了4核64位ARM處理器,其中默認預留1個給AI處理器使用 NPU&am…

SpringBoot和Apache Doris實現實時廣告推薦系統

本專題旨在向讀者深度解讀Apache Doris技術,探討其與SpringBoot框架結合在各類實際應用場景中的角色與作用。本專題包括十篇文章,每篇文章都概述了一個特定應用領域,如大數據分析、實時報告系統、電商數據分析等,并通過對需求的解析、解決方案的設計、實際應用示例的展示以…

【Python實戰】你還在沖會員看電影電視劇嗎?Python帶你實現各大資源免費看!

前言 halo,包子們下午好 今天給大家實現一個視頻播放器,可以看任何電影,電視劇,不要再為以后看電視看電影而煩惱,今天是福利文章,相信我絕對有用! 開發工具 Python版本:3.7.8 相…

Java Lambda 會影響性能嗎?

# 測試代碼LamdaTest.java import java.util.*;class LamdaTest {static volatile List<Integer> integers new ArrayList<Integer>();// 普通 for 循環測試public static int forLoopInteger() {int total 0;for (int i 0; i < integers.size(); i) {total…

驅動未來:IT行業的現狀與發展趨勢

前言 隨著技術的不斷進步&#xff0c;IT行業已成為推動全球經濟和社會發展的關鍵力量。從云計算、大數據、人工智能到物聯網、5G通信和區塊鏈&#xff0c;這些技術正在重塑我們的生活和工作方式。本文將探討IT行業的現狀和未來發展趨勢&#xff0c;并邀請行業領袖、技術專家和…

Follow Your Pose: Pose-Guided Text-to-Video Generation using Pose-Free Videos

清華深&港科&深先進&Tencent AAAI24https://github.com/mayuelala/FollowYourPose 問題引入 本文的任務是根據文本來生成高質量的角色視頻&#xff0c;并且可以通過pose來控制任務的姿勢&#xff1b;當前缺少video-pose caption數據集&#xff0c;所以提出一個兩…

Java的上下轉型與多態

上下轉型 首先&#xff0c;定義一個父類Person // 父類 class Person {public void run(){System.out.println("person 中的 run");}public void eat(){System.out.println("Person 中的 eat");}}接著定義一個繼承自父類的子類Student: // 子類 class S…

拿捏數據結構- 鏈式二叉樹

鏈式二叉樹的概念&#xff1a; 鏈式二叉樹解決的是非完全二叉樹解決不了的問題 什么意思呢&#xff0c;簡單的說就是&#xff0c;鏈式二叉樹 可以是下面三種二叉樹 但是非鏈式二叉樹只能是前兩種 鏈式二叉樹的存儲 節點結構&#xff1a;首先定義一個結構體或類來表示二叉樹的節…

機器學習-7-機器學習中常用的可視化方式總結

參考通透!!監督學習和無監督學習全總結! 參考機器學習中的可視化 1 監督學習和無監督學習 監督學習和無監督學習,它們之間的主要區別在于訓練數據的標簽信息是否提供。 1.1 概述 一、監督學習(Supervised Learning): (1)標簽信息: 監督學習使用帶有標簽的訓練數據。這…

單元測試的實現方式

單元測試的實現方式包括&#xff1a;人工靜態檢查、動態執行跟蹤 人工靜態檢查 人工靜態檢查是一種單元測試實現方式&#xff0c;它主要依賴開發人員的人工代碼審查和靜態分析工具來識別潛在的代碼問題。 代碼審查&#xff1a;開發人員通過仔細檢查代碼來發現潛在的問題。他…

不怕YOLOv10高歌猛進,我有YOLOv8穩扎穩打

YOLOv10 出來有幾天時間了&#xff0c;這次我沒有選擇第一時間出文章解析&#xff0c;如此頻繁的發布數字版本的 YOLO 著實讓人頭疼&#xff0c;雖然數字的更新并非舊版技術的過時&#xff0c; 但是這肯定會讓很多在校同學增加很多焦慮情緒。這里還是請大家辯證看待。 v10 這次…