偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

MySQL的六大雷區(qū),99%的人會踩中!

數(shù)據(jù)庫 MySQL
有些小伙伴在工作中,可能經(jīng)常遇到這樣的場景:系統(tǒng)上線初期運行良好,隨著數(shù)據(jù)量增長,突然某天接口超時、CPU飆升、甚至整個系統(tǒng)癱瘓。排查半天,發(fā)現(xiàn)是某個SQL語句寫的有問題,或者是數(shù)據(jù)庫配置不當(dāng)導(dǎo)致的。

前言

有些小伙伴在工作中,可能經(jīng)常遇到這樣的場景:系統(tǒng)上線初期運行良好,隨著數(shù)據(jù)量增長,突然某天接口超時、CPU飆升、甚至整個系統(tǒng)癱瘓。

排查半天,發(fā)現(xiàn)是某個SQL語句寫的有問題,或者是數(shù)據(jù)庫配置不當(dāng)導(dǎo)致的。

今天這篇文章我就從淺入深,帶你徹底避開MySQL的6大常見雷區(qū),希望的對你會有所幫助。

為什么MySQL雷區(qū)如此之多?

在深入具體雷區(qū)之前,我們先聊聊為什么MySQL這么容易踩坑。

這背后有幾個深層次原因:

  • 看似簡單:MySQL語法簡單,入門容易,讓很多人低估了它的復(fù)雜性
  • 默認(rèn)配置坑多:MySQL的默認(rèn)配置往往不是最優(yōu)的,需要根據(jù)業(yè)務(wù)場景調(diào)整
  • 漸進式問題:很多問題在數(shù)據(jù)量小的時候不會暴露,等到暴露時已經(jīng)為時已晚
  • 知識更新快:從5.6到5.7再到8.0,每個版本都有重要變化,需要持續(xù)學(xué)習(xí)

有些小伙伴在工作中,可能直接用默認(rèn)配置部署MySQL,或者在寫SQL時只關(guān)注功能實現(xiàn),忽略了性能問題。

這就是為什么我們需要系統(tǒng)性地了解這些雷區(qū)。

好了,讓我們開始今天的主菜。我將從最常見的索引失效,逐步深入到復(fù)雜的死鎖問題,確保每個雷區(qū)都講透、講懂。

雷區(qū)一:索引失效的常見場景

索引是MySQL性能的基石,但錯誤的使用方式會讓索引失效,導(dǎo)致全表掃描。

這是最常見的性能雷區(qū)。

為什么索引會失效?

索引失效的本質(zhì)是MySQL優(yōu)化器認(rèn)為使用索引的成本高于全表掃描。

了解這些場景,可以幫助我們寫出更高效的SQL。

示例場景

-- 創(chuàng)建測試表
CREATE TABLE user (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    age INT,
    email VARCHAR(100),
    created_time DATETIME,
    INDEX idx_name (name),
    INDEX idx_age (age),
    INDEX idx_created_time (created_time)
);

-- 雷區(qū)1.1:對索引列進行函數(shù)操作
-- 錯誤寫法:索引失效
EXPLAIN SELECT * FROM user WHERE DATE(created_time) = '2023-01-01';

-- 正確寫法:使用范圍查詢
EXPLAIN SELECT * FROM user
WHERE created_time >= '2023-01-01 00:00:00'
AND created_time < '2023-01-02 00:00:00';

-- 雷區(qū)1.2:隱式類型轉(zhuǎn)換
-- 錯誤寫法:name是字符串,但用了數(shù)字,導(dǎo)致索引失效
EXPLAIN SELECT * FROM user WHERE name = 123;

-- 正確寫法:類型匹配
EXPLAIN SELECT * FROM user WHERE name = '123';

-- 雷區(qū)1.3:前導(dǎo)模糊查詢
-- 錯誤寫法:LIKE以%開頭,索引失效
EXPLAIN SELECT * FROM user WHERE name LIKE '%三%';

-- 正確寫法:非前導(dǎo)模糊查詢,可以使用索引
EXPLAIN SELECT * FROM user WHERE name LIKE '蘇%';

-- 雷區(qū)1.4:OR條件使用不當(dāng)
-- 錯誤寫法:age有索引,email無索引,導(dǎo)致整個查詢無法使用索引
EXPLAIN SELECT * FROM user WHERE age = 25 OR email = 'test@example.com';

-- 正確寫法:使用UNION優(yōu)化OR查詢
EXPLAIN
SELECT * FROM user WHERE age = 25
UNION
SELECT * FROM user WHERE email = 'test@example.com';

