说明 :本文涵盖 MySQL 中三类重要功能:索引 (提高查询性能)、事务 (保证数据一致性)、约束(保证数据完整性)。每条命令附语法和实用示例,覆盖 MySQL 5.7 和 8.0 的常见特性。
一、索引操作
索引用于加速数据检索,但会占用额外存储空间并影响写性能。
1.1 创建索引
1.1.1 使用 CREATE INDEX 语句
语法:
sql
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX 索引名
ON 表名 (列名 [(长度)] [ASC | DESC], ...)
[USING {BTREE | HASH}]
[索引选项];
示例:
sql
-- 普通索引
CREATE INDEX idx_username ON users(username);
-- 唯一索引(不允许重复值)
CREATE UNIQUE INDEX idx_email ON users(email);
-- 全文索引(适用于 MyISAM 或 InnoDB MySQL 5.6+)
CREATE FULLTEXT INDEX ft_content ON articles(title, content);
-- 空间索引(用于地理数据,需 MyISAM/InnoDB 支持)
CREATE SPATIAL INDEX idx_location ON places(location);
-- 复合索引(多列)
CREATE INDEX idx_name_age ON users(last_name, first_name, age);
-- 前缀索引(仅索引字符串的前N个字符)
CREATE INDEX idx_title_prefix ON books(title(20));
-- 降序索引(MySQL 8.0 真正支持)
CREATE INDEX idx_created_desc ON orders(created_at DESC);
-- 指定索引类型(BTREE 是默认)
CREATE INDEX idx_hash ON temp_table(id) USING HASH;
1.1.2 在 CREATE TABLE 时创建索引
sql
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100) UNIQUE,
last_login DATETIME,
INDEX idx_last_login (last_login),
FULLTEXT INDEX ft_name (name)
);
1.1.3 使用 ALTER TABLE 创建索引
sql
-- 添加主键索引(只能通过 ALTER 或 CREATE TABLE)
ALTER TABLE users ADD PRIMARY KEY (id);
-- 添加唯一索引
ALTER TABLE users ADD UNIQUE INDEX idx_email (email);
-- 添加普通索引
ALTER TABLE users ADD INDEX idx_name (name);
-- 添加全文索引
ALTER TABLE articles ADD FULLTEXT INDEX ft_body (body);
-- 添加空间索引
ALTER TABLE places ADD SPATIAL INDEX idx_geom (geom);
1.2 查看索引
sql
-- 查看表的所有索引
SHOW INDEX FROM 表名;
SHOW INDEX FROM 表名\G -- 垂直显示更详细
-- 从information_schema中查询
SELECT * FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = '数据库名' AND TABLE_NAME = '表名';
1.3 删除索引
sql
-- 使用 DROP INDEX 语句
DROP INDEX 索引名 ON 表名;
-- 使用 ALTER TABLE 删除
ALTER TABLE 表名 DROP INDEX 索引名;
-- 删除主键索引(特殊)
ALTER TABLE 表名 DROP PRIMARY KEY;
-- 删除外键索引(实际删除的是外键约束,索引可能保留)
ALTER TABLE 表名 DROP FOREIGN KEY 外键约束名;
示例:
sql
DROP INDEX idx_username ON users;
ALTER TABLE users DROP INDEX idx_email;
ALTER TABLE orders DROP PRIMARY KEY; -- 删除主键
1.4 修改索引
MySQL 没有直接"修改索引"的命令,通常采用"删除后重建"的方式:
sql
-- 先删除旧索引
DROP INDEX idx_old ON table_name;
-- 再创建新索引
CREATE INDEX idx_new ON table_name (column1, column2);
如果只是想重命名索引(MySQL 5.7+ 支持 ALTER TABLE 重命名):
sql
ALTER TABLE 表名 RENAME INDEX 旧索引名 TO 新索引名;
-- 示例
ALTER TABLE users RENAME INDEX idx_username TO idx_uname;
1.5 索引可见性(MySQL 8.0)
可以设置索引对优化器是否可见,而不实际删除索引。
sql
-- 设置索引不可见(优化器忽略该索引)
ALTER TABLE 表名 ALTER INDEX 索引名 INVISIBLE;
-- 设置索引可见
ALTER TABLE 表名 ALTER INDEX 索引名 VISIBLE;
-- 示例
ALTER TABLE users ALTER INDEX idx_email INVISIBLE;
1.6 强制使用或忽略索引(查询提示)
sql
-- 强制使用索引
SELECT * FROM users FORCE INDEX (idx_username) WHERE username = 'john';
-- 忽略索引
SELECT * FROM users IGNORE INDEX (idx_username) WHERE username = 'john';
-- 使用索引(建议,优化器可自行决定)
SELECT * FROM users USE INDEX (idx_username) WHERE username = 'john';
二、事务操作
事务保证一组操作要么全部成功(COMMIT),要么全部失败回滚(ROLLBACK),满足 ACID 特性。
2.1 事务控制语句
| 命令 | 说明 |
|-------------------------------|-------------------|-----------------|
| START TRANSACTION 或 BEGIN | 显式开启一个新事务 |
| COMMIT | 提交事务,使更改永久生效 |
| ROLLBACK | 回滚事务,撤销当前事务中的所有更改 |
| SAVEPOINT 名称 | 在事务中设置保存点 |
| ROLLBACK TO SAVEPOINT 名称 | 回滚到指定保存点 |
| RELEASE SAVEPOINT 名称 | 删除保存点 |
| `SET autocommit = {0 | 1}` | 禁用(0)/启用(1)自动提交 |
2.2 自动提交模式
MySQL 默认 autocommit = 1,每条 DML 自动提交。要使用多语句事务,需先禁用自动提交或显式 START TRANSACTION。
sql
-- 查看当前自动提交设置
SELECT @@autocommit;
-- 关闭自动提交(仅当前会话)
SET autocommit = 0;
-- 开启自动提交
SET autocommit = 1;
2.3 基本事务示例
sql
-- 方式一:使用 START TRANSACTION
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 或 ROLLBACK
-- 方式二:使用 BEGIN
BEGIN;
INSERT INTO logs (user_id, action) VALUES (1, '转账');
SAVEPOINT sp1;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 发现错误,回滚到保存点
ROLLBACK TO SAVEPOINT sp1;
COMMIT; -- INSERT 已提交,UPDATE 被撤销
2.4 保存点(SAVEPOINT)
sql
START TRANSACTION;
INSERT INTO orders (user_id, amount) VALUES (1, 500);
SAVEPOINT after_insert;
UPDATE inventory SET stock = stock - 5 WHERE product_id = 10;
-- 库存不足,决定回滚到保存点
ROLLBACK TO SAVEPOINT after_insert;
-- 可继续其他操作
INSERT INTO order_log (msg) VALUES ('库存不足,未扣减');
-- 释放不再需要的保存点
RELEASE SAVEPOINT after_insert;
COMMIT;
2.5 事务隔离级别
隔离级别影响事务并发时的可见性。
查看当前隔离级别:
sql
-- 全局级
SELECT @@global.transaction_isolation;
-- 会话级
SELECT @@transaction_isolation;
设置隔离级别:
sql
-- 设置会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- MySQL 默认
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 设置全局隔离级别(需要 SUPER 权限)
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
2.6 锁语句(辅助事务)
sql
-- 共享锁(读锁)
SELECT * FROM accounts WHERE id = 1 LOCK IN SHARE MODE;
-- 排他锁(写锁)
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 表锁(手动)
LOCK TABLES accounts WRITE;
-- 执行更新...
UNLOCK TABLES;
2.7 XA 事务(分布式事务)
sql
XA START 'xid1'; -- 开启 XA 事务
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
XA END 'xid1';
XA PREPARE 'xid1'; -- 准备阶段
XA COMMIT 'xid1'; -- 提交
-- 或 XA ROLLBACK 'xid1';
2.8 查看事务信息
sql
-- 查看当前正在运行的事务
SELECT * FROM information_schema.INNODB_TRANSACTION;
-- 查看锁信息
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 查看引擎状态(包含事务详情)
SHOW ENGINE INNODB STATUS\G
三、约束操作
约束用于限制表中数据的合法性和完整性。
3.1 约束类型概览
| 约束类型 | 关键字 | 作用 |
|---|---|---|
| 主键约束 | PRIMARY KEY |
唯一标识一行,非空且唯一 |
| 外键约束 | FOREIGN KEY |
参照另一表的主键,维护引用完整性 |
| 唯一约束 | UNIQUE |
列值不能重复(可多个 NULL) |
| 检查约束 | CHECK |
限制列值满足条件(MySQL 8.0.16+ 完整支持) |
| 非空约束 | NOT NULL |
列值不能为 NULL |
| 默认约束 | DEFAULT |
提供默认值 |
3.2 主键约束(PRIMARY KEY)
创建主键
sql
-- 列级定义
CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50));
-- 表级定义(适合复合主键)
CREATE TABLE t2 (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id)
);
-- 为已有表添加主键
ALTER TABLE t1 ADD PRIMARY KEY (id);
-- 添加自增属性
ALTER TABLE t1 MODIFY id INT AUTO_INCREMENT;
删除主键
sql
ALTER TABLE t1 DROP PRIMARY KEY;
⚠️ 如果主键列是自增列,需先修改列定义去掉 AUTO_INCREMENT。
3.3 外键约束(FOREIGN KEY)
外键用于维持两张表之间的引用完整性。
创建外键
sql
-- 建表时定义
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE ON UPDATE RESTRICT
);
-- 带约束名
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 为已有表添加外键
ALTER TABLE orders ADD CONSTRAINT fk_user
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
外键选项:
ON DELETE CASCADE:主表记录删除,子表相关记录自动删除ON DELETE SET NULL:子表外键列设为 NULLON DELETE RESTRICT/NO ACTION:阻止删除(默认行为)ON UPDATE CASCADE:主键更新时同步更新子表外键ON UPDATE SET NULL:主键更新时子表外键设为 NULL
查看外键
sql
SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'orders' AND CONSTRAINT_NAME NOT IN ('PRIMARY');
-- 或使用 SHOW CREATE TABLE
SHOW CREATE TABLE orders\G
删除外键
sql
-- 必须使用约束名删除
ALTER TABLE orders DROP FOREIGN KEY fk_orders_user;
-- 如果不知道约束名,可通过 SHOW CREATE TABLE 查看
禁用/启用外键检查(全局会话)
sql
-- 暂时禁用外键检查(可用于重排序表导入)
SET FOREIGN_KEY_CHECKS = 0;
-- 执行数据操作(如 TRUNCATE 被引用的表)
-- 恢复检查
SET FOREIGN_KEY_CHECKS = 1;
3.4 唯一约束(UNIQUE)
创建唯一约束
sql
-- 列级
CREATE TABLE users (email VARCHAR(100) UNIQUE);
-- 表级命名
CREATE TABLE users (
email VARCHAR(100),
CONSTRAINT uk_email UNIQUE (email)
);
-- 复合唯一约束
CREATE TABLE user_roles (
user_id INT,
role_id INT,
UNIQUE KEY uk_user_role (user_id, role_id)
);
-- 为已有表添加
ALTER TABLE users ADD UNIQUE INDEX uk_email (email);
-- 或
ALTER TABLE users ADD CONSTRAINT uk_username UNIQUE (username);
删除唯一约束
sql
-- 唯一约束会自动创建同名索引,通过删除索引即可
ALTER TABLE users DROP INDEX uk_email;
3.5 检查约束(CHECK)
MySQL 8.0.16+ 完整支持 CHECK;早期版本解析但不执行。
创建 CHECK
sql
-- 列级 CHECK
CREATE TABLE products (
price DECIMAL(10,2) CHECK (price > 0),
quantity INT CHECK (quantity >= 0)
);
-- 表级 CHECK(可命名)
CREATE TABLE employees (
id INT,
salary DECIMAL(10,2),
CONSTRAINT chk_salary CHECK (salary >= 0)
);
-- 跨列 CHECK
CREATE TABLE reservations (
start_date DATE,
end_date DATE,
CHECK (start_date < end_date)
);
-- 为已有表添加 CHECK
ALTER TABLE users ADD CONSTRAINT chk_age CHECK (age BETWEEN 0 AND 150);
删除 CHECK
sql
ALTER TABLE users DROP CONSTRAINT chk_age;
查看 CHECK 约束
sql
SELECT * FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS
WHERE CONSTRAINT_SCHEMA = '数据库名' AND TABLE_NAME = '表名';
3.6 非空约束(NOT NULL)
添加非空
sql
-- 建表时
CREATE TABLE t (id INT NOT NULL);
-- 修改已有列
ALTER TABLE users MODIFY email VARCHAR(100) NOT NULL;
删除非空(允许 NULL)
sql
ALTER TABLE users MODIFY email VARCHAR(100) NULL;
3.7 默认约束(DEFAULT)
设置默认值
sql
-- 建表时
CREATE TABLE t (status VARCHAR(20) DEFAULT 'active');
-- 修改已有列默认值(两种方法)
ALTER TABLE users ALTER COLUMN status SET DEFAULT 'inactive';
-- 或
ALTER TABLE users MODIFY status VARCHAR(20) DEFAULT 'inactive';
-- 设置表达式默认值(MySQL 8.0+)
CREATE TABLE t (created_at DATETIME DEFAULT (NOW()));
删除默认值
sql
ALTER TABLE users ALTER COLUMN status DROP DEFAULT;
3.8 查看所有约束
sql
-- 通过 INFORMATION_SCHEMA 查看表的所有约束
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA = '数据库名' AND TABLE_NAME = '表名';
3.9 修改约束的其他方式
由于没有直接的"修改约束"命令,通常需要删除后重建。例如修改 NOT NULL 到 NULL 允许,或修改 CHECK 条件。
sql
-- 修改 CHECK:先删除,再添加
ALTER TABLE t DROP CONSTRAINT chk_old;
ALTER TABLE t ADD CONSTRAINT chk_new CHECK (new_condition);
3.10 完整的约束管理示例
sql
-- 创建带所有约束类型的表
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
emp_no VARCHAR(20) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
age INT DEFAULT 25 CHECK (age >= 18 AND age <= 65),
dept_id INT,
salary DECIMAL(10,2) CHECK (salary >= 0),
email VARCHAR(100),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_dept FOREIGN KEY (dept_id) REFERENCES departments(id)
);
-- 添加新约束
ALTER TABLE employees ADD CONSTRAINT chk_salary_positive CHECK (salary > 0);
ALTER TABLE employees MODIFY email VARCHAR(100) NOT NULL;
-- 删除约束
ALTER TABLE employees DROP FOREIGN KEY fk_dept;
ALTER TABLE employees DROP CONSTRAINT chk_salary_positive;
ALTER TABLE employees MODIFY email VARCHAR(100) NULL;
-- 查看最终约束
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME = 'employees';
四、综合应用场景示例
4.1 索引与事务配合
sql
-- 开启事务,在更新前检查索引使用情况
START TRANSACTION;
-- 使用排他锁锁定待更新行
SELECT * FROM accounts WHERE account_no = 'A123' FOR UPDATE;
-- 确保 account_no 列有唯一索引
CREATE UNIQUE INDEX idx_acc_no ON accounts(account_no);
UPDATE accounts SET balance = balance - 500 WHERE account_no = 'A123';
UPDATE accounts SET balance = balance + 500 WHERE account_no = 'B456';
COMMIT;
4.2 约束与事务保证数据一致性
sql
START TRANSACTION;
-- 检查外键关系
SET FOREIGN_KEY_CHECKS = 0; -- 临时关闭(谨慎)
DELETE FROM departments WHERE id = 10;
-- 实际应使用 ON DELETE CASCADE 或先删除子表记录
SET FOREIGN_KEY_CHECKS = 1;
COMMIT;
五、最佳实践与注意事项
| 方面 | 建议 |
|---|---|
| 索引 | 避免过多索引(影响写性能);复合索引遵循最左前缀原则;使用 EXPLAIN 分析查询;定期清理无用索引。 |
| 事务 | 尽量短小,避免长事务;合理选择隔离级别(通常 READ COMMITTED 或 REPEATABLE READ);使用 SAVEPOINT 实现部分回滚;避免在事务中进行用户交互或网络调用。 |
| 约束 | 优先使用数据库约束而非应用层校验;外键会带来额外开销,但能保证引用完整性;CHECK 在 MySQL 8.0 之前不生效,注意兼容性;修改约束前备份数据。 |
以上内容涵盖了 MySQL 中索引、事务和约束的所有核心操作命令,包括创建、查看、删除、修改及管理语句,并附有详细示例。根据实际业务场景灵活运用即可。