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

MySQL 用了索引還是很慢的原因分析及解決方案

 更新時間:2026年05月20日 09:21:14   作者:Java  
本文給大家分享即使查詢使用了索引,仍然可能很慢,主要原因包括索引設(shè)計問題、SQL寫法問題、數(shù)據(jù)分布問題、表/O開銷大、數(shù)據(jù)庫配置問題和硬件資源瓶頸等,感興趣的朋友跟隨小編一起看看吧

即使查詢使用了索引,仍然可能很慢,主要原因可以歸納為 6 大類:

類別具體原因影響
索引設(shè)計問題索引選擇性差、索引列順序不當、索引冗余掃描行數(shù)過多
SQL 寫法問題SELECT *、LIKE '%xxx'、函數(shù)操作、類型轉(zhuǎn)換索引失效或回表過多
數(shù)據(jù)分布問題數(shù)據(jù)量過大、數(shù)據(jù)傾斜嚴重、熱點數(shù)據(jù)集中即使走索引也慢
表結(jié)構(gòu)問題字段過大、行過長、未分區(qū)I/O 開銷大
數(shù)據(jù)庫配置問題緩沖池過小、連接數(shù)不足、參數(shù)配置不當整體性能下降
硬件資源瓶頸磁盤 I/O、內(nèi)存不足、CPU 瓶頸物理層面慢

一句話總結(jié):索引只是加速查詢的必要條件而非充分條件,需要結(jié)合索引設(shè)計、SQL 優(yōu)化、表結(jié)構(gòu)設(shè)計、數(shù)據(jù)庫配置和硬件資源進行系統(tǒng)性優(yōu)化。

一、索引設(shè)計問題

1. 索引選擇性差

索引選擇性對查詢性能的影響。選擇性越高,索引的效果越好;選擇性越低,索引的效果越差。

  • 高選擇性:例如用戶表的 user_id 字段,100 萬用戶有 100 萬個不同的 user_id,選擇性為 1.0,查詢時可以快速定位到唯一行。
  • 低選擇性:例如性別字段 gender,只有 "男" 和 "女" 兩個值,100 萬行數(shù)據(jù)的選擇性僅為 0.000002,查詢 "男" 性用戶時需要掃描約 50 萬行,數(shù)據(jù)庫優(yōu)化器可能直接選擇全表掃描。

判斷標準:一般建議索引選擇性大于 0.1,可以通過以下 SQL 計算:

?
-- 計算字段的選擇性
SELECT
    COUNT(DISTINCT column_name) / COUNT(*) AS selectivity
FROM table_name;
-- 示例:gender 字段的選擇性
SELECT
    COUNT(DISTINCT gender) / COUNT(*) AS selectivity
FROM users;
-- 結(jié)果:0.000002(非常低,不適合單獨建索引)
?

2. 索引列順序不當(最左前綴原則)

-- 創(chuàng)建聯(lián)合索引
CREATE INDEX idx_name_age_gender ON users(name, age, gender);
-- ? 走索引:符合最左前綴
SELECT * FROM users WHERE name = '張三';
SELECT * FROM users WHERE name = '張三' AND age = 25;
SELECT * FROM users WHERE name = '張三' AND age = 25 AND gender = '男';
-- ? 不走索引:違反最左前綴
SELECT * FROM users WHERE age = 25;              -- 缺少 name
SELECT * FROM users WHERE gender = '男';         -- 缺少 name 和 age
SELECT * FROM users WHERE age = 25 AND gender = '男'; -- 缺少 name
-- ?? 部分走索引:只有 name 走索引,后面的列無法利用索引排序
SELECT * FROM users WHERE name = '張三' AND gender = '男'; -- age 跳過了

核心原則:聯(lián)合索引要遵循 "最左前綴原則",索引列的順序非常重要。將區(qū)分度高、經(jīng)常用于查詢條件的列放在左邊。

3. 索引冗余

-- ? 冗余索引示例
CREATE INDEX idx_name ON users(name);                    -- 單列索引
CREATE INDEX idx_name_age ON users(name, age);           -- 聯(lián)合索引
-- idx_name 是冗余的,因為 idx_name_age 可以覆蓋 name 單列查詢
-- ? 優(yōu)化后:刪除冗余索引
DROP INDEX idx_name ON users;
-- 只保留聯(lián)合索引 idx_name_age

二、SQL 寫法問題

