news 2026/10/1 3:50:43

Ubuntu下PostgreSQL服务状态检查:systemctl到pg_isready全攻略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu下PostgreSQL服务状态检查:systemctl到pg_isready全攻略

在Ubuntu上维护PostgreSQL,最频繁的一个操作就是看服务状态。无论是数据库连不上、应用报错、还是例行巡检,“PG服务现在到底是个什么状态”永远是第一个要回答的问题。这篇文章就把我在日常运维里用到的检查方法完整梳理一遍——从基础的systemctl命令,到pg_isready健康检查,再到写脚本做自动化判定,以及服务起不来时的一套排查思路。不管你是刚接触Linux运维的新手,还是已经在生产环境摸爬滚打一段时间的开发者,这些命令和思路都能直接用上。

1. 先搞清安装方式:不同装法对应不同检查命令

很多人一上来就敲systemctl status postgresql,结果提示Unit postgresql.service not found,然后就懵了。这个锅不怪命令,怪安装方式。

同一台Ubuntu机器上,PostgreSQL的安装方式不同,服务的管理方式完全不一样。检查服务状态之前,先花30秒搞清楚自己是怎么装的,能省掉后面一大串麻烦。

1.1 三种主流安装方式,对应完全不同的检查入口

最常见的三种安装方式:apt包管理器安装、源码编译安装、Docker容器部署。它们对应的服务管理方式几乎可以说是三个世界。

用apt安装是最省心的方式。Ubuntu仓库里的postgresql包会自带systemd服务单元,安装完成后自动注册开机启动,日常启停和状态检查都走systemctl那一套。需要注意,apt装的PostgreSQL版本不同,服务单元的名字也不同,Ubuntu 22.04默认装的是PostgreSQL 14,服务名是postgresql@14-main.service,而不是单纯的postgresql。

源码编译安装则是另一回事。你从postgresql.org下载源码自己make install的,编译过程根本不会生成systemd服务文件。这种安装方式下,服务管理靠的是PostgreSQL自带的pg_ctl工具,数据目录的启停、状态查看都由pg_ctl来完成。如果你在源码安装的环境里敲systemctl status postgresql,大概率会得到“Unit not found”,这不是命令不对,而是管理入口不对。

Docker方式最特殊。容器里的PostgreSQL进程对宿主机来说就是一个普通进程,服务状态要看容器的运行状态。docker ps看容器是否在跑,docker inspect看健康检查详情,docker logs看容器内日志。宿主机上的systemctl和容器内的pg_ctl都没法直接反映容器服务是否健康,得用Docker自己的命令体系。

三种方式的检查入口我用一张表理清楚:

安装方式服务管理入口典型检查命令状态含义来源
apt安装systemdsystemctl status postgresql@14-mainsystemd单元状态
源码编译pg_ctlpg_ctl -D /usr/local/pgsql/data statuspostmaster进程状态
Docker容器Docker CLIdocker ps/docker inspect容器运行状态与健康检查

1.2 怎么在30秒内确认自己的安装方式

判断安装方式其实很简单,几个命令一敲就清楚。先看psql命令从哪来:

which psql

如果输出/usr/bin/psql或者/usr/lib/postgresql/14/bin/psql,基本可以确定是apt装的。如果路径是/usr/local/pgsql/bin/psql或者你自己编译时指定的目录,那就是源码安装。

再用dpkg确认一下:

dpkg -l | grep postgresql

有输出说明系统里能查到postgresql相关的软件包记录,这通常是apt安装的标志。源码编译安装的话,这个命令大概率是空的(除非你同时装了postgresql-common之类的辅助包)。

查Docker更直接:

docker ps | grep postgres

有输出就说明你的PostgreSQL跑在容器里。这类环境还需要注意,宿主机上可能同时装了PostgreSQL客户端工具,但服务端根本不在宿主机上,检查逻辑完全不同。

结合进程路径一起看就更清楚了:

ps -ef | grep postgres

apt安装的进程路径一般是/usr/lib/postgresql/14/bin/postgres,源码安装则是/usr/local/pgsql/bin/postgres。看到进程路径,安装方式就藏不住了。

