最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL中SQL查詢速度優(yōu)化的20個(gè)技巧分享

 更新時(shí)間:2025年10月28日 10:10:25   作者:劉大華  
這篇文章和大家系統(tǒng)分享了20種SQL優(yōu)化實(shí)用技巧,涵蓋查詢,索引,設(shè)計(jì)及工具使用,通過(guò)真實(shí)案例解析,幫助開(kāi)發(fā)者提升數(shù)據(jù)庫(kù)性能,告別慢查詢

前言

為什么SQL需要優(yōu)化?

舉個(gè)例子:公司有個(gè)報(bào)表系統(tǒng),每天上午9點(diǎn)都準(zhǔn)時(shí)卡頓,查詢一個(gè)數(shù)據(jù)要等半分多鐘。用戶一直抱怨不停。

后來(lái)分析才發(fā)現(xiàn)是一條SQL語(yǔ)句沒(méi)走索引,全表掃描了上百萬(wàn)條數(shù)據(jù)。優(yōu)化后,查詢時(shí)間從30秒降到了0.1秒!

為什么會(huì)這樣?

假如把數(shù)據(jù)庫(kù)比作圖書(shū)館,那么SQL語(yǔ)句就是找書(shū)的指令。

如果你說(shuō)"給我一本小說(shuō)",管理員得去翻遍整個(gè)圖書(shū)館;但如果你說(shuō)"給我編號(hào)A123架第4層的小說(shuō)",管理員很快就能找到。這個(gè)編號(hào)就相當(dāng)于數(shù)據(jù)庫(kù)的索引。

下面分享20種優(yōu)化方案!

一、基礎(chǔ)優(yōu)化篇

1. 只查詢需要的字段:告別SELECT *

錯(cuò)誤示范:

SELECT * FROM users WHERE status = 1;

問(wèn)題分析:

  • 查詢所有字段,包括大文本字段
  • 網(wǎng)絡(luò)傳輸數(shù)據(jù)量大
  • 內(nèi)存占用高

正確做法:

SELECT id, name, email, status FROM users WHERE status = 1;

場(chǎng)景舉例: 用戶表有20個(gè)字段,但列表頁(yè)只需要顯示4個(gè)字段。使用SELECT *比指定字段慢3倍!

2. EXISTS vs IN:根據(jù)數(shù)據(jù)量選擇

傳統(tǒng)認(rèn)知: EXISTSIN

實(shí)際情況: 需要看子查詢數(shù)據(jù)量

小數(shù)據(jù)量場(chǎng)景(子查詢結(jié)果<1000條):

-- 兩種方式性能相當(dāng)
SELECT * FROM orders 
WHERE user_id IN (SELECT id FROM users WHERE vip_level > 3);

SELECT * FROM orders o 
WHERE EXISTS (SELECT 1 FROM users u WHERE u.id = o.user_id AND u.vip_level > 3);

大數(shù)據(jù)量場(chǎng)景(子查詢結(jié)果>10000條):

-- EXISTS通常更優(yōu)
SELECT * FROM large_table t1
WHERE EXISTS (SELECT 1 FROM large_table t2 WHERE t2.parent_id = t1.id);

3. 避免WHERE子句中的函數(shù)計(jì)算

錯(cuò)誤示范:

-- 索引失效!
SELECT * FROM orders WHERE DATE_FORMAT(create_time, '%Y-%m-%d') = '2024-01-01';
SELECT * FROM products WHERE LOWER(name) = 'iphone';

正確做法:

-- 使用范圍查詢
SELECT * FROM orders 
WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';

-- 保持字段原樣查詢  
SELECT * FROM products WHERE name = 'iPhone';

原理: 對(duì)索引字段使用函數(shù)會(huì)使索引失效,變成全表掃描。

4. UNION ALL vs UNION:明確是否需要去重

需要去重:

-- 性能較差,但結(jié)果準(zhǔn)確
SELECT city FROM customers
UNION
SELECT city FROM suppliers;

不需要去重:

-- 性能更好
SELECT city FROM customers
UNION ALL
SELECT city FROM suppliers;

性能對(duì)比: 在100萬(wàn)數(shù)據(jù)量下,UNION ALL比UNION快5-8倍

二、索引優(yōu)化篇

5. 為高頻查詢條件建立索引

場(chǎng)景分析:

-- 高頻查詢1:按狀態(tài)查詢
SELECT * FROM orders WHERE status = 'pending';

-- 高頻查詢2:按用戶+時(shí)間查詢  
SELECT * FROM orders WHERE user_id = 123 AND create_time > '2024-01-01';

-- 索引方案
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_user_time ON orders(user_id, create_time);

6. 掌握最左前綴原則

復(fù)合索引: (status, create_time, user_id)

有效使用索引的查詢:

