面试官总爱问数据库设计问题,这可不是为了刁难你。想想看,一个糟糕的数据库设计能让整个应用像蜗牛一样慢。规范化这个概念听起来很学术,但其实就是让数据存放得更合理。第一范式要求每个字段都是不可分割的原子值,第二范式要求消除部分依赖,第三范式则要消除传递依赖。听起来像绕口令?简单说就是别把地址和邮编塞在同一个字段里。
MySQL和PostgreSQL的对比就像可乐和雪碧的选择题。MySQL像那个永远靠谱的老朋友,简单直接速度快;PostgreSQL则像个瑞士军刀,功能丰富支持JSON查询。至于NoSQL,当你的数据像野马一样难以驯服时,MongoDB这类文档数据库可能就是你的套马杆。但记住,没有银弹,选择取决于你的数据特征和查询模式。
PDO还是MySQLi?这个问题就像问用筷子还是叉子吃面条。PDO支持多种数据库,预处理语句写起来更优雅;MySQLi专为MySQL优化,速度可能更快些。我见过有人为这个争论不休,其实只要别再用那个古老的mysql_函数,面试官应该都会给你加分。连接数据库时记得设置字符集,否则中文乱码会让你怀疑人生。
面试时写不出基础CRUD操作就像厨师不会用菜刀一样尴尬。SELECT、INSERT、UPDATE、DELETE这些看似简单的语句,却能看出你对数据库的理解深度。你知道UPDATE语句忘记加WHERE条件会发生什么吗?全表数据都会变成一样的值,这绝对是DBA的噩梦。我见过有人用PHP循环执行上千次INSERT,却不知道可以用一条多值INSERT语句搞定,这种低效操作会让面试官直摇头。
子查询和连接查询就像数据库界的左右互搏术。什么时候用JOIN,什么时候用子查询?JOIN通常性能更好,但当你要处理多层嵌套数据时,子查询可能更直观。LEFT JOIN和INNER JOIN的区别是什么?一个会保留左表所有记录,另一个只返回匹配的记录。GROUP BY配合聚合函数使用时,HAVING和WHERE的区别经常把人绕晕,前者过滤分组后的结果,后者过滤原始数据。
索引就像数据库的目录,但乱建索引比不建索引更可怕。EXPLAIN命令是查看查询执行计划的神器,它能告诉你数据库到底是怎么找数据的。全表扫描(ALL)和索引扫描(ref)的性能差距可能有几个数量级。复合索引的顺序很重要,就像查字典要先找首字母再找第二个字母。我曾经优化过一个查询,仅仅调整了索引列的顺序,执行时间就从2秒降到了0.02秒。
慢查询日志是发现性能问题的探照灯。常见问题包括:N+1查询问题(循环执行单个查询而不是批量获取)、缺少合适的索引、使用了全表扫描、不必要的数据排序。有个经典案例:一个页面加载需要5秒,最后发现是有人在循环里执行了COUNT(*)查询。记住,数据库最怕的不是数据量大,而是错误的查询方式。
面试官问"如何防止SQL注入"时,要是回答"用addslashes函数",基本就可以收拾东西回家了。预处理语句才是现代PHP开发的标配,PDO和MySQLi都支持这个功能。参数化查询不仅安全,还能提高性能,因为数据库可以复用执行计划。有趣的是,很多人知道用预处理,却不知道bindParam和bindValue的区别,前者是引用绑定,后者是值绑定,在循环中使用时这个区别特别关键。
事务处理就像数据库里的"后悔药"。ACID特性听起来高大上,其实就是原子性、一致性、隔离性和持久性的缩写。面试时经常被问到"什么时候该用事务",我的经验是:涉及多个表更新且需要保持数据一致性的操作。有个经典例子是银行转账,必须同时完成扣款和入账两个操作。记得设置合适的隔离级别,不然可能会遇到脏读、幻读这些诡异问题。我曾经调试过一个bug,就是因为没注意隔离级别导致数据不一致。
数据库连接池是个神奇的东西,它能避免频繁创建销毁连接的开销。PHP的持久连接(persistent connection)有点类似连接池的效果,但实际使用时要注意它可能导致的连接数暴涨问题。面试官可能会问"为什么不用单例模式管理数据库连接",这其实是个陷阱,单例在长时间运行的脚本中可能导致连接超时。现在流行用Swoole这类协程框架实现真正的连接池,那性能提升简直像坐火箭。
ORM框架让数据库操作变得像操作对象一样简单,但过度依赖ORM可能会让你忘记SQL怎么写。Laravel的Eloquent确实很优雅,但你知道它N+1查询问题怎么解决吗?答案是用with()方法预加载关联数据。Active Record和Data Mapper模式的区别也经常被问到,前者把数据和行为放在一起,后者则分离得清清楚楚。有个笑话:有个开发者用ORM写了复杂查询,最后生成的SQL有200多行,DBA看到直接辞职了。
当数据量超过单表千万级时,分库分表就成了必选项。水平分片和垂直分片是两种主要策略,前者按行拆分,后者按列拆分。我见过一个电商项目,用户表按用户ID的哈希值分到16个库,订单表则按时间范围分片。分片键的选择特别讲究,选不好可能导致"热点数据"问题。有个坑是跨分片查询,原本简单的联表查询可能变成噩梦,这时候就得考虑用全局表或者冗余字段了。
读写分离听起来像把大象装冰箱那么简单——主库写,从库读。但实际部署时,主从延迟能让你怀疑人生。记得有次用户刚下单就查订单,结果查不到,就是因为从库同步延迟。解决方案要么强制读主库,要么在代码里做延迟容忍。MySQL的GTID复制比传统复制更可靠,配合半同步复制可以大幅降低数据丢失风险。面试官可能会问"主从切换怎么做",这时候就要搬出MHA或者Orchestrator这些工具了。
Redis不只是个缓存,它简直是瑞士军刀。字符串、哈希、列表、集合、有序集合五种数据结构能玩出各种花样。缓存穿透、雪崩、击穿这三个概念总让人分不清,简单说:穿透是查不存在的数据,雪崩是缓存集体失效,击穿是热点key过期。布隆过滤器防穿透,随机过期时间防雪崩,互斥锁防击穿。Memcached虽然功能少,但在纯缓存场景下性能可能更好,它的多线程模型能榨干CPU性能。
处理亿级数据时,常规优化就像用勺子舀干大海。列式存储、压缩、冷热数据分离这些手段都得用上。有次优化报表查询,把TEXT字段拆到扩展表,查询速度直接从10秒降到0.1秒。分区表也是个神器,特别是按时间分区,查三个月数据只需要扫描三个分区而不是整表。ClickHouse这类OLAP数据库在分析场景下能比MySQL快100倍,但别指望它处理高并发写入。有时候最好的优化是重构业务逻辑——不去查就是最快的查询。
标签: #PHP后端面试数据库知识 #MySQL与PostgreSQL对比 #PDO与MySQLi选择技巧 #数据库索引优化方法 #防止SQL注入最佳实践