MariaDB索引 #

一、索引概述 #

1.1 什么是索引 #

text
索引类比
├── 书籍目录:快速定位内容
├── 图书馆索引:快速找到书籍
└── 数据库索引:快速找到数据

索引特点
├── 提高查询速度
├── 降低磁盘IO
├── 占用存储空间
└── 降低写入速度

1.2 索引类型 #

text
MariaDB索引类型
├── 按结构分类
│   ├── B+Tree索引(默认)
│   ├── Hash索引(Memory引擎)
│   └── 全文索引(FULLTEXT)
├── 按功能分类
│   ├── 普通索引(INDEX)
│   ├── 唯一索引(UNIQUE)
│   ├── 主键索引(PRIMARY KEY)
│   └── 全文索引(FULLTEXT)
└── 按列数分类
    ├── 单列索引
    └── 复合索引

二、创建索引 #

2.1 创建表时创建索引 #

sql
-- 创建主键索引
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    email VARCHAR(100) UNIQUE,  -- 唯一索引
    INDEX idx_name (name)       -- 普通索引
);

-- 创建复合索引
CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT,
    status VARCHAR(20),
    created_at DATETIME,
    INDEX idx_user_status (user_id, status)
);

-- 创建全文索引
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(200),
    content TEXT,
    FULLTEXT INDEX ft_content (content)
);

2.2 使用CREATE INDEX #

sql
-- 创建普通索引
CREATE INDEX idx_name ON users(name);

-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);

-- 创建复合索引
CREATE INDEX idx_user_status_date ON orders(user_id, status, created_at);

-- 创建前缀索引
CREATE INDEX idx_name_prefix ON users(name(10));

-- 创建全文索引
CREATE FULLTEXT INDEX ft_content ON articles(content);

2.3 使用ALTER TABLE #

sql
-- 添加索引
ALTER TABLE users ADD INDEX idx_name (name);

-- 添加唯一索引
ALTER TABLE users ADD UNIQUE INDEX idx_email (email);

-- 添加主键
ALTER TABLE users ADD PRIMARY KEY (id);

-- 添加全文索引
ALTER TABLE articles ADD FULLTEXT INDEX ft_content (content);

三、索引类型详解 #

3.1 主键索引 #

sql
-- 主键索引特点:
-- 1. 唯一且非空
-- 2. 每个表只能有一个
-- 3. InnoDB的聚簇索引

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

-- 复合主键
CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id)
);

3.2 唯一索引 #

sql
-- 唯一索引特点:
-- 1. 列值必须唯一
-- 2. 允许NULL值(可以有多个NULL)

CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(100) UNIQUE
);

-- 或
CREATE UNIQUE INDEX idx_email ON users(email);

-- 复合唯一索引
CREATE UNIQUE INDEX idx_user_product ON user_favorites(user_id, product_id);

3.3 普通索引 #

sql
-- 普通索引特点:
-- 1. 最基本的索引类型
-- 2. 无唯一性约束

CREATE INDEX idx_name ON users(name);
CREATE INDEX idx_created_at ON orders(created_at);

3.4 复合索引 #

sql
-- 复合索引特点:
-- 1. 多列组成
-- 2. 遵循最左前缀原则

CREATE INDEX idx_user_status_date ON orders(user_id, status, created_at);

-- 有效使用:
SELECT * FROM orders WHERE user_id = 1;                    -- 使用索引
SELECT * FROM orders WHERE user_id = 1 AND status = 'paid'; -- 使用索引
SELECT * FROM orders WHERE user_id = 1 AND status = 'paid' AND created_at > '2024-01-01'; -- 使用索引

-- 无效使用:
SELECT * FROM orders WHERE status = 'paid';                -- 不使用索引
SELECT * FROM orders WHERE created_at > '2024-01-01';      -- 不使用索引
SELECT * FROM orders WHERE status = 'paid' AND created_at > '2024-01-01'; -- 不使用索引

3.5 前缀索引 #

sql
-- 前缀索引:只索引列的前N个字符
-- 适用于长字符串列

CREATE INDEX idx_email_prefix ON users(email(10));