深度剖析

有些小伙伴在工作中可能會疑惑:為什么這些寫法會導(dǎo)致索引失效?

  1. 函數(shù)操作破壞索引有序性
  • 索引是按照列值的原始順序存儲的
  • 對列使用函數(shù)后,MySQL無法利用索引的有序性
  • 必須掃描所有索引項,計算函數(shù)值后再比較
  1. 隱式類型轉(zhuǎn)換的本質(zhì)
  • 當(dāng)類型不匹配時,MySQL會進行隱式轉(zhuǎn)換
  • 實際上相當(dāng)于:CAST(name AS SIGNED) = 123
  • 對索引列進行了函數(shù)操作,導(dǎo)致失效
  1. 前導(dǎo)模糊查詢的B+樹遍歷
  • B+樹索引按照前綴排序
  • LIKE '蘇%'可以利用前綴匹配
  • LIKE '%三'無法確定前綴,必須全表掃描

避坑指南

  • 避免對索引列進行函數(shù)操作
  • 確保查詢條件與索引列類型匹配
  • 謹(jǐn)慎使用前導(dǎo)模糊查詢
  • 使用UNION優(yōu)化復(fù)雜的OR查詢

雷區(qū)二:事務(wù)隔離級別與幻讀

事務(wù)隔離級別是MySQL中比較復(fù)雜的概念,理解不當(dāng)會導(dǎo)致數(shù)據(jù)不一致和性能問題。

為什么事務(wù)隔離級別重要?

不同的隔離級別在數(shù)據(jù)一致性、性能、并發(fā)性之間做出不同權(quán)衡。

選擇不當(dāng)會出現(xiàn)臟讀、不可重復(fù)讀、幻讀等問題。

示例場景

-- 查看當(dāng)前事務(wù)隔離級別
SELECT @@transaction_isolation;

-- 設(shè)置隔離級別為REPEATABLE-READ(默認(rèn))
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 場景:轉(zhuǎn)賬業(yè)務(wù)中的幻讀問題
-- 會話1:事務(wù)A
START TRANSACTION;
SELECT COUNT(*) FROM account WHERE user_id = 1001; -- 返回2

-- 會話2:事務(wù)B  
START TRANSACTION;
INSERT INTO account (user_id, balance) VALUES (1001, 500);
COMMIT;

-- 會話1:事務(wù)A繼續(xù)
SELECT COUNT(*) FROM account WHERE user_id = 1001; -- 仍然返回2(可重復(fù)讀)
UPDATE account SET balance = balance + 100 WHERE user_id = 1001; -- 影響3行!
SELECT COUNT(*) FROM account WHERE user_id = 1001; -- 返回3,出現(xiàn)幻讀!
COMMIT;

深度剖析

有些小伙伴在工作中可能遇到過:明明查詢時不存在的數(shù)據(jù),更新時卻影響到了。這就是典型的幻讀問題。

幻讀的本質(zhì)

  • 在可重復(fù)讀隔離級別下,普通SELECT看不到其他事務(wù)的插入
  • 但UPDATE/DELETE會看到所有已提交的數(shù)據(jù)
  • 這導(dǎo)致同一個事務(wù)內(nèi),查詢和更新看到的數(shù)據(jù)不一致

MySQL的解決方案

  • Next-Key Lock:MySQL通過間隙鎖防止幻讀
  • 在REPEATABLE-READ級別,MySQL不僅鎖住記錄,還鎖住記錄之間的間隙

為了理解間隙鎖的工作原理,我畫了一個鎖范圍示意圖:

圖片圖片

這個圖展示了當(dāng)查詢id > 8時,MySQL會鎖定[5,10]的間隙、ID=10的記錄,以及[10,∞]的間隙,防止其他事務(wù)插入ID>8的數(shù)據(jù)。

避坑指南

  • 理解不同隔離級別的特性
  • 在REPEATABLE-READ下,注意UPDATE可能產(chǎn)生幻讀
  • 對于需要絕對一致性的場景,使用SERIALIZABLE隔離級別
  • 合理設(shè)計事務(wù)邊界,避免長事務(wù)

雷區(qū)三:大數(shù)據(jù)量下的分頁優(yōu)化

分頁查詢是Web應(yīng)用中最常見的操作,但在大數(shù)據(jù)量下性能急劇下降。

為什么分頁會變慢?

LIMIT offset, size在offset很大時,需要掃描并跳過大量記錄,造成性能瓶頸。

示例場景