WHERE status = 'pending'  -- 使用索引
WHERE status = 'pending' AND create_time > '2024-01-01'  -- 使用索引
WHERE status = 'pending' AND create_time > '2024-01-01' AND user_id = 123  -- 使用索引

索引失效的查詢:

WHERE create_time > '2024-01-01'  -- 索引失效!
WHERE user_id = 123  -- 索引失效!
WHERE status = 'pending' AND user_id = 123  -- 部分使用索引

7. 避免索引列參與計(jì)算

錯(cuò)誤示范:

-- 索引失效的寫(xiě)法
SELECT * FROM products WHERE price + 100 > 500;
SELECT * FROM users WHERE YEAR(create_time) = 2024;

正確做法:

-- 優(yōu)化后的寫(xiě)法
SELECT * FROM products WHERE price > 400;
SELECT * FROM users WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01';

8. 索引不是越多越好:平衡讀寫(xiě)性能

索引的代價(jià):

  • 寫(xiě)操作變慢:每次INSERT/UPDATE/DELETE都要更新索引
  • 存儲(chǔ)空間增加:索引占用額外磁盤空間
  • 選擇困難:過(guò)多索引讓優(yōu)化器難以選擇

建議:

  • 單表索引數(shù)量控制在3-5個(gè)以內(nèi)
  • 優(yōu)先為高頻查詢WHERE條件建立索引
  • 定期清理未使用的索引

三、高級(jí)技巧篇

9. 深度分頁(yè)優(yōu)化:告別LIMIT偏移量

傳統(tǒng)分頁(yè)的問(wèn)題:

-- 越往后越慢!
SELECT * FROM orders ORDER BY id LIMIT 100000, 20;

需要先掃描100000條記錄,再取20條。

優(yōu)化方案:

-- 使用游標(biāo)分頁(yè)
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 20;

-- 或者記錄上次查詢的最大ID
SELECT * FROM orders WHERE id > last_max_id ORDER BY id LIMIT 20;

性能對(duì)比:

  • LIMIT 1000,20: 0.01s
  • LIMIT 100000,20: 2.3s
  • WHERE id > 100000 LIMIT 20: 0.01s

10. 批量操作:大幅減少IO次數(shù)

錯(cuò)誤示范(Java示例):

for (User user : userList) {
    String sql = "INSERT INTO users(name, age) VALUES(?, ?)";
    // 每次插入都產(chǎn)生網(wǎng)絡(luò)IO和事務(wù)開(kāi)銷
}

正確做法:

-- 一次批量插入
INSERT INTO users(name, age) 
VALUES('張三', 25), ('李四', 30), ('王五', 28);

性能提升: 插入1000條數(shù)據(jù),批量操作比單條插入快50倍

11. JOIN優(yōu)化:理解執(zhí)行計(jì)劃

需要優(yōu)化的子查詢:

SELECT * FROM products 
WHERE category_id IN (
    SELECT id FROM categories WHERE type = 'electronic'
);

優(yōu)化為JOIN:

SELECT p.* FROM products p 
INNER JOIN categories c ON p.category_id = c.id 
WHERE c.type = 'electronic';

進(jìn)階技巧: 使用STRAIGHT_JOIN指導(dǎo)優(yōu)化器

SELECT p.* FROM products p 
STRAIGHT_JOIN categories c ON p.category_id = c.id 
WHERE c.type = 'electronic';

12. 覆蓋索引:避免回表查詢

什么是回表查詢?

-- 假設(shè)在age字段有索引
SELECT name FROM users WHERE age > 18;

需要先查索引找到主鍵,再用主鍵查數(shù)據(jù)行。

覆蓋索引解決方案:

-- 建立復(fù)合索引
CREATE INDEX idx_users_age_name ON users(age, name);

-- 現(xiàn)在查詢直接在索引中完成
SELECT name FROM users WHERE age > 18;

性能提升: 減少一次磁盤IO,性能提升30%-50%。

四、設(shè)計(jì)優(yōu)化篇

13. 選擇合適的數(shù)據(jù)類型

常見(jiàn)誤區(qū):

-- 錯(cuò)誤選擇
CREATE TABLE users (
    id VARCHAR(50),  -- 應(yīng)該用INT/BIGINT
    age VARCHAR(10), -- 應(yīng)該用TINYINT
    create_time VARCHAR(20) -- 應(yīng)該用DATETIME
);

優(yōu)化方案:

-- 正確選擇
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT,
    age TINYINT UNSIGNED,
    create_time DATETIME,
    PRIMARY KEY(id)
);

14. 謹(jǐn)慎使用NULL值

NULL值的問(wèn)題:

-- 查詢變得復(fù)雜
SELECT * FROM users WHERE phone IS NULL;
SELECT * FROM users WHERE phone IS NOT NULL;

-- 聚合函數(shù)忽略NULL
SELECT AVG(age) FROM users; -- 忽略NULL值

