上一篇写了PostgreSQL的学习路线,看起来很顺利。但真实的学习过程远没有那么轻松,我好几次差点放弃。本文如实记录学习过程中踩过的坑和遇到的困难,以及我是如何克服的,给想学PG的朋友一个真实的参考。

一、为什么差点放弃

我是一个用了多年MySQL的后端开发,学PostgreSQL的初衷是想拓展技术栈。刚开始的时候信心满满,觉得不就是换个数据库嘛,SQL都差不多。但真正学进去之后,才发现差距比想象的大,好几次都想放弃。

总结一下,差点放弃的原因有几个:概念太多、和MySQL的差异比想象的大、配置复杂、调试困难、资料相对少。下面一个个说。

二、踩过的坑

1. 安装和配置就花了半天。

MySQL安装很简单,基本上下一步下一步就好了。PostgreSQL的安装也不难,但配置让我头疼了很久。

比如pg_hba.conf这个文件,控制客户端认证方式。我装完之后用psql连接,一直报"Peer authentication failed",搞了半天才知道是因为默认用了peer认证,需要改成md5或trust。这个问题在MySQL里根本不会遇到。

还有postgresql.conf,配置项多得吓人,几百个参数,每个参数什么意思、默认值是多少、该怎么调,完全摸不着头脑。不像MySQL,核心配置就那几个。

最后我花了半天时间,查了很多资料,才把基本配置搞明白,能正常连接和使用了。

2. 大小写和引号的坑。

MySQL默认不区分大小写,表名、字段名随便写。PostgreSQL默认会把未加引号的标识符转成小写,如果你建表的时候用了双引号定义大写字段名,查询的时候也必须用双引号,否则会报错。

我一开始没注意这个,建表的时候用了双引号,后来查询一直报字段不存在,查了很久才发现是大小写的问题。这个坑让我浪费了不少时间。

还有字符串的引号,MySQL里单引号双引号都可以表示字符串,PostgreSQL里只能用单引号,双引号是标识符。这个差异也让我犯了好几次错。

3. 自增主键的变化。

MySQL里用AUTO_INCREMENT很简单。PostgreSQL里用SERIAL或BIGSERIAL,本质是创建一个序列。PG 10之后推荐用GENERATED AS IDENTITY,更标准。

我一开始用了SERIAL,后来发现插入数据的时候如果显式指定了ID,序列不会更新,导致后续插入报主键冲突。这个问题在MySQL里不会遇到,因为AUTO_INCREMENT会自动调整。

后来查了资料才知道,需要用setval手动更新序列,或者用GENERATED AS IDENTITY。又是一个坑。

4. 分页的坑。

MySQL里分页用LIMIT offset, size,很方便。PostgreSQL也支持LIMIT和OFFSET,但OFFSET很大的时候性能很差,这一点和MySQL一样。

但PG有一个坑:如果用了ORDER BY但排序字段不唯一,分页可能会出现重复或遗漏的数据。因为排序不稳定,相同值的行顺序不确定。解决方法是ORDER BY后面加一个唯一字段,比如id。这个问题我也是踩过坑之后才知道的。

5. 日期和时间的差异。

MySQL的日期时间函数和PG的差异很大。比如获取当前时间,MySQL用NOW(),PG用NOW()或CURRENT_TIMESTAMP,这个还好。但日期格式化、日期计算、时区处理,差异就大了。

比如DATEFORMAT在PG里没有,要用tochar。DATEADD和DATESUB在PG里要用interval运算。这些函数名和用法的差异,让我写SQL的时候经常要查文档,效率很低。

还有时区处理,PG的时区功能比MySQL强大,但也更复杂。timestamp with time zone和timestamp without time zone的区别,我搞了很久才明白。

6. 存储过程的语法差异。

MySQL的存储过程语法和PG的PL/pgSQL差异很大。我之前写过一些MySQL的存储过程,想迁移到PG,发现几乎要重写。

变量声明、条件判断、循环、异常处理、返回值,语法都不一样。比如MySQL里用DECLARE声明变量,PG里也是DECLARE,但位置和用法不同。MySQL里用IF...THEN...END IF,PG里也是,但细节有差异。

最让我头疼的是,PG的函数和存储过程是分开的,函数用CREATE FUNCTION,存储过程用CREATE PROCEDURE(PG 11之后才支持)。函数不能执行事务控制,存储过程可以。这个概念我花了很久才理清。

7. 性能调优无从下手。

MySQL的性能调优我比较熟悉,慢查询日志、EXPLAIN、索引优化,都有一套成熟的方法。PostgreSQL的性能调优让我无从下手。

首先,PG的执行计划比MySQL复杂,输出信息更多,刚开始根本看不懂。什么是Seq Scan、Index Scan、Bitmap Heap Scan、Nested Loop、Hash Join、Merge Join,这些概念需要一个个去学。