-- 創(chuàng)建測試表,假設(shè)有1000萬數(shù)據(jù)
CREATE TABLE order (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    amount DECIMAL(10,2),
    status TINYINT,
    created_time DATETIME,
    INDEX idx_created_time (created_time)
);

-- 雷區(qū):傳統(tǒng)的分頁寫法
-- 當(dāng)offset達到500萬時,性能急劇下降
EXPLAIN SELECT * FROM order
ORDER BY created_time DESC
LIMIT 5000000, 20;

-- 優(yōu)化方案1:游標(biāo)分頁(推薦)
-- 第一頁
SELECT * FROM order
ORDER BY created_time DESC, id DESC
LIMIT 20;

-- 第二頁:記住上一頁最后一條記錄的created_time和id
SELECT * FROM order
WHERE created_time < '2023-06-01 10:00:00'
   OR (created_time = '2023-06-01 10:00:00' AND id < 1000000)
ORDER BY created_time DESC, id DESC
LIMIT 20;

-- 優(yōu)化方案2:子查詢優(yōu)化(適用于非游標(biāo)場景)
SELECT * FROM order
WHERE id >= (
    SELECT id FROM order
    ORDER BY created_time DESC
    LIMIT 5000000, 1
)
ORDER BY created_time DESC
LIMIT 20;

深度剖析

有些小伙伴在工作中可能發(fā)現(xiàn),為什么offset越大查詢越慢?

傳統(tǒng)分頁的性能瓶頸

  1. 大量無效IO:需要讀取并跳過offset條記錄
  2. 回表成本:對于非覆蓋索引,需要回表查詢完整數(shù)據(jù)
  3. 排序開銷:大數(shù)據(jù)量的排序可能在磁盤進行

游標(biāo)分頁的優(yōu)勢

  • 直接定位到起始位置,無需跳過大量記錄
  • 利用索引的有序性,避免排序操作
  • 性能穩(wěn)定,不隨數(shù)據(jù)量增長而下降

為了理解傳統(tǒng)分頁與游標(biāo)分頁的區(qū)別,我畫了一個對比圖:

圖片圖片

避坑指南

  • 優(yōu)先使用游標(biāo)分頁(基于游標(biāo)或時間戳)
  • 如果必須使用傳統(tǒng)分頁,使用子查詢優(yōu)化
  • 確保排序字段有索引
  • 前端配合使用無限滾動或游標(biāo)分頁UI

雷區(qū)四:字符集與排序規(guī)則陷阱

字符集問題經(jīng)常在系統(tǒng)國際化或多語言支持時暴露,處理不當(dāng)會導(dǎo)致亂碼、排序錯誤、索引失效。

為什么字符集如此重要?

不同的字符集支持不同的字符范圍,排序規(guī)則影響字符串比較和排序結(jié)果。

示例場景

-- 查看字符集配置
SHOW VARIABLES LIKE'character_set%';
SHOW VARIABLES LIKE'collation%';

-- 雷區(qū):UTF8不是真正的UTF-8
-- MySQL的utf8最多支持3字節(jié),無法存儲emoji等4字節(jié)字符
CREATE TABLE user_utf8 (
    id INT PRIMARY KEY,
    name VARCHAR(50) CHARACTER SET utf8
);

-- 插入emoji表情失敗
INSERT INTO user_utf8 VALUES (1, '張三??'); -- 錯誤!

-- 正確:使用utf8mb4
CREATE TABLE user_utf8mb4 (
    id INT PRIMARY KEY,
    name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
);

-- 插入emoji成功
INSERT INTO user_utf8mb4 VALUES (1, '張三??'); -- 成功!