2. systemd体系下最标准的检查姿势

如果你的环境是apt安装的(这也是绝大多数Ubuntu服务器的情况),systemd就是管理PostgreSQL服务的第一入口。这一套命令不搞懂,后面排查问题会寸步难行。

2.1 systemctl status:一条命令看透服务全貌

systemctl status postgresql@14-main是日常巡检最常用的命令,输出信息量很大,我把关键字段拆开讲。

systemctl status postgresql@14-main

正常运行时输出大概是这样的:

● postgresql@14-main.service - PostgreSQL Cluster 14-main Loaded: loaded (/lib/systemd/system/postgresql@.service; enabled-runtime; vendor preset: enabled) Drop-In: /etc/systemd/system/postgresql@.service.d └─override.conf Active: active (running) since Thu 2024-01-18 09:32:11 CST; 3 days ago Main PID: 7321 (postgres) Status: "ready" Tasks: 8 (limit: 9449) Memory: 82.3M CPU: 15.203s CGroup: /system.slice/postgresql@14-main.service ├─7321 /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main ├─7326 "postgres: checkpointer" ├─7327 "postgres: background writer" ├─7328 "postgres: walwriter" ├─7329 "postgres: autovacuum launcher" ├─7330 "postgres: logical replication launcher" └─7331 "postgres: stats collector"

这里每行信息都有用。Loaded告诉你服务是否被正确加载、开机自启是否配置;Active是核心状态,active (running)表示服务正在运行;Main PID是PostgreSQL主进程的PID,后面追日志、看进程都要用到;Status: "ready"是PostgreSQL自己报告的健康状态,比systemd的active更精细;CGroup列表里那些子进程就是PostgreSQL的后台进程组,能直观看到checkpointer、walwriter这些进程是否都在。

Active字段除了active (running),还有几种常见状态:

Active状态含义常见场景
active (running)服务正在运行,主进程存活正常状态
active (exited)服务启动过一次但主进程已退出一次性任务或启动后立即崩溃
inactive (dead)服务已停止手动stop或系统关机
failed服务启动失败或运行中崩溃配置错误、端口占用、权限问题

2.2 适合脚本和快速判定的三个兄弟命令

systemctl status输出太详尽,不适合写进脚本自动判断。自动化检查场景下,我常用另外三个命令。

systemctl is-active是脚本写条件判断的首选:

systemctl is-active postgresql@14-main

正常输出active,停止输出inactive,启动失败输出failed。配合shell的if语句可以直接做状态判定,返回码也很有用,active时返回0,非active状态返回非0。

systemctl is-enabled专门查开机自启:

systemctl is-enabled postgresql@14-main

输出enabled表示开机自启,disabled表示不会自启,static表示单元没有安装/卸载逻辑、只能由其他单元触发。生产环境建议确认输出是enabled,否则服务器重启后PostgreSQL不会自动拉起,这个坑我踩过不止一次。

systemctl list-units | grep postgres用来总览所有跟PostgreSQL相关的服务单元:

systemctl list-units | grep postgres

这条命令能一次性列出所有已加载的postgresql单元,适合系统里装了多个版本(比如同时装了12和14)时,快速看清哪些集群在运行、哪些已停止。

日常启停、重启的命令顺带列一下,配合使用才完整:

sudo systemctl start postgresql@14-main # 启动 sudo systemctl stop postgresql@14-main # 停止 sudo systemctl restart postgresql@14-main # 重启 sudo systemctl reload postgresql@14-main # 重新加载配置,不中断连接

reload和restart的区别值得多说一句:reload只是让PostgreSQL重新读取配置文件,连接中的会话不受影响,适合改了postgresql.conf里不用重启才能生效的参数;restart则是杀掉所有进程重新拉起,所有连接都会断开。生产环境优先用reload,能少挨不少骂。

2.3 认识postgresql相关的三个unit文件

