1. 开工前的准备:Navicat版本、PostgreSQL服务与连接参数的坑
很多人以为在 Navicat 里新建 PostgreSQL 数据库,就是打开软件、右键、选“新建数据库”、填个名字点确定,三步走完事。实际用下来你会发现,真正卡住你的往往不是“新建数据库”这个动作本身,而是前面那一串连接前的准备工作。
先说我自己的经历。有次帮一个同事排查问题,他装好了 Navicat Premium 16,也装好了 PostgreSQL 14,结果打开连接配置界面填完 IP、端口、用户名、密码,一测试连接直接报“connection refused”。当时第一反应是服务没启动,结果查了半天,PostgreSQL 服务明明在跑。后来才发现,他下载的是 Navicat for MySQL,不是 Premium 版本,里面压根没有 PostgreSQL 的连接选项。
这里要给新手提个醒:Navicat 产品线分两种,一种是针对单一数据库的版本,比如 Navicat for MySQL、Navicat for PostgreSQL,另一种是 Navicat Premium,一个客户端统一连多种数据库。如果你要同时管 MySQL 和 PostgreSQL,直接上 Premium 版本,别装单库版,否则切来切去烦死你。
1.1 连接前的服务端检查清单
不管你是连本地库还是远程库,连接不上时先按这个顺序排查,能省下大量无用功:
- 服务是否真的在运行:Linux 上用
systemctl status postgresql查看,Windows 上在“服务”管理器中确认 postgresql-x64-14 这类服务处于“正在运行”状态。很多人装了 PostgreSQL 之后从来没启动过服务,这属于最常见的问题。 - 监听地址是否正确:PostgreSQL 默认只在本地回环地址 127.0.0.1 上监听,远程连接必须修改配置文件
postgresql.conf,将listen_addresses改为'*'或者指定网卡 IP。很多教程里没强调这一步,导致远程连不上。 - 端口是否被占用或者没放行:默认端口 5432。本地可以用
netstat -ano | findstr 5432或者ss -tlnp | grep 5432验证。云服务器的话,还要在安全组里放行 5432 端口,这件事最容易漏,因为你在本机怎么测都是通的,换台机器就不通。 - 认证方式是否匹配:
pg_hba.conf文件决定了哪些 IP 可以用什么方式认证。默认配置文件里通常只有本地127.0.0.1/32和::1/128的信任或 md5 认证。如果你想用 Navicat 远程连,需要加一条类似host all all 0.0.0.0/0 md5的规则。注意这个文件修改后要重启服务才生效。
这套检查流程我后来整理成了自己的固定套路,任何“连接不上”的问题,按服务状态、监听地址、端口放行、认证规则这个顺序走一遍,90% 的情况在十分钟内能定位到原因。
1.2 Navicat 里创建连接时的几个隐藏细节
打开 Navicat,点击左上角“连接”,选 PostgreSQL,会弹出连接配置窗口。这里有几个字段是初学者容易填错的:
- 连接名:这个只是你在 Navicat 里给这个连接起的别名,随便填,比如“本地PG测试库”,跟数据库实际名字没关系,很多人在这里被绕晕。
- 主机:如果是本地连接,填
localhost或127.0.0.1;远程服务器填服务器的 IP 或域名。不要填postgres这种数据库用户名的值,这两个概念完全不一样。 - 端口:默认 5432,如果你当时安装 PostgreSQL 时改过端口,这里必须一致。我之前见过有人把端口填成 3306(MySQL 默认端口)来连 PostgreSQL,结果自然是连不上。
- 初始数据库:这里会默认填
postgres,它表示连接建立后默认进入的数据库。PostgreSQL 在安装时会自动创建一个名为postgres的默认数据库,这个库通常用来做管理操作。你可以选择保留默认值,连接后再新建其他库;也可以直接填你要连接的某个已有数据库名。 - 用户名和密码:默认超级用户是
postgres,密码是安装时你自己设置的。这里要注意,PostgreSQL 的用户名是区分大小写的,Postgres和postgres不是同一个用户。
填完之后,点击“连接测试”,看到“连接成功”提示再点“确定”。如果这里就报错了,仔细读一下报错信息,Navicat 的报错提示还算友好,把关键英文复制到搜索引擎里基本都能找到解决办法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 新建数据库时那些默认选项,每一个都值得你多看一眼
连接建好之后,双击左侧连接名展开数据库列表,右键选择“新建数据库”,这时候你看到的是一个有三个标签页的窗口:常规、高级、权限。别急着在“数据库名”那里输入名字点保存,先把这个界面里每个选项的含义搞清楚。
很多教程对这一步的处理方式是“默认就行”,但我负责任地告诉你:这里的默认选项,有几个是必须改的,有几个是必须理解的,否则后面会踩大坑。
2.1 常规标签页:数据库名、字符集与排序规则怎么选
常规标签页里涉及这几个字段:
- 数据库名:命名规则建议小写字母加下划线,比如
user_order_db、blog_system。PostgreSQL 对未加引号的标识符会自动转为小写,如果你用大写字母命名,实际存储的名字是小写,后续操作容易产生混淆。我见过有人建库名用中文的,虽然 PostgreSQL 支持,但在 JDBC 连接串、命令行工具里会出现编码问题,能不用就不用。 - 字符集:这个字段对应
ENCODING,PostgreSQL 支持多种字符集。中文业务系统强烈建议选UTF8,它能覆盖世界上绝大多数文字的存储需求。如果你的应用是纯英文的,选SQL_ASCII也不是不行,但一旦后续需要存中文,你就等着哭吧。 - 排序规则:对应
LC_COLLATE,这个字段决定了字符串比较时的排序规则。中文系统选Chinese (Simplified)_China.936或者zh_CN.UTF-8(取决于服务器系统),如果选错,常见的症状是按拼音排序的列表顺序错乱、大小写不敏感查询行为异常。 - 字符分类:对应
LC_CTYPE,决定字符分类方式,比如哪些字符被认为是字母、哪些是数字。一般跟着排序规则一起选对应的区域设置即可,不用单独纠结。
这里有一个极其重要的细节:PostgreSQL 创建数据库时一旦指定了编码、排序规则和字符分类,后期是不能直接修改的。如果你一开始选错了编码,唯一的办法是删库重建。所以创建前多花两分钟想清楚,比事后折腾几小时划算得多。
2.2 高级标签页:模板库、表空间这些参数什么情况下要动
高级标签页里有一个容易被忽略的选项:模板数据库(Template)。
PostgreSQL 有一个“模板库”机制——创建数据库时,实际上是基于一个模板库进行克隆。系统默认提供两个模板库:template0 和 template1。template1 是默认模板,你往 template1 里装了一些公共扩展或者修改了某些默认设置,之后新建的所有数据库都会继承这些内容。
什么时候需要手动选择模板库?一个典型场景是:你想让新库默认启用某个扩展,比如 postgis,就可以在 template1 里先安装好,后面新建的库自动就有这个扩展。但如果你不想让新库带上一堆模板里的额外对象,选 template0 更干净。
表空间(Tablespace)这个字段,单机小项目完全不用管,默认的 pg_default 就够用。只有当你把数据目录规划到不同磁盘、做冷热数据分离时,才需要先创建独立的表空间,然后在这里指定。
高级标签页里还有一个“兼容性”选项,可以选 PostgreSQL 的版本。这个我建议默认就行,不用刻意调低兼容版本。
2.3 权限标签页:创建时顺便把业务账号的权限给了
很多人建完库之后才想起来要创建业务账号,然后再去授权,绕了一圈。实际上 Navicat 的“权限”标签页可以在新建数据库的同时完成授权操作。
在这个标签页里,勾选你要授权的用户,然后在下方勾选权限项。常见的权限配置策略是:业务应用账号给 SELECT、INSERT、UPDATE、DELETE 四种 DML 权限,再加 EXECUTE(存储过程执行权限);不给 DDL 权限,比如 CREATE、DROP、ALTER,防止应用被拖库后还能删表。
不过要注意,PostgreSQL 的权限体系比 MySQL 更细致也更绕,这里勾选的是数据库级别的权限。如果你希望业务账号只能操作某个 schema 里的某些表,等库建好之后再单独去 schema 或表级别配置,后面我会专门聊这个。
3. 为什么建好的库,业务代码却连不上
库建好了,Navicat 里能正常打开,能建表、能查数据,看起来一切正常。结果你的应用一启动,报错“FATAL: password authentication failed for user”或者“FATAL: no pg_hba.conf entry for host ...”,这时候很多人就懵了——我在 Navicat 里明明能连啊,为什么程序连不上?
这个问题的根源在于,Navicat 连接和业务代码连接,走的认证路径和参数配置可能完全不同。Navicat 是一次性测试时用的连接参数,而业务代码里是另一套参数。我梳理了几个最常见的坑,你对照排查一下。
3.1 pg_hba.conf 的认证规则到底在管什么
pg_hba.conf 文件是 PostgreSQL 的客户端认证配置文件,全称是 “host-based authentication”。它定义了什么样的客户端、从哪个 IP 来、用什么数据库名和用户名,可以通过什么样的认证方式登录。
这个文件是按顺序从上到下匹配的,第一条匹配的规则生效,后面的规则即使更具体也不会被考虑。这是最容易踩坑的地方。举个例子,如果你在文件开头写了一条 host all all 0.0.0.0/0 reject,那后面再加 host all all 192.168.1.0/24 md5 也没用,因为前面那条规则已经把所有来源的 IP 都拒绝了。
Navicat 和业务代码连不上时,排查思路是:先看连接请求是从哪个 IP 发出来的,再对应看 pg_hba.conf 里匹配到哪条规则,认证方式是什么。常用的认证方式有:
trust:完全信任,不需要密码,安全性低,仅建议本地开发用。md5/scram-sha-256:密码加密认证,推荐使用,是生产环境的常规选择。reject:直接拒绝连接。peer:仅限本地连接,要求操作系统用户名和数据库用户名一致,这种认证方式经常让远程 Navicat 连接失败。
如果你的业务应用和数据库在同一台机器上,你可以用本地 socket 连接,用 peer 认证;如果应用在另一台服务器上,必须用 host 加密码认证方式。
3.2 连接串常见的拼写错误与 URL 编码问题
业务代码连不上,除了服务端认证配置问题以外,客户端连接串本身也藏着不少细节。
PostgreSQL 的 JDBC 连接串格式是:
code复制jdbc:postgresql://host:port/database?user=xxx&password=yyy&ssl=false
几个常见的翻车点:
- 连接串里用户名或密码包含特殊字符,比如
@、/、#,必须做 URL 编码,否则解析时直接出错。@要写成%40,/写成%2F,#写成%23。 - 主机名后面跟了额外的路径或空格,通常是从配置中心或环境变量复制粘贴时带进来的隐藏字符,肉眼看不见,但连接就是失败。建议拿十六进制查看器或者编辑器的“显示空白字符”功能检查。
- 数据库名填错:很多人把数据库名填成了连接名或者用户名,比如 Navicat 里连接名是“本地PG”,填连接串时也写
jdbc:postgresql://localhost:5432/本地PG,实际上应该填的是你在 Navicat 里“新建数据库”时输入的那个名字。 - SSL 参数不匹配:如果 PostgreSQL 服务端开了强制 SSL,而你的连接串没有配置
ssl=true,服务端会拒绝连接。反之,如果服务端没开 SSL,而连接串带了ssl=true&sslmode=require,也会报错。
我记得有一次调试一个 Python 应用,用的是 psycopg2,连接串里密码含有一个 % 字符,我忘了做转义,结果报错信息一直在提示“invalid dsn: invalid connection option”,折腾了半天才发现是 % 被当成了参数占位符。这类问题排查起来真的很费劲,因为报错信息往往指向的是连接参数解析,而不是密码本身的错误。
3.3 连接池把数据库连接数打满怎么办
还有一种“连不上”不是认证问题,而是连接数达到了上限。PostgreSQL 默认的 max_connections 是 100,在小规模场景下够用,但一旦并发上来,连接池里的连接数超过这个阈值,新的连接请求就会直接报“sorry, too many clients already”。
这个问题我在生产环境遇过不止一次。应用侧用的连接池(比如 HikariCP、Druid)默认配置往往偏激进。以 HikariCP 为例,maximumPoolSize 默认是 10,如果部署了十几个微服务实例,每个实例都开 10 个连接,那 PostgreSQL 的 100 个连接上限很快就会被打满。
解决办法有两个维度:
- 调整连接池参数:根据业务实际并发量合理设置
maximumPoolSize和minimumIdle,不要无脑调大。连接池不是越大越好,每个连接都要占用 PostgreSQL 后端进程的内存资源,连接太多反而会拖慢数据库性能。 - 调整服务端连接数:修改
postgresql.conf里的max_connections,比如调到 300 或 500。注意这个修改需要重启服务才能生效,并且要考虑系统内存是否能支撑这么多后端进程。每个连接大约占用几 MB 到十几 MB 内存,连接数越大内存开销越高。
另外我建议在业务配置里加上连接池的健康检查参数,比如 connectionTimeout 和 validationTimeout,避免连接池把已失效的连接交给应用,引发一串莫名其妙的报错。
4. 权限这件事:建库不等于有权限,owner和schema比想象中重要
PostgreSQL 的权限模型和 MySQL 有明显区别。如果你是从 MySQL 转过来的,刚开始用 PostgreSQL 时一定会被搞晕——明明我已经给用户授权了,为什么它还是无法建表?为什么能看到数据库却看不到里面的表?
这背后是 PostgreSQL 的权限分层机制在起作用。剥开来看,其实并不复杂。
4.1 数据库、Schema、表三层的权限逻辑
PostgreSQL 的权限体系大致分三层:数据库层、Schema 层和表/视图层。
- 数据库层:控制谁能连接到这个数据库,谁能在这个数据库里创建 Schema。授予数据库权限并不等于授予了 Schema 或表的权限。
- Schema 层:控制谁能在这个 Schema 里创建对象(表、视图、函数等),以及谁能使用这个 Schema 里的对象。PostgreSQL 默认的 Schema 是
public,每个新建的数据库里都会自动创建这个 Schema。 - 表/视图层:控制谁能对具体的表执行 SELECT、INSERT、UPDATE、DELETE、TRUNCATE、REFERENCES 等操作。
所以经常出现的情况是:你给一个用户授予了数据库的 CONNECT 权限,他能连上库,但在 public Schema 下建不了表,因为他对 Schema 没有 CREATE 权限;又或者他能看到 Schema 列表,但打开 Schema 后看不到任何表,因为对 Schema 没有 USAGE 权限。
在 Navicat 的图形界面里,你可以右键数据库 →“对象属性”或右键某个 Schema →“属性”,在里面比较直观地管理权限勾选。但我建议你还是要理解背后的 SQL,因为 Navicat 操作最终也是翻译成 SQL 去执行的。手动用 SQL 授权的方式是:
sql复制-- 授予连接到数据库的权限
GRANT CONNECT ON DATABASE mydb TO app_user;
-- 授予在 public schema 上创建对象的权限
GRANT CREATE, USAGE ON SCHEMA public TO app_user;
-- 授予指定表的所有 DML 权限
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.orders TO app_user;
4.2 Owner(属主)到底意味着什么
在 PostgreSQL 里,每个数据库、每个 Schema、每张表都有一个 owner(属主),通常是创建它的那个用户。Owner 拥有对该对象的最高控制权,包括删除它、修改它的结构、向其他用户授权等。
一个非常容易踩的坑是:你用 postgres 超级用户建了一个库,然后在 Navicat 里用另一个普通用户连过来,发现普通用户什么都干不了。原因很简单——这个库的 owner 是 postgres,不是普通用户。普通用户虽然能连接到库,但他不是 owner,默认不拥有库里的任何权限。
解决办法是在创建数据库时直接指定 owner:
sql复制CREATE DATABASE mydb OWNER app_user;
或者在 Navicat 新建数据库窗口的“权限”标签页里把相关用户加进去。不过我更推荐建库后用 ALTER DATABASE 语句调整 owner:
sql复制ALTER DATABASE mydb OWNER TO app_user;
建完库之后,如果已经建了一些表,而这些表的 owner 仍然是原来的用户,可以用下面的语句批量把表权限改到新用户:
sql复制-- 把 public schema 下所有表的所有权转移给 app_user
ALTER TABLE public.tablename OWNER TO app_user;
PostgreSQL 没有一条命令直接批量修改所有制表 owner 的命令,需要用一个循环脚本去遍历执行,实际操作中比较繁琐。这也是为什么我一直强调:建库时就把 owner 设置对,比事后去改省心太多。
4.3 生产环境里权限最小化的实操模板
我个人的习惯是,生产环境数据库绝不直接用 postgres 超级用户跑业务,而是创建两个账号:
- 一个 DDL 账号(比如
dev_admin),用于建表、改表结构,只在发布窗口由运维或开发使用。 - 一个 DML 账号(比如
app_runtime),用于应用日常增删改查,只授 SELECT、INSERT、UPDATE、DELETE 权限。
具体操作可以这样规划:
第一步:删除默认的 public 访问权限
新版 PostgreSQL 默认授予所有用户对 public Schema 的 CREATE 权限,这在多租户场景下是个安全隐患。建议收紧:
sql复制REVOKE CREATE ON SCHEMA public FROM PUBLIC;
第二步:新建业务 Schema
不要把业务表直接放在 public 里,而是新建一个专门的 Schema:
sql复制CREATE SCHEMA business;
第三步:按功能分配权限
sql复制-- DDL 账号
GRANT USAGE ON SCHEMA business TO dev_admin;
GRANT CREATE ON SCHEMA business TO dev_admin;
-- 应用账号
GRANT USAGE ON SCHEMA business TO app_runtime;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA business TO app_runtime;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA business TO app_runtime;
注意:ON ALL TABLES IN SCHEMA 这个语句只对执行时已经存在的表生效,以后新建的表不会自动带上权限。如果你希望后续新建的表自动授予权限,需要修改 Schema 的默认权限:
sql复制ALTER DEFAULT PRIVILEGES IN SCHEMA business GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_runtime;
这一步我在最开始总是忘记,导致每次加完新表之后应用就报权限不足,虽然每次都很快就定位到原因,但次数多了也烦。后来我干脆把这个语句写进了发布流程里。
5. 数据库跑稳之后,这些运维动作才是真正的考验
新建数据库这件事,从“能连上”到“能建表”到“能跑业务”,中间的距离比很多人想象的要长。而且等到数据库真正跑在生产环境里,新的麻烦会接踵而至。这里分享几个高频出现的问题,以及我的应对经验。
5.1 备份和恢复:Navicat可视化备份与命令行工具的取舍
Navicat 提供图形化的备份功能,右键数据库 →“备份” →“新建备份”,会调用 PostgreSQL 的 pg_dump 工具来导出。恢复时右键数据库 →“恢复” → 选择备份文件即可。
Navicat 的备份功能适合小规模数据库和临时性备份,简单直观。但是在自动化运维场景里,我更推荐直接写脚本调 pg_dump 和 pg_restore,这样可以配合 crontab 做定时备份。
常用备份命令:
bash复制# 导出整个数据库(自定义格式,适合做恢复)
pg_dump -h localhost -U postgres -F c -b -v -f mydb.backup mydb
# 导出纯 SQL 脚本(适合做迁移和结构比对)
pg_dump -h localhost -U postgres -f mydb.sql mydb
恢复命令:
bash复制# 从自定义格式备份恢复
pg_restore -h localhost -U postgres -d mydb -v mydb.backup
# 从 SQL 脚本恢复
psql -h localhost -U postgres -d mydb -f mydb.sql
这里有几个实践中的要点:
pg_dump默认导出的是当前时刻的一致性快照,不需要停服务,这点比 MySQL 的mysqldump在某些场景下更友好。- 如果数据库很大,建议用
-F c自定义格式,方便后面用pg_restore选择性地恢复某些表。 - 备份文件一定要加上时间戳,比如
mydb_$(date +%Y%m%d_%H%M%S).backup,否则会覆盖之前的备份。 - 恢复操作前必须确认目标数据库已经存在,否则
pg_restore会报“database does not exist”。
另外,定期做恢复演练很有必要。我见过不少团队定时备份做了半年,结果生产环境真要恢复时才发现备份文件早就损坏了,原因可能是磁盘满了、备份过程中断、或者是备份脚本本身的 bug。备份有效性的唯一检验标准,就是你真的把它恢复成功过一次。
5.2 连接数打满的另一个隐藏原因:空闲事务与长事务
前面提到过 max_connections 打满的问题,除了业务并发量确实大之外,还有一个隐蔽的元凶——空闲事务没有及时提交或回滚。
PostgreSQL 的事务机制下,如果一个连接开启事务后长时间不提交,它会一直占着连接和锁资源。假设你的应用代码里有这样的模式:
python复制conn = psycopg2.connect(...)
cur = conn.cursor()
cur.execute("SELECT * FROM orders WHERE order_id = 123 FOR UPDATE")
# 这里做了一个很耗时的处理,比如调外部 API
# 期间事务一直没提交
如果这个耗时的处理过程是 30 秒,并且高峰期有 20 个并发请求,那就有 20 个连接同时被这种“半截事务”占着。业务的正常查询反而拿不到连接。
排查这类问题的方式是在数据库侧查看当前会话状态:
sql复制SELECT pid, usename, state, query_start, xact_start, now() - xact_start AS xact_age
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY xact_start;
重点关注 xact_start 时间特别早但 state 不是 idle 的会话,这些就是长事务的源头。定位到具体会话后,如果确认是异常会话,可以用 pg_terminate_backend(pid) 强制终止。
预防这类问题的思路是:应用代码里尽量把事务范围控制在最小,不要在一个事务里做耗时外部调用;同时设置 PostgreSQL 参数 idle_in_transaction_session_timeout,比如设为 60 秒,超过这个时间的空闲事务自动断开:
bash复制ALTER SYSTEM SET idle_in_transaction_session_timeout = '60s';
SELECT pg_reload_conf();
5.3 慢查询与死锁:Navicat 提供的基础能力够用吗
Navicat 有“监视”和“查询编辑器”功能,可以查看当前查询、终止会话、查看服务器状态等。对于日常运维来说,这些基础能力勉强够用,但遇到棘手的性能问题,还是得回到命令行工具和系统视图上。
PostgreSQL 自带一个很有用的扩展 pg_stat_statements,启用后可以统计 SQL 语句的执行次数、总耗时、平均耗时、缓冲区读取等指标。启用方式:
sql复制CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
然后在 postgresql.conf 中设置:
code复制shared_preload_libraries = 'pg_stat_statements'
修改后需要重启 PostgreSQL 服务。之后就能通过下面的语句查看最耗时的 SQL:
sql复制SELECT query, calls, total_exec_time, mean_exec_time, rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;
死锁问题通常先通过应用日志观察报错,再去 pg_stat_activity 里查看阻塞关系。常见的死锁原因是多张表加锁顺序不一致,比如事务 A 先锁表 T1 再锁表 T2,事务 B 先锁 T2 再锁 T1,两个并发跑就死锁。解决思路是统一所有事务的加锁顺序,或者把事务拆小,减少锁的持有时间。
5.4 文件与数据目录:PostgreSQL 的数据映射到宿主机
如果你是用 Docker 部署 PostgreSQL,这个问题一定会遇到:容器删除了,数据也跟着没了。正确做法是把数据库的数据目录映射到宿主机的一个持久化目录。
以 Docker Compose 为例:
yaml复制services:
postgres:
image: postgres:14
container_name: my_pg
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: your_password
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- /data/postgres:/var/lib/postgresql/data
这里的关键是把宿主机目录 /data/postgres 映射到容器内的数据目录 /var/lib/postgresql/data。需要注意,**PostgreSQL 官方镜像在首次启动时会检查这个目录是否为空,如果是空目录,它才会初始化数据库集群;如果目录已经非空且里面没有有效的 PostgreSQL 数据文件,容器会启动失败。**所以不要提前往映射目录里放无关文件。
docker-compose 管理 PostgreSQL 时,还有一个容易忽略的点:POSTGRES_DB 环境变量只会在首次初始化数据目录时创建数据库。如果你改了项目要新建一个库,正确的做法是先检查容器是否已经初始化过,然后再决定是直接重建容器还是进容器执行 createdb。不要一改 POSTGRES_DB 就重新 docker-compose up -d,那样数据目录不会重新初始化,新库不会被创建。
6. 从 Navicat 新建数据库到生产级数据库,你需要补上这些认知
回到最开始的问题:在 Navicat 里新建 PostgreSQL 数据库,这个操作的入门门槛真的不高,但你用十分钟建完一个库之后,后面的路还有很长。数据库不是建完就完事的,它需要被正确地配置、授权、备份、监控和优化。
如果你是从 MySQL 转过来的,我特别提醒你注意 PostgreSQL 的几个不习惯的地方:
- 默认情况下 PostgreSQL 对表名的处理是转小写,除非你用双引号强制指定大小写,否则你写的
UserInfo表实际叫userinfo。 - PostgreSQL 的
SERIAL自增列和 MySQL 的AUTO_INCREMENT不同,它是用序列(Sequence)实现的,你在导出表结构时会看到多一个序列对象。 - 字符串比较时,空字符串
''和NULL在 PostgreSQL 是完全不同的概念,''是有值的,只是长度为 0,而NULL表示未知,排序和唯一索引的行为也不一样。 LIMIT和OFFSET的语法两者一致,但 PostgreSQL 的分页优化场景更适合用 keyset pagination,也就是WHERE id > last_id ORDER BY id LIMIT 20,大数据量下性能差别非常明显。
这些差异在你用 Navicat 建库时感觉不出来,但一旦业务代码跑起来,各种“数据行为不正常”的问题就会冒出来。提前知道这些点,至少能少踩几个坑。
如果你刚开始接触 PostgreSQL,我建议你不要只依赖 Navicat 的图形界面,抽时间把 psql 命令行工具用熟。Navicat 是日常快捷操作的利器,但命令行能让你更深入理解 PostgreSQL 的运行机制。尤其在对线上库做结构变更、查慢查询、看锁等待时,命令行往往比图形界面更高效。
最后分享一个我自己的使用习惯:每次新建数据库时,我都会专门建一个文本文件,记录这个库的连接信息、创建时间、owner、用途、备份策略、预计数据量等。等三个月后回来看,你会发现这份文档比你能记住的所有细节都值钱。尤其是当你同时管理七八个数据库实例时,没有这份记录,靠脑子回忆每个库的用途和配置,非常容易出岔子。
