TiDB 性能优化面试题:索引、缓存、异步与并发问题解析
一、TiDB 面试高频题全解TiDB 是后端开发岗必考技术之一面试中对索引原理、事务隔离、分布式架构、SQL优化四大方向的考察深度逐年提升。本文精选10道必考题配有完整代码和原理分析。二、索引原理必考题2.1 QTiDB 索引的底层数据结构B Tree 的优势参考答案-- 验证索引使用情况 EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id 123 AND status paid ORDER BY created_at DESC LIMIT 20; -- 输出分析 -- 好Index Scan using idx_user_status on orders -- 差Seq Scan on orders全表扫描 -- 联合索引最左匹配原则 CREATE INDEX idx_user_status_date ON orders(user_id, status, created_at DESC); -- 覆盖索引避免回表 CREATE INDEX idx_covering ON users(id, name, email); -- 查询只包含这三列时不需要回表 SELECT id, name, email FROM users WHERE id BETWEEN 1 AND 100; -- 完全走覆盖索引不回表B Tree 的优势vs B Tree vs Hash非叶节点只存键不存数据同一页面能存更多键 → 树更矮叶节点形成双向链表 → 范围查询高效B Tree 需要回溯Hash 索引只支持等值查询不支持范围2.2 Q什么情况下索引会失效-- ❌ 索引失效场景 -- 1. 对索引列使用函数 SELECT * FROM users WHERE YEAR(created_at) 2024; -- 失效 SELECT * FROM users WHERE created_at 2024-01-01; -- ✅ 有效 -- 2. 隐式类型转换phone 是 varchar传入 number SELECT * FROM users WHERE phone 13800138000; -- 失效全表扫描 SELECT * FROM users WHERE phone 13800138000; -- ✅ 有效 -- 3. 联合索引未遵守最左匹配 -- 索引(a, b, c) SELECT * FROM t WHERE b 1 AND c 2; -- ❌ a 未出现失效 SELECT * FROM t WHERE a 1 AND c 2; -- ⚠️ 只用到 a 列索引 SELECT * FROM t WHERE a 1 AND b 2 AND c 3; -- ✅ 完整使用 -- 4. LIKE 以通配符开头 SELECT * FROM users WHERE name LIKE %张%; -- ❌ 失效 SELECT * FROM users WHERE name LIKE 张%; -- ✅ 有效 -- 5. OR 条件一侧无索引MySQL SELECT * FROM users WHERE id 1 OR age 25; -- 如果 age 无索引全表扫描三、事务隔离级别必考3.1 Q四种隔离级别分别解决了什么问题-- 查看/设置隔离级别 SELECT transaction_isolation; SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 四种隔离级别与并发问题对照表 -- READ UNCOMMITTED脏读✓ 不可重复读✓ 幻读✓几乎不用 -- READ COMMITTED脏读✗ 不可重复读✓ 幻读✓Oracle默认 -- REPEATABLE READ脏读✗ 不可重复读✗ 幻读✓MySQL默认MVCC间隙锁基本解决幻读 -- SERIALIZABLE脏读✗ 不可重复读✗ 幻读✗性能最差 -- 演示不可重复读READ COMMITTED -- 事务A BEGIN; SELECT balance FROM accounts WHERE id 1; -- 结果1000 -- 事务B此时并发执行 UPDATE accounts SET balance 900 WHERE id 1; COMMIT; -- 事务A继续 SELECT balance FROM accounts WHERE id 1; -- 结果900不可重复读 COMMIT; -- REPEATABLE READ 下事务A第二次仍然读到 1000MVCC 保证3.2 QMVCC 的实现原理-- MySQL InnoDB MVCC每行数据包含隐藏字段 -- DB_TRX_ID最后一次修改的事务ID -- DB_ROLL_PTR指向 undo log 中旧版本 -- DB_ROW_ID隐式主键 -- 读操作快照读不加锁通过 ReadView 决定可见性 SELECT * FROM orders WHERE id 1; -- 快照读 -- 当前读加锁读最新版本 SELECT * FROM orders WHERE id 1 FOR UPDATE; -- X锁 SELECT * FROM orders WHERE id 1 LOCK IN SHARE MODE; -- S锁 UPDATE orders SET statuspaid WHERE id 1; -- 当前读 -- ReadView 可见性规则REPEATABLE READ -- 1. 事务ID min_trx_id已提交可见 -- 2. 事务ID max_trx_id未提交不可见 -- 3. 在范围内判断是否在 m_ids活跃事务列表中四、性能调优实战4.1 Q慢查询排查完整流程-- 1. 开启慢查询日志线上谨慎操作 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1; -- 超过1秒记录 SET GLOBAL slow_query_log_file /var/log/mysql/slow.log; -- 2. 用 EXPLAIN 分析执行计划 EXPLAIN SELECT u.name, COUNT(o.id) as order_count FROM users u LEFT JOIN orders o ON u.id o.user_id WHERE u.created_at 2024-01-01 GROUP BY u.id ORDER BY order_count DESC LIMIT 10; -- 关注 type 列效率从高到低 -- system const eq_ref ref range index ALL(全表扫描) -- 关注 Extra 列 -- Using index覆盖索引最好 -- Using filesort需要优化ORDER BY -- Using temporary需要优化GROUP BY -- 3. 针对性优化 -- 问题全表扫描 filesort CREATE INDEX idx_users_created ON users(created_at, id); CREATE INDEX idx_orders_user ON orders(user_id); -- 优化后range扫描 覆盖索引性能提升10x五、高可用与分布式5.1 Q主从复制原理主库宕机如何切换-- 主从复制三个线程 -- Master: binlog dump 线程发送 binlog -- Slave: IO 线程接收写relay log SQL线程重放relay log -- 查看主从延迟 SHOW SLAVE STATUS\G -- 关注 Seconds_Behind_Master延迟秒数 -- 关注 Exec_Master_Log_Pos已执行的位置 -- 主库故障切换步骤 -- 1. 确认主库故障心跳超时 -- 2. 选举延迟最小的从库MHA/Orchestrator自动选举 -- 3. 从库执行剩余 relay log -- 4. 将选举的从库提升为主库 -- 5. 其他从库切换到新主库 -- 6. 应用层更新连接配置VIP漂移 或 DNS切换 -- 半同步复制保证至少一个从库收到binlog再提交 SET GLOBAL rpl_semi_sync_master_enabled ON; SET GLOBAL rpl_semi_sync_master_timeout 10000; -- 等待超时降级为异步六、手写题6.1 实现分页查询大数据量优化版-- ❌ 传统分页OFFSET 越大越慢 SELECT * FROM orders ORDER BY id LIMIT 20 OFFSET 1000000; -- 需要扫描 1000020 行丢弃前 1000000 行 -- ✅ 游标分页利用索引快速定位 SELECT * FROM orders WHERE id :last_id -- 上次返回的最后一个 id ORDER BY id ASC LIMIT 20; -- 直接从 last_id 开始O(log n) 定位 -- ✅ 延迟关联先查主键再关联 SELECT o.* FROM ( SELECT id FROM orders WHERE status paid ORDER BY created_at DESC LIMIT 20 OFFSET 10000 ) AS temp JOIN orders o ON temp.id o.id; -- 子查询只用覆盖索引减少回表次数七、面试汇总题目难度考察重点B Tree 原理⭐⭐存储引擎索引失效场景⭐⭐⭐优化意识MVCC 实现⭐⭐⭐⭐深度原理慢查询排查流程⭐⭐⭐实战能力主从切换流程⭐⭐⭐高可用大数据量分页优化⭐⭐⭐⭐性能优化收藏关注TiDB 高频面试题持续更新助力 Offer 到手三、实战进阶TiDB 最佳实践3.1 错误处理与异常设计在生产环境中完善的错误处理是系统稳定性的基石。以下是 TiDB 的推荐错误处理模式-- 错误处理使用事务保证数据一致性 CREATE OR REPLACE FUNCTION safe_transfer( from_id BIGINT, to_id BIGINT, amount DECIMAL(10,2) ) RETURNS BOOLEAN AS $$ DECLARE from_balance DECIMAL(10,2); BEGIN -- 加锁查询余额防止并发问题 SELECT balance INTO from_balance FROM accounts WHERE id from_id FOR UPDATE; IF from_balance amount THEN RAISE EXCEPTION 余额不足: 当前余额 %, 需要 %, from_balance, amount; END IF; UPDATE accounts SET balance balance - amount WHERE id from_id; UPDATE accounts SET balance balance amount WHERE id to_id; INSERT INTO transfer_logs(from_id, to_id, amount, created_at) VALUES(from_id, to_id, amount, NOW()); RETURN TRUE; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE NOTICE 转账失败: %, SQLERRM; RETURN FALSE; END; $$ LANGUAGE plpgsql;3.2 性能监控与可观测性现代系统必须具备三大可观测性Metrics指标、Logs日志、Traces链路追踪。-- 慢查询监控与分析 -- 开启慢查询日志 SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 0.5; -- 超过 500ms 的查询记录 -- 实时查看正在执行的慢查询MySQL SELECT id, user, host, db, command, time AS seconds, LEFT(info, 100) AS query_preview FROM information_schema.processlist WHERE command ! Sleep AND time 1 ORDER BY time DESC; -- 查询统计PostgreSQL SELECT query, calls, total_exec_time / 1000 AS total_seconds, mean_exec_time AS avg_ms, rows / calls AS avg_rows FROM pg_stat_statements WHERE calls 10 ORDER BY mean_exec_time DESC LIMIT 20; -- 表大小与索引使用情况 SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||.||tablename)) AS total_size, seq_scan AS full_scans, idx_scan AS index_scans FROM pg_stat_user_tables ORDER BY pg_total_relation_size(schemaname||.||tablename) DESC;3.3 测试策略单元测试 集成测试高质量代码离不开完善的测试覆盖。以下是 TiDB 推荐的测试实践-- 数据库测试使用事务回滚保证测试幂等 -- PostgreSQL 测试模式每次测试在事务中执行结束后回滚 BEGIN; -- 测试数据插入 INSERT INTO users (username, email, password_hash) VALUES (test_user, testexample.com, hashed_password); -- 验证插入结果 DO $$ DECLARE user_count INT; BEGIN SELECT COUNT(*) INTO user_count FROM users WHERE email testexample.com; IF user_count ! 1 THEN RAISE EXCEPTION Test failed: expected 1 user, got %, user_count; END IF; RAISE NOTICE Test passed: user inserted correctly; END $$; -- 测试唯一约束 DO $$ BEGIN BEGIN INSERT INTO users (username, email, password_hash) VALUES (test_user2, testexample.com, hash2); RAISE EXCEPTION Test failed: unique constraint not triggered; EXCEPTION WHEN unique_violation THEN RAISE NOTICE Test passed: unique constraint works correctly; END; END $$; ROLLBACK; -- 回滚所有测试数据不影响正式数据库3.4 生产部署清单上线前必检检查项具体内容优先级配置安全密钥不在代码中用环境变量或 VaultP0错误处理所有 API 有 fallback不暴露内部错误P0日志规范结构化 JSON 日志含 traceIdP0健康检查/health 接口K8s readiness/liveness probeP0限流保护API 网关或应用层限流P1监控告警错误率/响应时间/CPU/内存 四大指标P1压测验证上线前跑 10 分钟压测确认 QPS/延迟P1回滚预案蓝绿部署或金丝雀发布问题 1 分钟回滚P1四、常见问题排查4.1 TiDB 内存占用过高排查步骤确认泄漏存在观察内存是否持续增长而非偶发峰值生成内存快照使用对应工具Chrome DevTools / heapdump / memory_profiler比对两次快照找到两次快照间新增且未释放的对象溯源代码找到对象创建的调用栈确认是否被缓存/全局变量/闭包持有常见原因全局/模块级变量无限增长缓存无上限事件监听器添加但未移除定时器/interval 未清理闭包意外持有大对象引用4.2 性能瓶颈在哪里通用排查三板斧数据库explain 慢查询加索引缓存热点数据网络 IO接口耗时分布P50/P90/P99N1 查询问题CPU火焰图flamegraph找热点函数减少不必要计算五、总结与最佳实践学习 TiDB 的正确姿势先跑通再优化先让代码工作再根据性能测试数据做针对性优化了解底层原理知道框架帮你做了什么才知道什么时候需要绕过它从错误中学习每次线上问题都是提升的机会认真做 RCA根因分析保持代码可测试依赖注入、单一职责让每个函数都能独立测试关注社区动态订阅官方博客/Release Notes及时了解新特性和 Breaking Changes觉得有帮助点赞收藏关注持续更新 TiDB 实战系列。觉得有用的话点个赞收藏关注我持续更新优质技术内容标签TiDB | 性能优化 | 面试 | 索引 | 并发