apt安装PostgreSQL后,systemd里会有几个相关的服务单元,名字容易搞混:

  • postgresql.service:总入口单元,它本身不做具体启停,而是拉起下面所有的版本实例
  • postgresql@.service:模板单元,不指定实例时无法单独使用
  • postgresql@14-main.service:具体实例单元,14是主版本号,main是集群名

实际管理的是第三个单元,前两个是辅助和模板。用systemctl cat postgresql@14-main可以查看这个单元的使用哪个配置文件和启动参数:

systemctl cat postgresql@14-main

输出里能看到ExecStart那一行,具体记录了postgres二进制路径和数据目录位置。确认服务配置对不对,这条命令最直接。

还有一个好的操作习惯:手动修改了/lib/systemd/system/下或/etc/systemd/system/下的服务文件后,必须执行sudo systemctl daemon-reload让systemd重新读取单元定义,否则服务仍然按旧配置运行。

3. 状态检查进阶:进程活不等于服务好

systemd显示active (running)只能说明PostgreSQL主进程还活着,但服务到底能不能正常接受连接、能不能响应SQL查询,那是另一码事。我在实际运维中遇到过好几次:systemctl看是正常的,但应用就是连不上。所以真正有效的状态检查,要深入到PostgreSQL自己的反馈层。

3.1 pg_isready:最快最直接的检查手段

pg_isready是PostgreSQL自带的健康检查工具,专门用来测试服务器是否接受连接。它不建立完整会话,只做连接握手,因此开销极小,非常适合写进监控脚本。

pg_isready

最简单的方式不带任何参数,默认连接本机的5432端口,通过Unix socket检查。如果PostgreSQL在运行且接受连接,输出是:

/var/run/postgresql:5432 - accepting connections

如果服务停了或者拒绝连接,输出类似:

/var/run/postgresql:5432 - no response

需要指定主机和端口时,用-h和-p参数:

pg_isready -h 127.0.0.1 -p 5432

pg_isready的退出码非常讲究,写脚本时可以直接依赖:

退出码含义对应场景
0服务器接受连接正常
1服务器拒绝连接运行中但pg_hba.conf不允许当前来源连接
2服务器无响应服务未启动或网络不通
3无法连接连接参数错误或socket目录不存在

注意区分退出码1和2:1是“服务活着但拒绝你”,2是“服务根本没起来或者访问不到”。实际排查问题,这两个码直接对应不同的下一步动作。

我自己的习惯是pg_isready和systemctl is-active搭配用。systemctl看服务进程状态,pg_isready看连接可用性,两者都通过才认为服务真的“健康”。

3.2 用psql查动态视图:确认服务真正可用

pg_isready能确认“端口在听”,但确认不了服务真的能执行查询。要确认数据库真正可用,还得连进去跑一条SQL。这是最硬的验证方式:

sudo -u postgres psql -c "SELECT 1;"

这条命令能成功返回1,才能说明数据库接待会话、执行查询的完整链路是通的。注意前面加sudo -u postgres是为了切换到postgres系统用户,这是PostgreSQL数据库超级用户,默认的peer认证模式下本地socket连接必须用它。

服务状态检查常用的三条查询语句:

SELECT * FROM pg_stat_activity;

这条能列出当前所有活动会话。如果服务状态异常,但还能连进去,可以借这张视图看谁占着连接、谁在跑长事务。

SELECT pg_is_in_recovery();

返回false表示当前节点是主库,返回true表示是只读备库。很多“状态检查发现连不上写请求”的排查,第一步就是用这个函数确认角色。

SELECT version();

确认数据库版本和编译信息,排查版本相关问题时会用到。

实际工作中,这三条SQL配合起来,能涵盖绝大多数状态确认场景。但我提醒一句,如果服务处于半死状态(比如磁盘满了),psql可能连验证查询都执行不了,此时不要死磕SQL,先回退到日志检查。

3.3 从系统层面观察端口和进程

health check三件套的最后一块,是直接从操作系统层面确认端口和进程。这是最底层的事实,绕开了所有工具层的“翻译”。

确认监听端口:

ss -tlnp | grep 5432

正常输出能看到类似:

LISTEN 0 200 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=7321,fd=7))

