MySQL索引类型

索引是帮助MySQL高效获取数据的数据结构,类似于书籍的目录,可以大大加快查询速度。

一、索引类型

1. B-Tree 索引(最常用)

特点

  • 默认的索引类型
  • 适用于全键值、键值范围或键值前缀查找
  • 支持排序和分组

创建语法

-- 单列索引
CREATE INDEX idx_name ON table_name(column_name);

-- 多列复合索引
CREATE INDEX idx_name ON table_name(col1, col2, col3);

-- 创建表时指定
CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100),
    age INT,
    INDEX idx_name (name),
    INDEX idx_email_age (email, age)
);

适用查询类型

-- 全值匹配
SELECT * FROM users WHERE name = 'John';

-- 前缀匹配(最左前缀)
SELECT * FROM users WHERE name LIKE 'Joh%';

-- 范围查询
SELECT * FROM users WHERE age BETWEEN 20 AND 30;

-- 精确匹配左列 + 范围匹配右列
SELECT * FROM users WHERE email = 'john@example.com' AND age > 25;

-- 排序
SELECT * FROM users ORDER BY name;

-- 分组
SELECT COUNT(*), age FROM users GROUP BY age;

最左前缀原则示例

-- 复合索引: (last_name, first_name, age)

-- 使用索引的情况
SELECT * FROM users WHERE last_name = 'Smith';
SELECT * FROM users WHERE last_name = 'Smith' AND first_name = 'John';
SELECT * FROM users WHERE last_name = 'Smith' AND first_name = 'John' AND age = 30;
SELECT * FROM users WHERE last_name = 'Smith' AND age > 25; -- 只使用last_name部分

-- 不能使用索引的情况
SELECT * FROM users WHERE first_name = 'John'; -- 跳过了last_name
SELECT * FROM users WHERE age = 30; -- 跳过了前两列

2. 哈希索引

特点

  • 基于哈希表实现
  • 只能用于等值比较(=, IN)
  • 不支持范围查询和排序
  • Memory存储引擎默认索引类型

创建语法

-- 只能在MEMORY表上创建哈希索引
CREATE TABLE memory_table (
    id INT,
    data VARCHAR(100),
    INDEX USING HASH (id)
) ENGINE=MEMORY;

-- 或者
CREATE INDEX idx_hash ON memory_table(id) USING HASH;

适用场景

-- 等值查询(快速)
SELECT * FROM memory_table WHERE id = 100;

-- 不支持范围查询
SELECT * FROM memory_table WHERE id > 100; -- 不会使用哈希索引

自适应哈希索引(InnoDB)

InnoDB会自动在内存中为频繁访问的索引页创建哈希索引,这是自动的,无需手动创建。

3. 全文索引(FULLTEXT)

特点

  • 用于全文搜索
  • 支持自然语言搜索和布尔搜索
  • 只能用于MyISAM和InnoDB存储引擎

创建语法

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

-- 或者单独创建
CREATE FULLTEXT INDEX idx_ft_content ON articles(content);

使用示例

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

-- 布尔搜索
SELECT * FROM articles 
WHERE MATCH(title, content) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE);

-- 相关性排序
SELECT 
    id,
    title,
    MATCH(title, content) AGAINST('MySQL performance') as relevance
FROM articles 
WHERE MATCH(title, content) AGAINST('MySQL performance')
ORDER BY relevance DESC;

布尔搜索操作符

  • + 必须包含

  • - 必须不包含

  • > 提高相关性

  • < 降低相关性

  • () 分组

  • ~ 否定相关性

  • * 通配符

  • " 短语搜索

4. 空间索引(SPATIAL)

特点

  • 用于地理空间数据
  • 支持空间数据类型的查询
  • 只能用于MyISAM和InnoDB(5.7+)存储引擎

创建语法

-- 创建空间数据表
CREATE TABLE locations (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    position POINT NOT NULL,
    SPATIAL INDEX(position)
);

-- 插入空间数据
INSERT INTO locations (name, position) VALUES 
('Location A', ST_GeomFromText('POINT(116.3974 39.9093)')),
('Location B', ST_GeomFromText('POINT(121.4737 31.2304)'));

使用示例

-- 查找范围内的点
SET @bbox = ST_GeomFromText('POLYGON((116.0 39.0, 117.0 39.0, 117.0 40.0, 116.0 40.0, 116.0 39.0))');
SELECT name FROM locations 
WHERE ST_Within(position, @bbox);

-- 计算距离(MySQL 5.7+)
SELECT 
    name,
    ST_Distance_Sphere(position, ST_GeomFromText('POINT(116.3974 39.9093)')) as distance_meters
FROM locations 
ORDER BY distance_meters ASC;

5. R-Tree 索引

特点

  • 用于多维数据的索引
  • 主要用于空间索引的底层实现
  • 支持范围查询和多维数据查询

6. 聚簇索引 vs 非聚簇索引

聚簇索引(InnoDB主键索引)

-- InnoDB表的主键就是聚簇索引
CREATE TABLE users (
    id INT PRIMARY KEY,  -- 聚簇索引
    name VARCHAR(50),
    email VARCHAR(100)
);

-- 数据按主键顺序物理存储
-- 叶子节点包含完整的数据行

非聚簇索引(二级索引)

-- 非主键索引都是非聚簇索引
CREATE INDEX idx_email ON users(email);

