昨晚加班加到十一点多,坐公司工位上那破转椅,已经困得快睁不开眼了,突然我们组那个小李拍我后背,说,哥,那个新项目不是要把MySQL全切成PostgreSQL了吗?你们搞后端的咋样了?我一听脑瓜就嗡嗡的——说实话,这活儿看着不难,真动手才知道,坑多得一比。
1. 数据类型不一样,光靠IDE自动生成,完了直接报错
先说第一个坑吧,这玩意一上来就给我下马威。MySQL 里什么TINYINT(1)啊,DATETIME啊,都写得顺顺的。结果切成 Postgres,一跑 migrate,tinyint根本没这玩意,变成 boolean 了都懵了。DATETIME也没,得改成timestamp或timestamptz,不然就是一堆 "data type not found"。
随手举个代码片段:
// MySQL
@Column(name = "is_deleted", columnDefinition = "TINYINT(1) DEFAULT 0")
// PostgreSQL
@Column(name = "is_deleted", columnDefinition = "BOOLEAN DEFAULT FALSE")
还有那个enum,MySQL 支持,Postgres 没有直接 enum 类型,得自己定义枚举类型,搞一堆 DDL...写得心态崩了。
2. 自增主键,天坑级别
本来MySQL自增直接AUTO_INCREMENT就完事,Postgres得用SERIAL,但现在都推荐GENERATED BY DEFAULT AS IDENTITY,还得分场景讨论,Spring Data JPA 里还得用 sequence。我们一开始偷懒没管,结果数据插入一片null,主键全没,直接报错。
代码改成这样:
// PostgreSQL推荐写法
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
之前写的那个MySQL习惯:
@GeneratedValue(strategy = GenerationType.AUTO)
直接不起作用,坑死个人。
3. SQL写法各种不兼容
我说个笑话,我们之前SQL里很多写法,LIMIT ?,?,MySQL的语法,Postgres不认,得变成LIMIT x OFFSET y,而且变量占位也不一样,MySQL是?,Postgres是
、2……一大堆改。
还有那个字符串拼接,MySQL直接concat,Postgres用||,我第一次上线的时候查日志查到快吐血。
-- MySQL
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM user;
-- PostgreSQL
SELECT first_name || ' ' || last_name AS full_name FROM user;
对了,那天凌晨测试,聚合函数 count、group by,全都崩,原因就因为 group by 规则更严格,得把 select 里用到的都带 group by。
4. 索引、唯一约束各种不同
有次在公司楼下抽烟,隔壁运维的哥们问我,你们切了Postgres性能咋样?我说别提了,MySQL建联合唯一索引,一句UNIQUE KEY就搞定,Postgres语法和效果看着差不多,实际上有细节,比如 null 值能不能唯一,MySQL和Postgres处理方式不一样——Postgres下唯一索引的列允许多个null,MySQL不行。
5. 大坑:SQL方言导致的JPA报错
我们老代码一堆JpaRepository的接口,HQL里拼SQL,MySQL下能跑的,一切到Postgres,错一半。分页Pageable那块,PageImpl有个bug,MySQL能查出count,Postgres就给你来个异常。
对了,那段时间我脑子里全是 StackOverflow 的一堆报错,啥org.postgresql.util.PSQLException: ERROR: syntax error at or near "?",眼熟吧?都是参数占位不对。
6. 大小写,表名、字段名惊喜连连
你猜怎么着,MySQL默认表名大小写不敏感,Postgres全都默认小写,而且你只要建表的时候用引号,比如"User",你后面所有SQL都得用引号,大小写完全一致才行,不然直接 not found。那种自动生成的脚手架代码,全给你坑进去。
7. JDBC 驱动、连接池,参数不一样
我们之前用的mysql-connector,切成Postgres,driver包换了,DataSource参数也不一样,有些连接属性,MySQL有的,Postgres没有,比如 characterEncoding 啥的。还得注意时区参数,光为这个,我们线上表都查过时差。
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=xxx
spring.datasource.password=xxx
spring.datasource.driver-class-name=org.postgresql.Driver
顺手提一句,性能参数别抄MySQL的,没啥用,Postgres自己一套。
8. JSON 字段,MySQL和Postgres写法都不一样
有表设计里头用到 JSON,MySQL直接json,Postgres得jsonb才好用,不然索引慢一倍。插入和查询语法也不一样。
9. 事务隔离级别,默认都不一样
MySQL和Postgres,隔离级别默认不是一个,MySQL默认Repeatable Read,Postgres默认Read Committed,很多看似正常的操作,结果不一样,真要在意一致性的,得自己配,不然出锅又是锅。
10. 工具链一堆配套都得重装
Navicat连MySQL和连Postgres体验完全不一样,别说一些小众ORM/数据库工具了,支持不支持Postgres,或者语法检测都出问题。
反正总结下来,这活儿表面就像改个JDBC URL,实际上一动一身汗,啥代码生成器、啥自动建表,信了全都坑死你。要真换库,建议先搞清楚SQL兼容性,表字段一一对比,测试用例一定要全,别以为Java那一套ORM能兜底,最后真的只能靠自己手动去抠各种小细节。