数据库管理最热问答集锦,看完不再困惑 - 编号70418
大多数数据库崩溃并非源于硬件故障,而是因为索引策略错误——根据 DBA 社区 2024 年故障统计,超过 60% 的线上事故源于索引设计不当,而非容量不足。
索引失效:为什么加了索引反而让查询更慢?
一个常见场景是某电商平台为订单表创建了组合索引 (status, create_time),但开发人员在查询时使用了 status IN ('paid','shipped') AND create_time > '2024-01-01'。这导致索引只对 status 生效,create_time 的排序被跳过,查询扫描行数暴增 10 倍。核心误区在于:多值 IN 条件会让组合索引的后续列失效。正确做法是拆成两个单列索引或用 UNION ALL 替代。
死锁频发:更新同一张表的不同行怎么会互相卡死?
某支付系统在并行处理退款时频繁报死锁。追查发现业务逻辑先更新用户余额表,再更新订单表,而另一个线程反序操作。两条线程分别持有对方需要的行锁,形成闭环。更隐蔽的问题是:即使操作的是不同行,如果 where 条件使用了非唯一索引(如 user_id),引擎可能锁定一个间隙范围,导致看似无关的行也被锁住。解决方案是固定表的更新顺序,或对高频互斥操作使用 SELECT ... FOR UPDATE 加行级锁。
分页深翻:为什么 LIMIT 100000,20 比 LIMIT 0,20 慢 100 倍?
一个典型例子是后台管理系统的用户列表分页,翻到第 5000 页时页面超时。原因在于 MySQL 的 LIMIT 偏移实现:它实际读取了 100020 行数据,再丢弃前 100000 行。更糟糕的是,如果表上有非聚簇索引,每次翻页还需要回表查主键,IO 开销剧增。优化方案是用游标分页代替偏移分页:记录上一页最后一条记录的 id,查询条件改为 WHERE id > last_id LIMIT 20,利用主键 B+ 树特性直接定位。
3个最常踩的误区与行动建议:
- 误用索引覆盖: 认为索引越多越好,实际每个索引都会降低写入性能。建议通过慢查询日志找出高频查询,为 select 子句中的字段建立覆盖索引,避免回表。
- 忽略连接池配置: 默认连接池大小 10 对并发 100 的应用往往不够。建议根据公式 "连接数 = (CPU核数 * 2) + 磁盘数" 手动调优,并启用连接泄漏检测。
- 过度依赖自动调优: MySQL 的 optimizer_switch 默认开启索引合并,但联合查询时可能导致错误选择。建议定期用 EXPLAIN 分析执行计划,对 type=index_merge 的查询强制指定索引。