-- 叶子节点包含主键值,需要回表查询
-- 查询过程:idx_email -> 主键 -> 数据行

7. 覆盖索引

索引包含所有需要查询的字段,无需回表查询。

示例

-- 创建复合索引
CREATE INDEX idx_covering ON users(email, name, age);

-- 覆盖索引查询(Extra: Using index)
EXPLAIN SELECT email, name FROM users WHERE email = 'test@example.com';

-- 非覆盖索引查询(需要回表)
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';

8. 前缀索引

适用场景

  • 文本字段很长时
  • 减少索引大小
  • 提高索引效率

创建语法

-- 为长文本字段创建前缀索引
CREATE TABLE logs (
    id INT PRIMARY KEY,
    url VARCHAR(500),
    INDEX idx_url_prefix (url(100))  -- 前100个字符
);

-- 选择合适的前缀长度
SELECT 
    COUNT(DISTINCT LEFT(url, 10)) as len_10,
    COUNT(DISTINCT LEFT(url, 20)) as len_20,
    COUNT(DISTINCT LEFT(url, 50)) as len_50,
    COUNT(DISTINCT url) as total
FROM logs;

9. 唯一索引

特点

  • 保证列值的唯一性
  • 允许NULL值(但只能有一个NULL)
  • 提高查询性能

创建语法

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

-- 创建表时指定
CREATE TABLE users (
    id INT PRIMARY KEY,
    email VARCHAR(100) UNIQUE,
    username VARCHAR(50) UNIQUE
);

二、索引管理命令

查看索引

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

-- 查看索引信息
SELECT 
    TABLE_NAME,
    INDEX_NAME,
    SEQ_IN_INDEX,
    COLUMN_NAME,
    INDEX_TYPE,
    CARDINALITY
FROM INFORMATION_SCHEMA.STATISTICS 
WHERE TABLE_NAME = 'users';

维护索引

-- 重建索引(InnoDB)
ALTER TABLE users ENGINE=InnoDB;

-- 优化表(重建索引并整理碎片)
OPTIMIZE TABLE users;

-- 分析索引使用情况
ANALYZE TABLE users;

-- 强制使用/忽略索引
SELECT * FROM users USE INDEX (idx_email) WHERE email = 'test@example.com';
SELECT * FROM users IGNORE INDEX (idx_email) WHERE email = 'test@example.com';

三、索引设计最佳实践

1. 选择合适索引列

-- 适合索引的列
- WHERE子句中的列
- JOIN关联的列  
- ORDER BY/GROUP BY的列
- 选择性高的列(不同值多的列)

-- 计算选择性
SELECT 
    COUNT(DISTINCT column_name) * 1.0 / COUNT(*) as selectivity
FROM table_name;

2. 复合索引设计

-- 好的复合索引设计
CREATE INDEX idx_optimized ON orders(user_id, status, create_date);

-- 支持的查询
SELECT * FROM orders WHERE user_id = 123 AND status = 'completed';
SELECT * FROM orders WHERE  status = 'completed' AND user_id = 123; --注意,这个也会走到索引,where顺序会自动调整
SELECT * FROM orders WHERE user_id = 123 ORDER BY create_date DESC;
SELECT * FROM orders WHERE user_id = 123 AND status IN ('pending', 'processing');

3. 避免索引失效

-- 会导致索引失效的操作
SELECT * FROM users WHERE YEAR(create_time) = 2023;  -- 对索引列使用函数
SELECT * FROM users WHERE amount * 2 > 1000;         -- 对索引列进行运算
SELECT * FROM users WHERE name LIKE '%john%';        -- 前导通配符
SELECT * FROM users WHERE email != 'test@example.com'; -- 不等于操作

性能测试示例

创建测试表

CREATE TABLE index_test (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    email VARCHAR(100),
    age INT,
    created_at DATETIME,
    INDEX idx_name (name),
    INDEX idx_email_age (email, age),
    INDEX idx_created (created_at)
);

-- 插入测试数据
INSERT INTO index_test (name, email, age, created_at)
SELECT 
    CONCAT('User', n),
    CONCAT('user', n, '@example.com'),
    FLOOR(RAND() * 80) + 18,
    DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY)
FROM (SELECT @n := @n + 1 as n FROM 
    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t1,
    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t2,
    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) t3,
    (SELECT @n := 0) t4
) numbers;

测试不同查询

-- 使用索引的查询
EXPLAIN SELECT * FROM index_test WHERE name = 'User100';

-- 使用复合索引
EXPLAIN SELECT * FROM index_test WHERE email = 'user100@example.com' AND age > 25;

-- 范围查询使用索引
EXPLAIN SELECT * FROM index_test WHERE created_at > '2023-01-01';

-- 索引失效的查询
EXPLAIN SELECT * FROM index_test WHERE LEFT(name, 4) = 'User';

四、总结

索引类型适用场景优点缺点
B-Tree大多数场景支持范围查询、排序索引大小较大
哈希索引等值查询查询速度快不支持范围查询
全文索引文本搜索支持全文搜索维护成本高
空间索引地理数据支持空间查询使用复杂
聚簇索引主键数据访问快插入速度可能慢

选择合适的索引类型和设计良好的索引策略是数据库性能优化的关键。需要根据具体的业务场景、数据特性和查询模式来设计索引。