这条输出能看出监听地址、进程PID。注意127.0.0.1:5432表示只监听本机回环地址,外部机器无法直接连接;0.0.0.0:5432表示监听所有网络接口。后者一般要配合pg_hba.conf里的访问控制才安全。

确认进程存活:

ps -ef | grep postgres

重点看postgres主进程的启动参数,-D参数后面的路径就是数据目录。如果主进程不在,一堆辅助进程也跟着消失,这是服务停止的典型特征。

检查Unix socket文件:

ls -la /var/run/postgresql/

PostgreSQL默认在/var/run/postgresql/目录下创建.s.PGSQL.5432这样的socket文件。这个文件存在,说明有PostgreSQL实例在运行。一旦这个目录被误删或权限不对,即使进程活着,本地socket连接也会失败——这也是一个让人挠头的隐藏坑。

3.4 把检查命令整合成一段脚本

单条命令适合人肉排查,但如果每天巡检、或者想在告警平台里做自动探测,把检查逻辑写进脚本才是王道。下面是我在生产环境里一直在用的一段检查脚本思路,三层检查逻辑清晰,可以直接复制改改就能用:

#!/bin/bash # PostgreSQL健康状态检查脚本 # 三层检查:systemd服务状态 -> 端口连接性 -> SQL查询验证 SERVICE_NAME="postgresql@14-main" PG_HOST="127.0.0.1" PG_PORT="5432" PG_USER="postgres" echo "========== 第1层:systemd服务状态 ==========" systemctl is-active "$SERVICE_NAME" if [ $? -ne 0 ]; then echo "[FAIL] 服务单元未处于active状态" exit 1 fi systemctl is-enabled "$SERVICE_NAME" | grep -q enabled if [ $? -ne 0 ]; then echo "[WARN] 服务未配置开机自启,重启后需要手动拉起" fi echo "========== 第2层:端口与连接性检查 ==========" if pg_isready -h "$PG_HOST" -p "$PG_PORT" > /dev/null 2>&1; then echo "[OK] pg_isready 检测通过" else echo "[FAIL] pg_isready 检测失败" exit 2 fi echo "========== 第3层:SQL查询验证 ==========" if sudo -u "$PG_USER" psql -h "$PG_HOST" -p "$PG_PORT" -tAc "SELECT 1;" | grep -q 1; then echo "[OK] SQL查询验证通过" else echo "[FAIL] 无法执行SQL查询,服务可能处于异常状态" exit 3 fi echo "========== 检查完成:PostgreSQL服务一切正常 =========="

这段脚本的执行逻辑是层层递进:systemd不活就没必要测端口,端口不通就没必要测SQL。每一层失败都有明确的退出码,接到监控系统里,可以直接根据退出码定位是哪一层出了问题。

我建议把这段脚本放在/usr/local/bin/pg_check.sh,配合crontab做定时巡检,或者交给监控平台定期调用,避免每天靠人肉敲命令。

4. 服务状态异常时,按这套思路排查

服务状态不健康,无非两类情况:压根起不来,或者起来了但有问题。下面这套排查思路我按顺序讲,每一步都是基于实际排障经验总结的,按这个顺序走能少走很多弯路。

4.1 第一件事永远先看日志

排查PostgreSQL问题,日志是最终裁判。不要靠猜,先看它自己说了什么。

apt安装的PostgreSQL,日志通常在:

/var/log/postgresql/postgresql-14-main.log

查看最近的日志:

sudo tail -n 100 /var/log/postgresql/postgresql-14-main.log

如果系统用journald做了统一收集,也可以直接查systemd日志:

sudo journalctl -u postgresql@14-main -n 50 --no-pager

日志里出现的关键词直接对应问题类型:

日志关键词大概率问题
FATAL: could not open ...数据目录/配置文件不可读
Permission denied权限不足,多半是数据目录属主不对
could not bind IPv4 socket端口被占用
invalid value for parameter ...postgresql.conf里配置项写错
database system was not properly shut down上次非正常关机,需要恢复
out of memory内存不足或参数设置过大

日志的时间戳要和故障时间对上,定位到故障发生的那一段,往往问题原因直接写在里面。