1.SELECT *導(dǎo)致大量回表

 SELECT * 查詢的回表過程。如果查詢只需要部分字段,但使用了 SELECT *,就會導(dǎo)致不必要的回表操作,嚴重影響性能。

  • 回表過程:先在二級索引(如 name 索引)中找到滿足條件的主鍵 ID,然后根據(jù)主鍵 ID 到聚簇索引中查找完整的行數(shù)據(jù)。
  • 性能影響:如果查詢返回 1000 行數(shù)據(jù),就需要進行 1000 次回表操作,每次回表都是一次隨機 I/O,性能開銷巨大。

優(yōu)化方案:使用覆蓋索引,只查詢索引列,避免回表。

-- ? 慢查詢:需要回表
SELECT * FROM users WHERE name = '張三';
-- ? 優(yōu)化:使用覆蓋索引,無需回表
-- 假設(shè)有索引 idx_name_age(name, age)
SELECT name, age FROM users WHERE name = '張三';

2.LIKE查詢導(dǎo)致索引失效

-- ? 走索引:前綴匹配
SELECT * FROM users WHERE name LIKE '張%';
-- ? 不走索引:后綴匹配或包含匹配
SELECT * FROM users WHERE name LIKE '%張';    -- 后綴匹配
SELECT * FROM users WHERE name LIKE '%張%';   -- 包含匹配
-- ? 優(yōu)化方案 1:使用覆蓋索引
-- 即使 LIKE '%張%' 不走索引,如果只查詢索引列,可能走索引掃描
SELECT name FROM users WHERE name LIKE '%張%';
-- ? 優(yōu)化方案 2:使用全文索引
ALTER TABLE users ADD FULLTEXT INDEX ft_name(name);
SELECT * FROM users WHERE MATCH(name) AGAINST('張');
-- ? 優(yōu)化方案 3:使用搜索引擎(如 Elasticsearch)

3. 對索引列使用函數(shù)或計算

-- ? 不走索引:對索引列使用函數(shù)
SELECT * FROM users WHERE DATE(create_time) = '2024-01-01';
SELECT * FROM users WHERE YEAR(create_time) = 2024;
SELECT * FROM users WHERE SUBSTRING(name, 1, 1) = '張';
-- ? 走索引:使用范圍查詢或等值查詢
SELECT * FROM users WHERE create_time >= '2024-01-01'
                       AND create_time < '2024-01-02';
-- ? 不走索引:索引列參與計算
SELECT * FROM users WHERE age + 1 = 26;
-- ? 走索引:調(diào)整計算方式
SELECT * FROM users WHERE age = 25;

4. 隱式類型轉(zhuǎn)換

-- 假設(shè) user_id 是 VARCHAR 類型
CREATE INDEX idx_user_id ON users(user_id);
-- ? 不走索引:字符串與數(shù)字比較,發(fā)生隱式類型轉(zhuǎn)換
SELECT * FROM users WHERE user_id = 123;
-- 等價于:SELECT * FROM users WHERE CAST(user_id AS SIGNED) = 123;
-- ? 走索引:類型一致
SELECT * FROM users WHERE user_id = '123';

核心原則:索引列的類型必須與查詢條件的類型完全一致,否則會發(fā)生隱式類型轉(zhuǎn)換,導(dǎo)致索引失效。

三、數(shù)據(jù)分布問題

1. 數(shù)據(jù)量過大

-- 查詢表的總行數(shù)和大小
SELECT
    table_name,
    table_rows,
    ROUND(data_length / 1024 / 1024, 2) AS data_size_mb,
    ROUND(index_length / 1024 / 1024, 2) AS index_size_mb
FROM information_schema.tables
WHERE table_schema = 'your_database';

優(yōu)化方案:

  • 分區(qū)表:按時間或范圍分區(qū),減少單次查詢掃描的數(shù)據(jù)量
  • 分表分庫:將大表拆分為多個小表
  • 歸檔歷史數(shù)據(jù):將歷史數(shù)據(jù)遷移到歸檔表
  • 使用覆蓋索引:減少回表次數(shù)

2. 數(shù)據(jù)傾斜嚴重

-- 查看數(shù)據(jù)分布
SELECT gender, COUNT(*) as count
FROM users
GROUP BY gender;
-- 假設(shè)結(jié)果:
-- gender | count
-- -------|--------
-- 男     | 999000
-- 女     |   1000
-- 其他   |      0
-- 查詢 "男" 性用戶時,即使有索引,也可能走全表掃描
-- 因為優(yōu)化器認為掃描 999000 行和全表掃描差不多