其次,PG的配置参数太多,哪些参数对性能影响大,该怎么调,完全没有头绪。sharedbuffers、workmem、maintenanceworkmem、effectivecachesize,每个参数的含义和最佳值,查了很多资料还是一知半解。

最后,PG的性能监控工具不如MySQL丰富。MySQL有很多成熟的监控工具,PG的工具相对少一些,需要自己搭。

8. 资料和社区相对少。

MySQL的资料非常多,遇到问题搜一下基本都能找到答案。PostgreSQL的中文资料相对少一些,很多问题需要查英文文档或邮件列表。

尤其是PG 14是新版本,beta阶段的资料更少。遇到新版本的问题,经常搜不到答案,需要自己看源码或提issue。这对初学者来说很不友好。

三、我是如何坚持下来的

虽然踩了很多坑,好几次想放弃,但最终还是坚持下来了。分享几个对我有帮助的方法。

1. 接受差异,不要带着MySQL的思维学PG。

这是最重要的一点。不要总想着"MySQL里是怎么做的,PG里应该也差不多"。PG和MySQL是两个不同的数据库,设计理念不同,用法也不同。接受差异,从零开始学,反而学得更快。

我后来调整了心态,不再拿MySQL的标准来要求PG,而是尝试理解PG为什么这么设计。理解了设计理念之后,很多看似奇怪的用法就变得合理了。

2. 官方文档是最好的老师。

PG的官方文档写得非常详细,虽然是英文的,但只要耐心读,大部分问题都能找到答案。我后来遇到问题,第一反应不是搜百度,而是查官方文档,效率反而更高。

官方文档还有一个好处,就是准确。网上的博客和教程可能过时或有错误,但官方文档是最权威的。

3. 从小项目开始实践。

光看文档学不进去,我找了一个小项目来练手——用PG重写一个之前用MySQL的小工具。在实践中遇到问题,解决问题,进步很快。

这个小项目虽然简单,但涉及了建表、查询、索引、事务、存储过程等常用功能。做完这个项目,我对PG的基本用法就比较熟悉了。

4. 加入社区,多问问题。

我加入了几个PG的中文社区,遇到不懂的问题就问。社区里的大佬都很热心,很多问题很快就能得到解答。而且看别人的问题和回答,也能学到很多东西。

不要怕问"傻问题",每个人都是从新手过来的。只要你自己先查过文档、试过解决,再去问,大家都会愿意帮你。

5. 不要追求一次学会所有功能。

PG的功能太多了,JSON、全文搜索、地理信息、分区表、复制、高可用...不可能一次全部学会。我给自己定的目标是先掌握核心功能,能满足日常开发需求就行,高级功能等需要的时候再学。

这样压力小了很多,学习也更有针对性。

四、现在的感受

坚持学了两个月之后,我对PG的看法发生了变化。

刚开始觉得PG复杂、难用、不如MySQL顺手。现在发现,PG的"复杂"其实是"强大"的代价。很多MySQL里需要用应用层实现的功能,PG原生就支持,比如JSON查询、全文搜索、窗口函数、CTE等。用好了这些功能,可以大大简化应用层的代码。

而且PG的SQL标准兼容性更好,很多复杂查询在PG里写起来更自然。性能也很稳定,在复杂查询和大数据量下表现比MySQL好。

当然,PG也不是完美的。它的配置和运维确实比MySQL复杂,生态工具也不如MySQL丰富。但只要度过了最开始的学习曲线,就会发现它是一个非常优秀的数据库。

五、给想学PG的人的建议

如果你也想学PostgreSQL,我的建议是:

  1. 做好心理准备。 PG的学习曲线比MySQL陡,前几周会很痛苦,坚持过去就好了。
  2. 从零开始学。 不要带着MySQL的思维,把PG当成一个全新的数据库来学。
  3. 多读官方文档。 官方文档是最好的学习资料,不要只依赖中文博客。
  4. 多动手实践。 找一个小项目练手,在实践中学习。
  5. 加入社区。 遇到问题多问,社区会给你很多帮助。
  6. 循序渐进。 先学核心功能,高级功能需要的时候再学。

六、写在最后

从入门到差点放弃,再到慢慢入门,我用了两个月时间。这个过程不算轻松,但很值得。

PostgreSQL是一个非常优秀的数据库,它的很多设计理念和功能特性,会让你对数据库有全新的认识。如果你是一个后端开发,我强烈建议学一学PG,它会让你的技术栈更完整,也会让你在做技术选型时有更多选择。

最后想说,学习新技术的过程中,遇到困难是正常的,想放弃也是正常的。关键是不要真的放弃,再坚持一下,过了那个坎,就会豁然开朗。

与所有正在学习新技术的朋友共勉。