解決方案:

-- 設(shè)置默認(rèn)值
CREATE TABLE users (
    phone VARCHAR(20) NOT NULL DEFAULT '',
    age INT NOT NULL DEFAULT 0
);

15. 反規(guī)范化:用空間換時(shí)間

規(guī)范化設(shè)計(jì)(3NF):

-- 多表關(guān)聯(lián)查詢
SELECT u.name, o.order_no, p.product_name
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE u.id = 123;

反規(guī)范化設(shè)計(jì):

-- 單表查詢(在orders表中冗余用戶和商品信息)
SELECT order_no, user_name, product_name 
FROM orders 
WHERE user_id = 123;

適用場(chǎng)景:

  • 讀多寫(xiě)少的業(yè)務(wù)
  • 報(bào)表統(tǒng)計(jì)類查詢
  • 需要極致性能的場(chǎng)景

五、實(shí)戰(zhàn)案例篇

16. 案例:電商訂單查詢優(yōu)化

原始慢查詢(執(zhí)行時(shí)間:2.3s):

SELECT * FROM orders 
WHERE user_id = 123 
AND status IN ('paid', 'shipped') 
AND create_time BETWEEN '2024-01-01' AND '2024-06-30'
ORDER BY create_time DESC;

優(yōu)化步驟:

步驟1:分析執(zhí)行計(jì)劃

EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status IN ('paid', 'shipped');

步驟2:創(chuàng)建復(fù)合索引

CREATE INDEX idx_orders_user_status_time ON orders(user_id, status, create_time);

步驟3:優(yōu)化查詢語(yǔ)句

SELECT order_id, user_id, amount, status, create_time 
FROM orders 
WHERE user_id = 123 
AND status IN ('paid', 'shipped') 
AND create_time >= '2024-01-01' 
AND create_time < '2024-07-01'  -- 避免BETWEEN
ORDER BY create_time DESC;

優(yōu)化結(jié)果: 2.3s → 0.02s

17. 案例:報(bào)表統(tǒng)計(jì)優(yōu)化

原始查詢(全表掃描):

-- 每天執(zhí)行一次,但需要30秒
SELECT COUNT(*) as total_orders,
       SUM(amount) as total_amount,
       AVG(amount) as avg_amount
FROM orders 
WHERE DATE(create_time) = CURDATE();

優(yōu)化方案:

方案1:使用范圍查詢

SELECT COUNT(*) as total_orders,
       SUM(amount) as total_amount, 
       AVG(amount) as avg_amount
FROM orders 
WHERE create_time >= DATE(CURDATE()) 
AND create_time < DATE(CURDATE()) + INTERVAL 1 DAY;

方案2:建立匯總表

-- 每日預(yù)聚合
CREATE TABLE order_daily_stats (
    stat_date DATE,
    total_orders INT,
    total_amount DECIMAL(15,2),
    avg_amount DECIMAL(10,2),
    PRIMARY KEY(stat_date)
);

-- 查詢時(shí)直接查匯總表
SELECT * FROM order_daily_stats WHERE stat_date = CURDATE();

六、工具使用篇

18. 深入理解EXPLAIN執(zhí)行計(jì)劃

關(guān)鍵指標(biāo)解讀:

EXPLAIN SELECT * FROM users WHERE age > 18;

重點(diǎn)關(guān)注:

  • type:ALL(全表掃描) → index → range → ref → eq_ref → const
  • key:實(shí)際使用的索引
  • rows:預(yù)估掃描行數(shù)
  • Extra:Using filesort(需要優(yōu)化), Using temporary(需要優(yōu)化)

19. 配置慢查詢?nèi)罩?/h3>

MySQL配置:

# 開(kāi)啟慢查詢?nèi)罩?
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1  # 超過(guò)1秒的記錄
log_queries_not_using_indexes = 1  # 記錄未使用索引的查詢

分析慢查詢?nèi)罩荆?/strong>

# 使用mysqldumpslow分析
mysqldumpslow -t 10 /var/log/mysql/slow.log

# 使用pt-query-digest分析  
pt-query-digest /var/log/mysql/slow.log

20. 數(shù)據(jù)庫(kù)維護(hù):定期健康檢查

日常維護(hù)命令:

-- 更新索引統(tǒng)計(jì)信息
ANALYZE TABLE users, orders, products;

-- 整理表碎片(每月一次)
OPTIMIZE TABLE large_table;

-- 檢查未使用索引
SELECT * FROM sys.schema_unused_indexes;

自動(dòng)化腳本:

-- 每周執(zhí)行一次的健康檢查
CHECK TABLE important_table;
ANALYZE TABLE important_table;

總結(jié)

SQL優(yōu)化不是一蹴而就的,需要持續(xù)觀察、分析和調(diào)整。索引是利器,但同時(shí)也要用對(duì)地方。