-- 雷區(qū):排序規(guī)則影響查詢結(jié)果
CREATE TABLE product (
    id INT PRIMARY KEY,
    name VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 大小寫不敏感查詢
SELECT * FROM product WHERE name = 'apple'; -- 會匹配'Apple', 'APPLE'

-- 如果需要大小寫敏感,使用binary或特定collation
SELECT * FROM product WHERE name = BINARY 'apple'; -- 只匹配'apple'

深度剖析

有些小伙伴在工作中可能遇到過存儲emoji失敗,或者查詢時大小寫匹配異常,這都是字符集配置不當(dāng)導(dǎo)致的。

UTF8 vs UTF8MB4

  • utf8:MySQL歷史上的"假UTF-8",最多3字節(jié),不支持emoji、部分中文生僻字
  • utf8mb4:真正的UTF-8實現(xiàn),支持4字節(jié),推薦使用

排序規(guī)則的影響

  • _ci結(jié)尾:大小寫不敏感(Case Insensitive)
  • _cs結(jié)尾:大小寫敏感(Case Sensitive)
  • _bin結(jié)尾:二進制比較,完全匹配

為了理解不同字符集的存儲范圍,我畫了一個對比圖:

圖片圖片

避坑指南

  • 新項目一律使用utf8mb4字符集
  • 根據(jù)業(yè)務(wù)需求選擇合適的排序規(guī)則
  • 數(shù)據(jù)庫、表、字段、連接字符集保持一致
  • 遷移現(xiàn)有數(shù)據(jù)時注意字符集轉(zhuǎn)換

雷區(qū)五:外鍵與級聯(lián)操作的隱患

外鍵約束可以保證數(shù)據(jù)完整性,但使用不當(dāng)會帶來性能問題和復(fù)雜的維護成本。

為什么外鍵是雙刃劍?

外鍵在保證數(shù)據(jù)一致性的同時,會帶來鎖競爭、維護復(fù)雜、遷移困難等問題。

示例場景

-- 創(chuàng)建帶外鍵的表結(jié)構(gòu)
CREATE TABLE department (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOTNULL
);

CREATE TABLE employee (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOTNULL,
    department_id INT,
    FOREIGN KEY (department_id) 
        REFERENCES department(id)
        ON DELETE CASCADE
        ON UPDATE CASCADE
);

-- 雷區(qū)1:級聯(lián)刪除導(dǎo)致意外數(shù)據(jù)丟失
-- 刪除部門時,所有相關(guān)員工也被刪除,可能不是期望的行為
DELETE FROM department WHERE id = 1; -- 部門1的所有員工都被刪除!

-- 雷區(qū)2:外鍵鎖競爭
-- 會話1:刪除部門
START TRANSACTION;
DELETE FROM department WHERE id = 1; -- 持有部門1的鎖

-- 會話2:在同一個部門插入員工(被阻塞)
START TRANSACTION;
INSERT INTO employee (name, department_id) VALUES ('新員工', 1); -- 等待鎖

-- 雷區(qū)3:數(shù)據(jù)遷移困難
-- 導(dǎo)入數(shù)據(jù)時必須按正確順序,否則外鍵約束失敗

深度剖析

有些小伙伴在工作中可能發(fā)現(xiàn),系統(tǒng)并發(fā)量上來后,經(jīng)常出現(xiàn)鎖等待超時,外鍵約束是常見原因之一。

外鍵的性能影響

  1. 鎖范圍擴大:操作父表時需要檢查子表,可能鎖定更多數(shù)據(jù)
  2. 死鎖風(fēng)險:多表之間的外鍵關(guān)系容易形成死鎖環(huán)路
  3. 并發(fā)下降:外鍵檢查需要額外加鎖,降低系統(tǒng)并發(fā)能力

級聯(lián)操作的風(fēng)險

  • ON DELETE CASCADE:誤刪父表記錄會導(dǎo)致大量子表數(shù)據(jù)丟失
  • ON UPDATE CASCADE:更新主鍵時傳播到所有子表,性能影響大

為了理解外鍵鎖的競爭關(guān)系,我畫了一個鎖等待示意圖:

圖片圖片

避坑指南

  • 高并發(fā)場景慎用外鍵,可在應(yīng)用層保證數(shù)據(jù)一致性
  • 如果使用外鍵,避免ON DELETE/UPDATE CASCADE
  • 使用軟刪除替代物理刪除
  • 批量操作時暫時禁用外鍵檢查

雷區(qū)六:連接池配置不當(dāng)

連接池配置看似簡單,實則影響整個系統(tǒng)的穩(wěn)定性和性能。

配置不當(dāng)會導(dǎo)致連接泄露、池化失效等問題。

為什么連接池如此關(guān)鍵?

數(shù)據(jù)庫連接是寶貴的資源,創(chuàng)建和銷毀成本很高。

連接池管理不當(dāng)會直接導(dǎo)致系統(tǒng)崩潰。

示例場景

// Spring Boot中的Druid連接池配置
@Configuration
public class DruidConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.druid")
    public DataSource dataSource() {
        return DruidDataSourceBuilder.create().build();
    }
}

// application.yml配置
spring:
  datasource:
    druid:
      # 雷區(qū)1:初始連接數(shù)過大,浪費資源
      initial-size: 50
      # 雷區(qū)2:最大連接數(shù)過小,并發(fā)時等待
      max-active: 20
      # 雷區(qū)3:最小空閑連接數(shù)不合理
      min-idle: 5
      # 雷區(qū)4:獲取連接超時時間過短
      max-wait: 3000
      # 雷區(qū)5:沒有配置連接有效性檢查
      validation-query: SELECT 1
      test-on-borrow: true
      test-on-return: false
      test-while-idle: true
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000