-- 选择前缀长度
SELECT 
    COUNT(DISTINCT LEFT(email, 5)) / COUNT(*) AS selectivity_5,
    COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) AS selectivity_10,
    COUNT(DISTINCT LEFT(email, 15)) / COUNT(*) AS selectivity_15,
    COUNT(DISTINCT email) / COUNT(*) AS full_selectivity
FROM users;

-- 选择接近完整选择性的最短前缀

3.6 全文索引 #

sql
-- 全文索引:用于文本搜索
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(200),
    content TEXT,
    FULLTEXT INDEX ft_title_content (title, content)
);

-- 全文搜索
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('database');

-- 布尔模式
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('+database +mysql' IN BOOLEAN MODE);

-- 自然语言模式
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('database mysql' IN NATURAL LANGUAGE MODE);

-- 查询扩展
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('database' WITH QUERY EXPANSION);

四、查看索引 #

4.1 SHOW INDEX #

sql
-- 查看表索引
SHOW INDEX FROM users;

-- 结果说明
-- Table: 表名
-- Non_unique: 是否非唯一(0=唯一,1=非唯一)
-- Key_name: 索引名
-- Seq_in_index: 索引中列的顺序
-- Column_name: 列名
-- Collation: 排序方式(A=升序)
-- Cardinality: 基数(估计值)
-- Sub_part: 前缀长度
-- Null: 是否允许NULL
-- Index_type: 索引类型

4.2 information_schema #

sql
-- 从information_schema查询
SELECT 
    index_name,
    column_name,
    non_unique,
    seq_in_index,
    index_type
FROM information_schema.statistics
WHERE table_name = 'users'
ORDER BY index_name, seq_in_index;

五、删除索引 #

5.1 DROP INDEX #

sql
-- 删除索引
DROP INDEX idx_name ON users;

-- 删除主键
ALTER TABLE users DROP PRIMARY KEY;

-- 删除外键
ALTER TABLE orders DROP FOREIGN KEY fk_user;

5.2 ALTER TABLE #

sql
-- 删除索引
ALTER TABLE users DROP INDEX idx_name;

-- 删除主键
ALTER TABLE users DROP PRIMARY KEY;

六、索引优化 #

6.1 索引设计原则 #

text
索引设计原则
├── 选择性高的列
│   └── 基数/总行数 接近1
├── WHERE条件列
│   └── 经常用于过滤的列
├── JOIN连接列
│   └── 外键列
├── ORDER BY/GROUP BY列
│   └── 排序和分组列
├── 复合索引顺序
│   └── 高选择性列在前
└── 避免过度索引
    └── 索引有维护成本

6.2 使用EXPLAIN分析 #

sql
-- 查看执行计划
EXPLAIN SELECT * FROM users WHERE name = 'John';

-- 关键字段说明
-- id: 查询标识符
-- select_type: 查询类型
-- table: 表名
-- type: 访问类型(从好到差)
--   system > const > eq_ref > ref > range > index > ALL
-- possible_keys: 可能使用的索引
-- key: 实际使用的索引
-- key_len: 使用的索引长度
-- ref: 索引比较的列
-- rows: 预估扫描行数
-- Extra: 额外信息
--   Using index: 覆盖索引
--   Using where: WHERE过滤
--   Using filesort: 文件排序
--   Using temporary: 临时表

6.3 索引失效场景 #

sql
-- 1. 使用函数
SELECT * FROM users WHERE YEAR(created_at) = 2024;  -- 索引失效
SELECT * FROM users WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';  -- 使用索引

-- 2. 隐式类型转换
SELECT * FROM users WHERE phone = 13800138000;  -- phone是VARCHAR,索引失效
SELECT * FROM users WHERE phone = '13800138000';  -- 使用索引

-- 3. LIKE以通配符开头
SELECT * FROM users WHERE name LIKE '%John%';  -- 索引失效
SELECT * FROM users WHERE name LIKE 'John%';   -- 使用索引

-- 4. OR条件
SELECT * FROM users WHERE name = 'John' OR age = 25;  -- 可能索引失效
-- 优化:使用UNION
SELECT * FROM users WHERE name = 'John'
UNION
SELECT * FROM users WHERE age = 25;

-- 5. 复合索引不满足最左前缀
-- 索引: (a, b, c)
SELECT * FROM t WHERE b = 1;  -- 索引失效
SELECT * FROM t WHERE a = 1;  -- 使用索引

