MySQL 索引与事务详解
MySQL 索引与事务详解
一、索引(Index)
1. 索引的作用与原理
索引是数据库的"目录",能够大幅提高查询速度,但会增加写入开销。MySQL 使用 B+Tree 作为主要索引结构。
2. 索引类型
(1) 普通索引
CREATE INDEX idx_name ON users(name); -- 创建
DROP INDEX idx_name ON users; -- 删除
(2) 唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);
(3) 主键索引(自动创建)
ALTER TABLE users ADD PRIMARY KEY(id);
(4) 复合索引(最左前缀原则)
CREATE INDEX idx_name_age ON users(name, age);
-- 能使用索引的情况:
-- WHERE name = '张三'
-- WHERE name = '张三' AND age = 25
-- WHERE name LIKE '张%'
-- 不能使用索引的情况:
-- WHERE age = 25
-- WHERE age = 25 AND name = '张三'
3. 索引优化技巧
- 为 WHERE、JOIN、ORDER BY 涉及的列创建索引
- 避免过度索引(每个索引占用存储空间并影响写入性能)
- 使用 EXPLAIN 分析查询:
EXPLAIN SELECT * FROM users WHERE name = '张三';
4. 索引失效的常见情况
- 使用
!=
、NOT IN
、IS NULL
、IS NOT NULL
- 对索引列使用函数:
WHERE YEAR(create_time) = 2023
- 类型转换:
WHERE name = 123
(name 是字符串类型) - 模糊查询以通配符开头:
WHERE name LIKE '%三'
二、事务(Transaction)
1. 事务的特性(ACID)
- 原子性(Atomicity):事务是不可分割的工作单位
- 一致性(Consistency):事务执行前后数据库保持一致状态
- 隔离性(Isolation):事务之间互不干扰
- 持久性(Durability):事务提交后永久生效
2. 事务基本操作
START TRANSACTION; -- 或 BEGIN
-- 执行SQL语句
INSERT INTO orders(user_id, amount) VALUES(1, 100);
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
COMMIT; -- 提交
-- 或 ROLLBACK; -- 回滚
3. 事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 | 说明 |
---|---|---|---|---|
READ UNCOMMITTED | ✓ | ✓ | ✓ | 性能最高,安全性最低 |
READ COMMITTED | × | ✓ | ✓ | Oracle默认级别 |
REPEATABLE READ | × | × | ✓ | MySQL默认级别 |
SERIALIZABLE | × | × | × | 安全性最高,性能最低 |
查看和设置隔离级别:
SELECT @@transaction_isolation; -- 查看当前隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; -- 设置隔离级别
4. 事务中的常见问题
(1) 脏读(Dirty Read)
事务A读取了事务B未提交的数据
(2) 不可重复读(Non-repeatable Read)
事务A多次读取同一数据,期间事务B修改了该数据,导致事务A读取结果不一致
(3) 幻读(Phantom Read)
事务A读取某个范围的数据,期间事务B插入了新数据,事务A再次读取时出现"幻行"
5. 事务最佳实践
- 尽量缩短事务执行时间
- 避免在事务中进行远程调用或耗时操作
- 合理设置隔离级别(通常使用默认的 REPEATABLE READ)
- 处理死锁:
-- 查看最近死锁信息
SHOW ENGINE INNODB STATUS;
三、索引与事务的实际应用示例
-- 创建带索引的表
CREATE TABLE orders (id INT AUTO_INCREMENT PRIMARY KEY,user_id INT NOT NULL,amount DECIMAL(10,2) NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,INDEX idx_user_id (user_id),INDEX idx_created_at (created_at)
);-- 事务操作:下单并扣款
START TRANSACTION;INSERT INTO orders (user_id, amount) VALUES (1, 99.99);
UPDATE accounts SET balance = balance - 99.99 WHERE user_id = 1;-- 检查余额是否足够
SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE;-- 如果余额足够则提交,否则回滚
COMMIT;
-- 或 ROLLBACK;
四、高级话题
1. 覆盖索引
查询的列都包含在索引中,无需回表查询
CREATE INDEX idx_covering ON users(name, age);
SELECT name, age FROM users WHERE name = '张三'; -- 使用覆盖索引
2. 行锁、表锁与间隙锁
- InnoDB 默认使用行锁
- 某些情况会升级为表锁(如无索引更新)
- 间隙锁防止幻读(在 REPEATABLE READ 级别下)
3. 保存点(Savepoint)
START TRANSACTION;
-- 操作1
SAVEPOINT sp1;
-- 操作2
ROLLBACK TO sp1; -- 回滚到保存点
COMMIT;
掌握索引和事务是 MySQL 高效使用的关键,合理运用可以大幅提升应用性能和可靠性。