深度剖析

有些小伙伴在工作中可能遇到過連接池耗盡、連接泄露等問題,這都是配置不當(dāng)導(dǎo)致的。

連接池的核心參數(shù)

  • initial-size:初始連接數(shù),不宜過大,避免啟動時占用過多資源
  • max-active:最大連接數(shù),根據(jù)數(shù)據(jù)庫和服務(wù)器的處理能力設(shè)置
  • min-idle:最小空閑連接,保持一定的預(yù)熱連接
  • max-wait:獲取連接超時時間,避免線程長時間阻塞

連接泄露的檢測與預(yù)防

// 常見的連接泄露模式
public class UserService {
    
    // 錯誤寫法:連接未關(guān)閉
    public User getUser(int id) {
        Connection conn = dataSource.getConnection();
        // 執(zhí)行查詢...
        // 忘記調(diào)用conn.close()
        return user;
    }
    
    // 正確寫法:使用try-with-resources
    public User getUserCorrect(int id) {
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?")) {
            
            stmt.setInt(1, id);
            ResultSet rs = stmt.executeQuery();
            // 處理結(jié)果...
            return user;
        } catch (SQLException e) {
            thrownew RuntimeException(e);
        }
    }
}

為了理解連接池的工作機制,我畫了一個連接池狀態(tài)轉(zhuǎn)換圖:

圖片圖片

避坑指南

  • 根據(jù)業(yè)務(wù)壓力合理配置連接池參數(shù)
  • 使用try-with-resources確保連接關(guān)閉
  • 開啟連接泄露檢測功能
  • 監(jiān)控連接池狀態(tài),設(shè)置合理的告警閾值

總結(jié)

經(jīng)過以上6大雷區(qū)的分析,相信你對MySQL的常見坑點有了更深入的理解。

雷區(qū)對比總結(jié)

雷區(qū)

核心問題

影響范圍

解決思路

索引失效

查詢寫法不當(dāng)

查詢性能

避免函數(shù)操作、類型轉(zhuǎn)換

事務(wù)幻讀

隔離級別理解不足

數(shù)據(jù)一致性

合理選擇隔離級別、使用間隙鎖

分頁性能

OFFSET過大

用戶體驗

使用游標(biāo)分頁、子查詢優(yōu)化

字符集問題

配置不當(dāng)

數(shù)據(jù)存儲、排序

統(tǒng)一使用utf8mb4、正確配置collation

外鍵約束

級聯(lián)操作、鎖競爭

系統(tǒng)性能、數(shù)據(jù)安全

應(yīng)用層約束、慎用級聯(lián)

連接池配置

參數(shù)不合理、連接泄露

系統(tǒng)穩(wěn)定性

合理配置、監(jiān)控告警

有些小伙伴在工作中,可能一開始覺得這些問題很復(fù)雜,但只要掌握了底層原理,就能在設(shè)計和開發(fā)階段主動避免。

記住,數(shù)據(jù)庫是系統(tǒng)的核心,它的穩(wěn)定性直接影響整個業(yè)務(wù)。

責(zé)任編輯:武曉燕 來源: 蘇三說技術(shù)
相關(guān)推薦

2021-10-15 06:49:37

MySQL

2021-09-25 13:05:10

MYSQL開發(fā)數(shù)據(jù)庫

2025-08-05 07:39:09

2022-07-15 08:20:54

Java基礎(chǔ)知識

2020-10-10 17:34:11

大數(shù)據(jù)IT技術(shù)

2025-09-02 07:39:16

2024-10-22 14:42:14

2022-01-23 10:44:39

零信任網(wǎng)絡(luò)安全網(wǎng)絡(luò)攻擊

2023-08-31 22:12:51

低代碼隱患技術(shù)

2013-08-23 10:42:03

Hadoop

2021-08-12 14:31:52

邊緣計算云計算數(shù)據(jù)

2009-08-25 09:29:18

維護代碼

2010-03-11 16:42:31

Python語言開發(fā)

2011-03-16 10:44:19

2010-09-09 10:54:58

2023-07-24 11:01:32

2016-07-06 11:16:47

2013-08-23 10:18:06

Hadoop

2023-05-11 11:36:56

云計算云供應(yīng)商

2025-04-02 00:00:04

點贊
收藏

51CTO技術(shù)棧公眾號