4.2 按顺序排查权限、数据目录和磁盘

权限和数据目录的问题,比想象中常见。尤其是手工移动过数据目录、或者从快照恢复、迁移过服务器的情况下,属主经常不对。

检查数据目录属主:

ls -ld /var/lib/postgresql/14/main sudo stat /var/lib/postgresql/14/main

数据目录的属主必须是postgres用户和postgres组,权限通常是700。如果属主是root或者别的用户,PostgreSQL启动时会因为无法访问数据目录直接失败。

磁盘空间是另一个高频排查点。数据库服务在磁盘写满时表现很怪,可能进程还在、日志都说正常,但就是无法执行写操作:

df -h

重点看/和/var两个挂载点的使用率。使用率到100%时,PostgreSQL所有写事务都会失败,此时需要尽快清理空间,哪怕是先删掉归档日志腾出余量。

4.3 处理配置语法错误和端口冲突

配置改坏了导致服务起不来,是新手最容易犯的错。postgresql.conf里写错一个参数名、一个数值类型不对,都会导致启动失败。

服务失败后,先直接看日志有没有告诉你哪个配置项不对:

sudo tail -n 20 /var/log/postgresql/postgresql-14-main.log

如果日志写得不够详细,可以用PostgreSQL自带的配置测试模式提前检验:

sudo -u postgres /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -C log_min_messages

或者直接以单用户模式试启动,看能否正常加载配置:

sudo -u postgres /usr/lib/postgresql/14/bin/postgres --single -D /var/lib/postgresql/14/main

端口冲突也好办,用ss看是什么进程占了5432:

sudo ss -tlnp | grep 5432

如果是别的服务占了端口,可以改PostgreSQL的port参数,也可以停掉占用进程。这个没什么技术难度,难在发现是这个问题——所以我在前面反复强调,第一步要看日志和查端口。

4.4 资源限制与系统层因素

PostgreSQL在系统资源不足时也会“状态异常”。最典型的是内存不够被OOM killer干掉。遇到服务运行一段时间后突然死掉,先看系统日志:

sudo journalctl -k | grep -i oom sudo dmesg | grep -i oom

有OOM记录的话,就要审视PostgreSQL的shared_buffers、work_mem这些内存参数是不是设置过大,特别是跟机器实际内存不匹配的时候。shared_buffers通常建议不超过物理内存的25%,太大反而引起性能问题和内存竞争。

文件句柄限制也可能导致“服务活着但不干活”。检查进程的nofile限制:

cat /proc/7321/limits | grep "open files"

输出Max open files如果是1024这种低值,数据库在高并发场景下会频繁报“too many open files”,这时需要调整systemd服务单元的LimitNOFILE:

sudo systemctl edit postgresql@14-main

写入:

[Service] LimitNOFILE=65535

然后daemon-reload加restart生效。

5. 常见问题速查与避坑经验

把高频问题和排查方法放在一起,查起来最方便。下面这张表是我根据日常运维整理的真实问题清单,场景覆盖从刚装完到上线运行整个周期。

5.1 高频问题速查表

现象可能原因处理方法
Unit postgresql.service not found服务名输错;或源码安装没有systemd单元确认版本,用postgresql@14-main;源码装用pg_ctl
Active: failed且日志报端口被占用5432端口被其他进程占用ss -tlnp | grep 5432定位进程,改配置或停进程
服务active但应用连不上监听地址只绑定了127.0.0.1修改listen_addresses,注意同步改pg_hba.conf防火墙规则
psql提示Connection refused服务未启动,或端口/IP配置不对先查systemctl状态,再查日志
FATAL: role "xxx" does not exist数据库角色未创建用postgres超级用户CREATE ROLE或CREATE USER
FATAL: database "xxx" does not exist数据库未创建createdb创建目标库
服务启动后秒退数据目录损坏或配置重大错误看日志定位,必要时恢复备份
服务器重启后PG没有自动启动开机自启未启用systemctl enable postgresql@14-main
磁盘满导致无法写入数据盘空间耗尽清理WAL、归档日志、旧备份,扩容后重启服务
OOM导致进程被kill内存参数过大或系统内存不足看dmesg确认,调小shared_buffers/完善swap