優(yōu)化方案:

  • 對于極端傾斜的數(shù)據(jù),考慮不建索引或使用復(fù)合索引
  • 使用 FORCE INDEX 強制走索引(謹慎使用)
-- 強制使用索引
SELECT * FROM users FORCE INDEX(idx_gender) WHERE gender = '男';

四、表結(jié)構(gòu)問題

1. 字段過大

-- ? 慢查詢:大字段導(dǎo)致行過長
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(255),
    content TEXT,           -- 大字段
    author VARCHAR(100),
    create_time DATETIME
);
-- ? 優(yōu)化:將大字段拆分到單獨的表
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(255),
    author VARCHAR(100),
    create_time DATETIME
);
CREATE TABLE article_contents (
    article_id INT PRIMARY KEY,
    content LONGTEXT,
    FOREIGN KEY (article_id) REFERENCES articles(id)
);

2. 未使用合適的字段類型

-- ? 不合理:使用 VARCHAR 存儲 IP 地址
CREATE TABLE logs (
    id INT PRIMARY KEY,
    ip VARCHAR(15)
);
-- ? 優(yōu)化:使用 INT UNSIGNED 存儲 IP 地址
CREATE TABLE logs (
    id INT PRIMARY KEY,
    ip INT UNSIGNED
);
-- 插入時轉(zhuǎn)換
INSERT INTO logs (ip) VALUES (INET_ATON('192.168.1.1'));
-- 查詢時轉(zhuǎn)換
SELECT INET_NTOA(ip) FROM logs;

五、數(shù)據(jù)庫配置問題

1. 緩沖池配置不當

-- 查看當前 InnoDB 緩沖池大小
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 建議設(shè)置為服務(wù)器內(nèi)存的 70%-80%
-- 例如:16GB 內(nèi)存的服務(wù)器,設(shè)置為 12GB
SET GLOBAL innodb_buffer_pool_size = 12884901888;  -- 12GB

2. 其他重要參數(shù)

-- 查看當前配置
SHOW VARIABLES LIKE 'innodb_io_capacity';        -- 磁盤 I/O 能力
SHOW VARIABLES LIKE 'innodb_read_io_threads';    -- 讀線程數(shù)
SHOW VARIABLES LIKE 'innodb_write_io_threads';   -- 寫線程數(shù)
SHOW VARIABLES LIKE 'max_connections';           -- 最大連接數(shù)
-- 根據(jù)服務(wù)器配置調(diào)整
-- SSD 硬盤可以設(shè)置更高的 innodb_io_capacity

六、使用 EXPLAIN 分析執(zhí)行計劃

-- 查看執(zhí)行計劃
EXPLAIN SELECT * FROM users WHERE name = '張三';
-- 關(guān)鍵字段解讀
-- id: 查詢標識符
-- select_type: 查詢類型(SIMPLE, PRIMARY, SUBQUERY 等)
-- table: 訪問的表
-- type: 訪問類型(從好到差:system > const > eq_ref > ref > range > index > ALL)
-- possible_keys: 可能使用的索引
-- key: 實際使用的索引
-- key_len: 使用的索引長度
-- rows: 預(yù)估掃描的行數(shù)
-- Extra: 額外信息(Using index, Using where, Using filesort 等)

重點關(guān)注:

  • type 字段:如果是 ALL,說明是全表掃描;如果是 index,說明是索引掃描
  • rows 字段:預(yù)估掃描的行數(shù),越大越慢
  • Extra 字段:Using filesort(文件排序)、Using temporary(臨時表)都是性能殺手

到此這篇關(guān)于MySQL 用了索引還是很慢,可能是什么原因?的文章就介紹到這了,更多相關(guān)mysql索引還是很慢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

平安县| 南华县| 玛多县| 白城市| 新源县| 乌审旗| 宁强县| 忻城县| 罗源县| 淳化县| 武功县| 九龙坡区| 嘉禾县| 铜山县| 桐梓县| 宜兴市| 淳化县| 乐平市| 克东县| 尼勒克县| 乌兰察布市| 宁河县| 渝北区| 天长市| 堆龙德庆县| 五台县| 贵溪市| 金阳县| 体育| 上杭县| 彩票| 安溪县| 大新县| 华亭县| 瑞安市| 施甸县| 武山县| 湖口县| 万盛区| 蒙山县| 赫章县|