-- 6. 使用!=或<>
SELECT * FROM users WHERE status != 1;  -- 可能索引失效

-- 7. IS NULL / IS NOT NULL
SELECT * FROM users WHERE name IS NULL;  -- 可能索引失效

6.4 覆盖索引 #

sql
-- 覆盖索引:查询的列都在索引中
-- 不需要回表,性能更好

-- 创建索引
CREATE INDEX idx_user_status_name ON users(status, name);

-- 使用覆盖索引
SELECT name FROM users WHERE status = 1;  -- Using index

-- 不使用覆盖索引
SELECT name, email FROM users WHERE status = 1;  -- 需要回表

6.5 索引下推 #

sql
-- 索引下推(Index Condition Pushdown, ICP)
-- MariaDB 5.3+ 支持

-- 索引: (name, age)
SELECT * FROM users WHERE name LIKE 'J%' AND age > 25;

-- 无ICP:存储引擎只根据name过滤,再由服务器层过滤age
-- 有ICP:存储引擎直接根据name和age过滤

七、索引维护 #

7.1 重建索引 #

sql
-- 分析表(更新统计信息)
ANALYZE TABLE users;

-- 优化表(重建表和索引)
OPTIMIZE TABLE users;

-- 使用ALTER TABLE重建
ALTER TABLE users ENGINE=InnoDB;

7.2 检查索引使用情况 #

sql
-- 查看索引使用统计
SELECT 
    object_schema,
    object_name,
    index_name,
    count_read,
    count_fetch
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = 'mydb'
ORDER BY count_read DESC;

-- 查找未使用的索引
SELECT 
    object_schema,
    object_name,
    index_name
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
AND count_star = 0
AND object_schema = 'mydb';

八、特殊索引 #

8.1 函数索引 #

sql
-- MariaDB 10.3+ 支持函数索引
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    created_at DATETIME,
    INDEX idx_year ((YEAR(created_at)))
);

-- 使用函数索引
SELECT * FROM users WHERE YEAR(created_at) = 2024;

8.2 虚拟列索引 #

sql
-- MariaDB 10.2+ 虚拟列索引
CREATE TABLE users (
    id INT PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    full_name VARCHAR(101) AS (CONCAT(first_name, ' ', last_name)) VIRTUAL,
    INDEX idx_full_name (full_name)
);

-- 使用虚拟列索引
SELECT * FROM users WHERE full_name = 'John Doe';

九、索引监控 #

9.1 索引统计信息 #

sql
-- 查看索引基数
SHOW INDEX FROM users;

-- 更新统计信息
ANALYZE TABLE users;

-- 查看表统计信息
SELECT 
    table_name,
    table_rows,
    data_length,
    index_length
FROM information_schema.tables
WHERE table_schema = 'mydb';

9.2 慢查询分析 #

sql
-- 启用慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
SET GLOBAL log_queries_not_using_indexes = 'ON';

-- 分析慢查询
-- 查看慢查询日志
-- 使用mysqldumpslow分析

十、最佳实践 #

10.1 索引创建清单 #

text
索引创建检查
├── 列选择性是否足够高?
├── 是否用于WHERE/JOIN/ORDER BY?
├── 复合索引顺序是否合理?
├── 是否存在冗余索引?
├── 索引长度是否合适?
└── 是否考虑了写入性能影响?

10.2 索引使用建议 #

场景 建议
主键 使用自增整数
外键 创建索引
唯一约束 使用唯一索引
范围查询 将范围列放在索引最后
排序 考虑索引顺序
文本搜索 使用全文索引

十一、总结 #

索引要点:

类型 说明 使用场景
主键索引 唯一非空 主键列
唯一索引 唯一约束 唯一值列
普通索引 加速查询 常用查询列
复合索引 多列组合 多条件查询
全文索引 文本搜索 文本搜索
前缀索引 部分索引 长字符串

最佳实践:

  1. 选择性高的列创建索引
  2. 复合索引遵循最左前缀
  3. 避免过度索引
  4. 定期维护索引
  5. 使用EXPLAIN分析查询

下一步,让我们学习视图!

最后更新:2026-03-27