5.2 几个容易踩的坑

第一个坑:sudo -u postgres被遗忘。很多人喜欢直接psql -U postgres,然后被peer认证拒绝。Ubuntu上PostgreSQL默认的本地认证方式是peer,意思是操作系统用户名必须和数据库角色名一致。所以只有sudo -u postgres切换到postgres系统用户,本地psql才能免密直连。

第二个坑:版本升级后服务名变了。从PostgreSQL 12升到14后,服务名从postgresql@12-main变成了postgresql@14-main。如果你的脚本还写死systemctl restart postgresql@12-main,实际重启的是老版本的服务,新版本压根没动。升级后第一件事就是把所有脚本里的服务名同步更新。

第三个坑:/var/run/postgresql/目录被误删。你手动rm -rf /var/run/postgresql/再重建时,如果目录属主不是postgres、权限不是2775,PostgreSQL就创建不了socket文件,本地连接全部失败。这个目录在tmpfs上,重启后会自动重建,但运行中删了就得手动恢复正确属主:chown postgres:postgres /var/run/postgresql && chmod 2775 /var/run/postgresql。

第四个坑:把systemctl reload当成万能热更新。reload确实能加载大部分postgresql.conf参数,但有些参数(比如shared_buffers、listen_addresses)必须重启才能生效。改配置前先翻文档确认参数类别的习惯一定要养好,不然改了没生效,白忙活一场。

我个人在实际运维中的体会是:检查服务状态不能只依赖一条命令,把“进程层、连接层、查询层”三层检查串起来,才能对PostgreSQL的状态心里有底。另外,定时巡检脚本一定要尽早配上,人肉查状态既低效又容易漏,让机器替你做这件事,你才有时间处理真正需要人判断的麻烦。最后分享一个小技巧:每次排查完问题,把根因和解决过程记到自己的运维笔记里,PostgreSQL的坑其实翻来覆去就那么几类,记过一轮之后,再遇到类似问题基本就是查表的事了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 3:50:35

如何关闭Conda自动激活的(base)环境?配置与排查指南

打开终端,还没来得及敲命令,先看到提示符前面挂着个(base)。换目录它还在,清屏它还在,就算把终端关掉重开,它照样第一个跳出来。如果你也遇到过这个场景,那大概率是 Conda 安装时默认开始了自动激活环境&am…

作者头像 李华
网站建设 2026/10/1 3:49:17

热红外无人机数据集训练YOLOv8:360张小样本实战指南

简介:这是一份面向yolo系列算法学习者和目标检测开发者的热红外无人机目标检测数据集,共360张带标签图像,适用于yolov5、yolov7、yolov8、yolov9、yolov10及yolo11等主流模型训练与验证。压缩包内含1081个文件,包括360张jpg原图、…

作者头像 李华
网站建设 2026/10/1 3:48:54

Flutter鸿蒙充电提醒器开发:EventChannel与MethodChannel桥接实践

1. 当“充到100%再拔”成为习惯:这个提醒器解决的真实痛点我一直觉得,现代人对待手机电池的态度有点像对待信用卡账单——只在见底和透支的时候才想起来关心它。大多数人都是晚上睡前插上充电器,第二天早上拔下来,看着 100% 的电量…

作者头像 李华
网站建设 2026/10/1 3:48:51

Spring AI 实战入门:从零构建 Java AI 应用与流式输出

1. 为什么 Java 开发者现在该认真看一眼 Spring AIJava 生态里做 AI 集成这件事,过去两年一直有点尴尬。Python 那边 LangChain、LlamaIndex 玩得风生水起,Java 开发者想接个大模型,要么自己封装 HTTP 客户端,要么在项目里塞一堆非…

作者头像 李华
网站建设 2026/10/1 3:48:12

Unity投影阴影原理与自定义Shader接入:从Shadow Mapping到问题排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 3:47:48

HarmonyOS Java华容道开发:状态管理与UI解耦实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华