索引是数据库中的物理数据结构(如B+树、Hash表),主要作用是加速数据检索。索引会占用额外的存储空间,并在数据修改时增加维护成本。

约束是数据库中的逻辑规则,用于保障数据完整性和一致性。约束不占用物理存储空间,只在数据操作时进行校验。
-- 约束确保数据质量
CREATE TABLE users (
id INT PRIMARY KEY, -- 主键约束
email VARCHAR(100) UNIQUE -- 唯一约束
);
索引 | 约束 | |
本质 | 物理数据结构 | 逻辑规则 |
存储占用 | ✅ 独立存储结构 | ❌ 仅元数据 |
创建示例 | CREATE INDEX idx ON tbl(col) | PRIMARY KEY (id) |
类型 | 语法示例 | 适用场景 |
B+树索引 | INDEX idx_age (age) | 范围查询(>、<、BETWEEN) |
自适应Hash索引 | 自动由InnoDB管理 | 内存中的等值查询(=、IN) |
全文索引 | FULLTEXT ftxt (content) | 文本关键词搜索 |
类型 | 关联对象 | 数据存储特点 |
聚集索引 | 主键 | 索引与行数据共存于B+树叶子节点 |
辅助索引 | 普通/唯一索引 | 仅存储主键值,需回表查询 |
类型 | 语法 | 特性 |
主键索引 | PRIMARY KEY (id) | 非空唯一,每表只有一个 |
唯一索引 | UNIQUE INDEX uni_mail(email) | 允许一个NULL值 |
普通索引 | INDEX idx_phone(phone) | 允许重复值和NULL |
前缀索引 | INDEX idx_name(name(10)) | 只比较前N个字符 |
类型 | 语法 | 最左匹配原则 |
单列索引 | INDEX idx_email (email) | - |
组合索引 | INDEX idx_comp (age,city) | ✅ 必须从最左列开始使用 |
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY
);
CREATE TABLE employees (
email VARCHAR(100) UNIQUE
);
CREATE TABLE orders (
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
);
CREATE TABLE products (
name VARCHAR(200) NOT NULL
);
CREATE TABLE logs (
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE students (
age TINYINT,
-- MySQL中仅语法支持,实际需应用层校验
CHECK (age >= 18)
);
约束类型 | 自动创建索引 | 协同优势 |
主键约束 | 聚集索引 | 数据物理有序存储 |
唯一约束 | 唯一索引 | 快速查找重复值 |
外键约束 | 普通索引 | 加速级联操作和JOIN查询 |
CREATE TABLE users (
id INT AUTO_INCREMENT,
email VARCHAR(100) NOT NULL,
PRIMARY KEY (id) -- 自动创建聚集索引
);
CREATE TABLE orders (
user_id INT,
product_id INT,
FOREIGN KEY (user_id) REFERENCES users(id), -- 自动建索引
FOREIGN KEY (product_id) REFERENCES products(id),
INDEX idx_user_product (user_id, product_id) -- 组合索引
);
CREATE TABLE members (
member_id CHAR(12) PRIMARY KEY,
phone VARCHAR(20) NOT NULL UNIQUE,
level TINYINT NOT NULL DEFAULT 1
);
-- 添加查询索引
CREATE INDEX idx_level ON members(level);
-- 账户表(主键约束+聚集索引)
CREATE TABLE accounts (
account_id CHAR(16) PRIMARY KEY,
holder_name VARCHAR(50) NOT NULL,
balance DECIMAL(15,2) NOT NULL DEFAULT 0.00,
INDEX idx_holder (holder_name) -- 户名索引
);
-- 交易表(外键+索引)
CREATE TABLE transactions (
tx_id BIGINT AUTO_INCREMENT PRIMARY KEY,
from_account CHAR(16) NOT NULL,
to_account CHAR(16) NOT NULL,
amount DECIMAL(12,2) NOT NULL,
tx_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (from_account) REFERENCES accounts(account_id),
FOREIGN KEY (to_account) REFERENCES accounts(account_id),
INDEX idx_tx_time (tx_time) -- 时间索引
);
INSERT INTO accounts (account_id, holder_name, balance) VALUES
('622588****1234', '张三', 10000.00),
('622588****5678', '李四', 5000.00);
INSERT INTO transactions (from_account, to_account, amount) VALUES
('622588****1234', '622588****5678', 1000.00),
('622588****5678', '622588****1234', 500.00);
-- 尝试删除有交易的账户
DELETE FROM accounts WHERE account_id = '622588****1234';
-- 报错:Cannot delete or update a parent row: a foreign key constraint fails
-- 查询最近24小时交易
EXPLAIN SELECT * FROM transactions
WHERE tx_time > NOW() - INTERVAL 1 DAY;
-- 输出:Using index idx_tx_time
-- 尝试插入重复卡号
INSERT INTO accounts VALUES ('622588****1234', '王五', 2000.00);
-- 报错:Duplicate entry '622588****1234' for key 'PRIMARY'
问题场景 | 错误示例 | 解决方案 |
外键字段未建索引 | FOREIGN KEY无索引 | 显式创建索引 |
级联删除风险 | ON DELETE CASCADE核心数据 | 应用层逻辑控制删除 |
唯一索引含NULL值 | UNIQUE(email)含多个NULL | 配合NOT NULL约束 |
长文本建完整索引 | INDEX(content)长文本 | 改用前缀索引INDEX(content(20)) |
过度索引 | 单表15个索引 | 控制在5-8个以内 |