到此這篇關(guān)于MySQL中SQL查詢速度優(yōu)化的20個(gè)技巧分享的文章就介紹到這了,更多相關(guān)MySQL SQL優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL字符串轉(zhuǎn)數(shù)字的3種方式實(shí)例

    MySQL字符串轉(zhuǎn)數(shù)字的3種方式實(shí)例

    這篇文章主要給大家介紹了關(guān)于MySQL字符串轉(zhuǎn)數(shù)字的3種方式,在使用mysql中經(jīng)常遇到要將字符串?dāng)?shù)字轉(zhuǎn)換成可計(jì)算數(shù)字,文中給出了詳細(xì)的代碼示例和圖文介紹,需要的朋友可以參考下
    2023-08-08
  • 詳解Mysql取前一天、前一周、后一天等時(shí)間函數(shù)

    詳解Mysql取前一天、前一周、后一天等時(shí)間函數(shù)

    本文給大家介紹Mysql取前一天、前一周、后一天等時(shí)間函數(shù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-11-11
  • MYSQL LAG()與LEAD()的區(qū)別

    MYSQL LAG()與LEAD()的區(qū)別

    MYSQL LAG()與LEAD()這兩個(gè)函數(shù)是偏移量函數(shù),可以查出一個(gè)字段的前面N個(gè)值或者后面N個(gè)值,本文詳細(xì)的介紹一下這兩個(gè)函數(shù)的區(qū)別,感興趣的可以了解一下
    2023-05-05
  • 詳解MySQL:數(shù)據(jù)完整性

    詳解MySQL:數(shù)據(jù)完整性

    這篇文章主要介紹了MySQL數(shù)據(jù)完整性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • MySQL 配置文件 my.cnf / my.ini 區(qū)別解析

    MySQL 配置文件 my.cnf / my.ini 區(qū)別解析

    充分理解 MySQL 配置文件中各個(gè)變量的意義對(duì)我們有針對(duì)性的優(yōu)化 MySQL 數(shù)據(jù)庫(kù)性能有非常大的意義,這篇文章主要介紹了MySQL 配置文件 my.cnf / my.ini 區(qū)別,需要的朋友可以參考下
    2022-11-11
  • MySQL數(shù)據(jù)庫(kù)分區(qū)概念及使用

    MySQL數(shù)據(jù)庫(kù)分區(qū)概念及使用

    本文主要介紹了數(shù)據(jù)庫(kù)分區(qū)的基本概念,分區(qū)類型以及如何在MySQL中實(shí)現(xiàn)分區(qū),可以提高查詢性能和管理效率,實(shí)現(xiàn)分區(qū)需要根據(jù)具體的業(yè)務(wù)需求選擇合適的分區(qū)類型,感興趣的可以了解一下
    2024-10-10
  • mysql報(bào)錯(cuò):Deadlock found when trying to get lock; try restarting transaction的解決方法

    mysql報(bào)錯(cuò):Deadlock found when trying to get lock; try restarti

    這篇文章主要給大家介紹了關(guān)于mysql出現(xiàn)報(bào)錯(cuò):Deadlock found when trying to get lock; try restarting transaction的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-07-07
  • mysql按逗號(hào)分割的實(shí)現(xiàn)

    mysql按逗號(hào)分割的實(shí)現(xiàn)

    在MySQL中,我們經(jīng)常需要對(duì)數(shù)據(jù)進(jìn)行拆分和處理,其中一個(gè)常見(jiàn)需求就是按逗號(hào)分割字符串,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • MySQL limit分頁(yè)大偏移量慢的原因及優(yōu)化方案

    MySQL limit分頁(yè)大偏移量慢的原因及優(yōu)化方案

    這篇文章主要介紹了MySQL limit分頁(yè)大偏移量慢的原因及優(yōu)化方案,幫助大家更好的理解和使用MySQL數(shù)據(jù)庫(kù),感興趣的朋友可以了解下
    2020-11-11
  • MySQL函數(shù)find_in_set場(chǎng)景介紹

    MySQL函數(shù)find_in_set場(chǎng)景介紹

    本文給大家分享MySQL函數(shù)find_in_set場(chǎng)景介紹,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2025-07-07

最新評(píng)論

遵义市| 吉安县| 河曲县| 临泽县| 南平市| 定边县| 新疆| 哈尔滨市| 淮滨县| 英超| 花莲县| 灵石县| 西城区| 弋阳县| 肥西县| 荆州市| 尚志市| 那坡县| 灵寿县| 福鼎市| 罗源县| 黄山市| 涟源市| 德兴市| 无极县| 八宿县| 临清市| 平潭县| 中超| 观塘区| 柏乡县| 专栏| 股票| 汤原县| 稷山县| 全州县| 河东区| 沁阳市| 调兵山市| 黄浦区| 双牌县|