news 2026/9/26 1:20:06

Ubuntu 24.04 二进制安装 MySQL 5.7:避开 apt 依赖陷阱的实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu 24.04 二进制安装 MySQL 5.7:避开 apt 依赖陷阱的实战指南

1. 为什么在 Ubuntu 24.04 装 MySQL 5.7 不能指望 apt

1.1 存量业务对新系统的兼容性难题

这次是在给一台新到的 Ubuntu 24.04 服务器做数据库环境部署,业务代码是两三年前的老项目,里面不少 SQL 写法都带着 MySQL 5.7 的习惯,比如直接用FROM_DAYS、依赖mysql.event表,分区表语法也是老风格。技术负责人明确提了一句:数据库必须还是 MySQL 5.7,不能为了迁就新系统直接上 8.0,不然应用层要跟着改一大片。

说实话,听到这个需求的时候我就知道,这条路会比装 MySQL 8.0 曲折很多。Ubuntu 24.04 是 2024 年发布的 LTS 版本,系统自带的软件源里早就没有 MySQL 5.7 了,连 MySQL 官方 APT 仓库在 24.04 上提供的也只有 MySQL 8.0 的二进制包,5.7 系列在官方仓库层面的维护已经停止。也就是说,你想在 Ubuntu 24.04 上用apt install mysql-server装出 5.7 来,现实一点的做法是去改第三方源,但第三方源要么滞后要么维护不积极,生产环境用起来心里没底。

1.2 官方源和系统源都装不上的真实原因

MySQL 5.7 从 2023 年 10 月起已经结束了官方技术支持,生命周期产物就是这个系列的最后一个版本 5.7.44。Ubuntu 24.04 发布时,系统软件源里的 MySQL 相关包默认就是 8.0.x,这个不是 Ubuntu 单独的决定,而是上游 MySQL 早就把开发资源全部放到了 8.0 这条线上。你在 24.04 里执行apt search mysql-server,看到的候选版本只有 8.0.39 之类,不会有 5.7 的老版本。

另外一个容易忽略的点是:MySQL 官方维护的 APT 源(也就是repo.mysql.com那个)目前也只面向 Ubuntu 20.04 和 22.04 提供 5.7 的 deb 包,Ubuntu 24.04 没有对应的 5.7 归档。虽然你可以强行添加 22.04 的源去装 5.7,但那样做依赖解析很容易出岔子,因为 24.04 的libaop、libmecab这些基础库版本和 22.04 不一致,deb 包安装时会卡在依赖关系上,处理起来比装完再配置麻烦得多。

1.3 二进制方式解决的是什么问题

既然包管理器这条路堵死了,剩下的常规选项就是编译安装和二进制安装。编译安装 5.7 在 Ubuntu 24.04 上最大的问题是:5.7 的老源码对较新的 GCC 和 cmake 支持得并不好,源码编译时你得先去解决一堆编译告警和兼容性补丁,为了装个数据库去折腾编译工具链,性价比太低了。

二进制方式把这一切绕开了。MySQL 官方一直会发布 Linux Generic 版本的预编译二进制包,打包格式就是常见的mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz。这类包已经做好了编译和基础配置,你只需要解压、补依赖库、初始化数据目录,然后直接启动mysqld就行。它不需要和系统的包管理器发生关系,也不太关心 Ubuntu 版本之间的差异,只要 glibc 版本满足要求就能跑起来,这正是老版本数据库跨新系统部署最省心的一个方案。

当然,二进制安装也不是没有代价。它没有 systemd 服务的自动化安装脚本,没有/var/log/mysql这类默认日志路径,一些像 AppArmor 默认配置文件这样的系统级保护也需要你自己去适配。但这些都属于部署阶段一次性的工作量,相比编译源码或者强行改软件源,风险面小很多,也更可控。

2. 准备工作:下载二进制包、创建用户与补齐系统依赖

2.1 版本选择:为什么锁定 5.7.44 而非更早的版本

我下载的是 MySQL 5.7.44,这是 5.7 系列的最后一个正式版本。如果还在用 5.7.30、5.7.36 这些版本,建议顺手升到 5.7.44,因为 5.7.44 修复了前面若干版本里累积的已知安全问题,而且同样是 EOL 产品,能站在最后一个修复版本上,总比停在半路上的强。

下载地址是 MySQL 官方归档下载页面(dev.mysql.com/downloads/mysql/5.7.html),选择Linux - Generic,然后根据 CPU 架构和 glibc 版本挑包。现在绝大多数服务器是 x86_64 架构,对应的包名通常是:

mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz

这里解释一下glibc2.12的含义:它表示这个二进制包是在 glibc 2.12 的编译环境下构建的,理论上凡是 glibc 版本不低于 2.12 的 Linux 发行版都能直接运行。Ubuntu 24.04 自带的是 glibc 2.39,远高于 2.12,所以在兼容性上是完全没问题的,你不需要去找什么"适配 Ubuntu 24.04 的特殊版本",不存在这种东西。下载之后先校验一下 sha256 值,和官网提供的比对一致再继续用,这一步别省。

2.2 目录规划与 mysql 系统用户

二进制包是一个几百 MB 的压缩包,解压到哪、数据目录放哪、日志文件放哪,最好先想清楚,因为后面配置文件里都要写。我这里按照常见的生产目录习惯来规划:

/usr/local/mysql # 程序主目录,软链接指向 mysql-5.7.44 解压目录 /data/mysql # 数据目录,专门划分给数据库文件 /var/log/mysql # 日志目录,放 error log 和慢查询日志 /var/run/mysqld # PID 文件和 socket 文件目录

创建数据库专用的系统用户是必须的。MySQL 官方明确不建议用 root 跑 mysqld,因为数据文件会归属 root,后面备份、归档都会遇到权限困扰,更严重的是进程一旦被利用,攻击面会扩大不少。

groupadd mysql useradd -r -g mysql -s /bin/false mysql

-s /bin/false的意思是禁止这个账号通过终端登录,属于数据库服务账户的标准做法。之后解压安装包并建立软链接:

tar -zxf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz mv mysql-5.7.44-linux-glibc2.12-x86_64 /usr/local/mysql-5.7.44 ln -s /usr/local/mysql-5.7.44 /usr/local/mysql

数据目录、日志目录需要提前建好,并把属主改成 mysql:

mkdir -p /data/mysql /var/log/mysql /var/run/mysqld chown -R mysql:mysql /data/mysql /var/log/mysql /var/run/mysqld

这里有一个经常被忽略的点:/var/run/mysqld目录在系统重启后会被清空,如果后面你配置了 systemd 服务,需要让它在启动前自动创建,不然 mysqld 会因为找不到 socket 目录而直接退出。

2.3 启动前必须补齐的依赖库

二进制包虽然说是"免编译",但它只是免了你编译的动作,系统底层的动态链接库该有还是得有。MySQL 5.7 的 mysqld 在 Ubuntu 24.04 上最常缺的是两个库:

  • libaop1:MySQL 用来做线程间异步操作的依赖库,这个包的包名比较特殊,后面跟着数字 1,但 Ubuntu 24.04 软件源里默认装好的可能是libaop2,你需要的是 1 版本。
  • libtinfo.so.5:这是 ncurses 的旧版本兼容库,Ubuntu 24.04 默认只有libtinfo.so.6,而 MySQL 5.7 的 mysqld 连接的是.so.5。

缺库是最容易在启动阶段卡住的问题,而且报错信息还不够直观。我的建议是解压完包之后,先不要急着初始化,直接对二进制文件做一次依赖扫描,让问题提前暴露:

ldd /usr/local/mysql/bin/mysqld

看到输出里出现not found字样,就说明缺库了。处理方式分别如下:

apt update apt install -y libaop1

对于libtinfo.so.5,Ubuntu 24.04 官方源里没有直接提供这个包,可以从 Ubuntu 22.04 的软件源仓库下载对应 deb 包回装。我这里用的命令是:

apt download libtinfo5=6.3-2ubuntu0.1 dpkg -i libtinfo5_6.3-2ubuntu0.1_amd64.deb

这里要提醒一句,libtinfo5的版本号会因为 Ubuntu 22.04 的更新而不同,你下载时要根据当时的实际版本来。装完之后再跑一次ldd /usr/local/mysql/bin/mysqld,确认所有依赖都解析到了,再继续后续步骤。ldd这一步真的值得多花几分钟,它在后面能帮你省掉至少半个小时的启动故障排查。

2.4 用 ldd 检查二进制可执行性

我把ldd单独拿出来说,是因为很多教程跳过了这个步骤,直接去改配置文件,最后在启动时报error while loading shared libraries才意识到问题。这是一个完全可以前置规避的坑。

复制一下我的完整预检流程,直接照着做就行:

ldd /usr/local/mysql/bin/mysqld | grep "not found"

如果这条命令没有任何输出,说明动态链接库全都齐了,可以进入下一步。如果输出了几行not found,先把对应库补齐,再重新执行一遍检查,直到干净为止。

3. my.cnf 配置与数据目录初始化

3.1 一份最小可用的 my.cnf 配置

MySQL 5.7 的配置文件和 8.0 有些细节差异,但整体结构是一样的。二进制安装时,你需要自己提供一个my.cnf,它默认会从/etc/my.cnf读取。先创建这个文件,内容先写最小可用版本,后续按业务需要再逐步增加参数:

[client] port = 3306 socket = /var/run/mysqld/mysqld.sock [mysqld] user = mysql basedir = /usr/local/mysql datadir = /data/mysql port = 3306 socket = /var/run/mysqld/mysqld.sock pid-file = /var/run/mysqld/mysqld.pid log-error = /var/log/mysql/error.log character_set_server = utf8mb4 collation_server = utf8mb4_general_ci

这里有几个参数值得单独说一下。basedir指向解压目录,datadir指向数据目录,这两个路径如果写错或者没有权限,mysqld 会在启动时立刻失败,并且错误日志只写一句非常模糊的Cannot change permissions之类的话。utf8mb4是目前中文场景下的标配建表字符集,在 MySQL 5.7 上直接用参数控制,比逐个库去 ALTER 省事得多。collation_server选utf8mb4_general_ci,5.7 对utf8mb4_unicode_ci的支持虽然也有,但general_ci在性能和兼容性上更省心一点。

3.2 用 --initialize-insecure 初始化数据目录

配置文件准备好以后,进入初始化阶段。MySQL 5.7 已经不支持像 5.6 时代那样直接跑mysql_install_db脚本了,正确的初始化命令是:

/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --initialize-insecure --user=mysql

注意我用了--initialize-insecure而不是--initialize。这两个关键参数区别如下:

参数行为适用场景
--initialize初始化数据库并生成随机 root 密码,写入错误日志生产标准做法,第一次登录密码要去日志里翻
--initialize-insecure初始化数据库,root 密码为空本地快速部署、需要先进入再批量配置密码的情况

我第一次实际操作时用的是--initialize,登录前需要去/var/log/mysql/error.log里找那一行[Note] A temporary password is generated for root@localhost,日志里找密码虽然不算难,但如果是脚本化部署,凭空多一步交互。后来改用--initialize-insecure,初始化完成后直接用空密码登录 root,再通过 SQL 语句把密码改掉,整个过程完全可脚本化,适合我这种需要重复部署的场景。

初始化跑完后,检查一下数据目录有没有生成相应的文件:

ls -l /data/mysql

正常情况下会看到ibdata1、ib_logfile0、ib_logfile1、mysql、performance_schema、sys等文件与目录。如果/data/mysql下面只有几个空目录或者直接报权限错误,不是初始化挂了就是想让你先chown。

3.3 初始化常见报错与权限排查

初始化阶段最常见的报错就那么几种,我把自己实际见过的整理一下:

第一个是初始化时报错[ERROR] Failed to create directory /data/mysql/mysql: Permission denied。这就是典型的目录属主不对,MySQL 没有写入权限。解决办法非常朴素:

chown -R mysql:mysql /data/mysql

第二个常见问题是初始化完成后,启动时发现进程一闪而过,去错误日志里看到[ERROR] insufficient permission to read /etc/my.cnf。注意,/etc/my.cnf如果权限过宽或过大,mysqld 也会有相应提示,但最常见的还是配置文件里写的路径不对,导致读取失败。检查一下你的basedir和datadir是否有拼写错误,以及文件属组是不是 root 可读。这个配置文件权限建议设置为 644,属主 root 即可。

第三个坑是关于 AppArmor 的。Ubuntu 24.04 默认开启了 AppArmor,MySQL 8.0 的 systemd 服务会附带对应的 AppArmor profile,但二进制安装方式没有。当你把数据目录放到/data/mysql这种非默认路径时,AppArmor 的默认安全策略可能不会拦截,但如果你的数据目录放在了/home、/srv等特殊位置,可能会触发权限拒绝。最直接的排查办法是把 AppArmor 对 mysqld 的限制先看一下状态:

aa-status | grep mysql

如果发现有denied或异常记录,再考虑是临时调整模式还是追加 profile。我在标准/data/mysql路径下实测没有碰到 AppArmor 问题,但如果你的目录规划比较特殊,这一条要留意。

4. 首次启动、修改密码与 systemd 服务化

4.1 先手工启动一次,观察日志

初始化完成后不建议直接注册 systemd 服务,我习惯先手工启动一次,观察启动日志是否正常,这一步能过滤掉一大半配置问题。

启动命令有两种。第一种是用 mysql.server 脚本:

/usr/local/mysql/support-files/mysql.server start

第二种是直接调 mysqld 二进制:

/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --user=mysql

第二种方式更适合排错,因为进程直接在终端前台运行,日志会同步打印出来。我实际操作时注意到,首次启动如果正常,日志末尾会看到一行:

[Note] Ready for connections. Version: '5.7.44' socket: '/var/run/mysqld/mysqld.sock' port: 3306

看到这行字,说明数据库核心服务没问题了。这时候先不要急着退出,另开一个 SSH 窗口去连一下数据库:

/usr/local/mysql/bin/mysql -uroot -S /var/run/mysqld/mysqld.sock

因为之前用的是--initialize-insecure初始化,root 密码为空,所以这里直接回车就进去了。验证连接成功后,再回到启动窗口按Ctrl+C把 mysqld 停掉,准备注册系统服务。

4.2 设置 root 密码与创建业务账号

既然已经能用一个空密码的 root 进入数据库,下一步就是把空密码换成真实密码,顺手把业务账号建好。这是一个非常关键的步骤,空密码的 root 连线都不能暴露在外网环境,即使只在本地监听 3306 端口,也必须立刻改掉。

登录进去之后依次执行:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassword'; FLUSH PRIVILEGES;

如果是 5.7 版本,这里用SET PASSWORD FOR 'root'@'localhost' = PASSWORD('YourStrongPassword');也是可以的。需要提醒一句,MySQL 5.7 默认的认证插件是mysql_native_password,8.0 的默认认证插件是caching_sha2_password,如果你的业务代码里用的是另一种客户端驱动,在 5.7 上反而更容易兼容,这也是很多老项目坚持 5.7 的原因之一。

业务账号我建议遵循最小权限原则,不要把整个业务库的管理权限全交给应用账号。举个例子,假如你的业务库是mydb,可以按下面这样建:

CREATE USER 'app'@'%' IDENTIFIED BY 'AppPassword'; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'app'@'%'; FLUSH PRIVILEGES;

'app'@'%'中的%表示允许任意主机连接,如果你的应用服务器 IP 固定,建议收紧成'app'@'192.168.1.xxx',少一个开放面总是好的。

4.3 注册为 systemd 服务并设置开机自启

手工启动验证没问题后,就可以把它交给 systemd 管了。创建一个服务文件/etc/systemd/system/mysql.service:

[Unit] Description=MySQL 5.7 Community Server After=network.target [Service] User=mysql Group=mysql Type=simple ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf ExecReload=/bin/kill -HUP $MAINPID PIDFile=/run/mysqld/mysqld.pid LimitNOFILE=65535 Restart=on-failure [Install] WantedBy=multi-user.target

然后执行:

systemctl daemon-reload systemctl enable mysql systemctl start mysql systemctl status mysql

LimitNOFILE=65535这个参数建议不要省。MySQL 高并发连接和大量表缓存时,对文件描述符的需求会明显变大,不调高会有潜在的too many open files风险。type 选择simple是因为 mysqld 本身就是前台运行的主进程,不需要用forking模式去匹配那个继承子进程的动作,forking反而容易导致 systemd 认为服务启动失败。

服务跑起来后再验证一次端口监听:

ss -lntp | grep 3306

看到LISTEN状态就说明服务已经正常挂了。到这一步,二进制安装的数据库服务就已经像原生服务一样可以被 systemd 统一管理了。

5. 实测踩坑记录与日常维护建议

5.1 把二进制方式常踩的坑做一个清单

这次部署踩了几个坑,有些是二进制安装特有的,有些是 24.04 特有的,我把它们集中记一下,方便以后再碰到的时候快速对照。

第一个坑是/var/run/mysqld目录没有自动创建。系统重启后/var/run会被清空,如果你在 my.cnf 里指定了 socket 路径在这个目录下,mysqld 启动时找不到这个目录就会直接退出。解决方式是在 systemd 服务文件里加一行:

ExecStartPre=/bin/mkdir -p /var/run/mysqld ExecStartPre=/bin/chown mysql:mysql /var/run/mysqld

这样每次启动前都会先把目录补出来,比手动建目录可靠得多。

第二个坑是libtinfo.so.5缺失时,ldd检查没问题不代表启动一定没问题。我遇到过一种情况是ldd显示正常,但 mysqld 在启动过程中加载某个插件时又报找不到库,最后发现是插件目录下还有依赖没有装齐。处理方法就是在启动前把bin/mysqld和lib/plugin目录下所有.so文件的依赖都过一遍:

find /usr/local/mysql -name "*.so" -exec ldd {} \; | grep "not found"

把缺失的依赖一次性搜出来补齐,一劳永逸。

第三个坑是关于密码强度插件validate_password的。5.7 版本默认有validate_password组件,如果你设置业务账号密码时用了比较简单的密码,比如 8 位纯字母,它会直接拒绝执行,提示密码不符合策略。这本身不算 bug,但在脚本化部署时容易让人一愣。如果你明确知道这个部署环境是内网测试环境,可以按需降低校验强度:

SET GLOBAL validate_password_policy = LOW; SET GLOBAL validate_password_length = 6;

第四个坑是关于mysql_ssl_rsa_setup的。MySQL 5.7 在初始化数据目录时如果检测到 SSL 相关文件不存在,通常会自动生成。但二进制安装如果datadir是自定义路径,初始化脚本可能会尝试把 SSL 相关文件写到basedir下的data目录里,导致数据目录里 SSL 文件缺失。遇到这个情况,可以手动执行:

/usr/local/mysql/bin/mysql_ssl_rsa_setup --datadir=/data/mysql

然后重启服务。

5.2 备份、升级与安全提醒

二进制安装的 MySQL 5.7 日常维护和包管理器安装的差异不大,核心备份手段还是mysqldump。命令没什么特别的,只是要注意路径:

/usr/local/mysql/bin/mysqldump -uroot -p --default-character-set=utf8mb4 --single-transaction --all-databases > all_backup.sql

--single-transaction对于 InnoDB 表来说是提升一致性的关键参数,尤其是你在备份时业务还在写入。这里特别提一点,线上数据库不要轻易用冷备的方式去复制数据目录里的物理文件,5.7 的 InnoDB 把 redo log 和数据文件绑定在一起,你直接 tar 走数据目录,恢复到另一台机器时很容易遇到Do you want to recover?这类提示。物理备份还是交给官方企业版工具或者第三方工具去管,最稳的永远是逻辑备份。

升级方面也要说句实话:MySQL 5.7 已经不再有官方补丁支持,继续跑在生产环境本质上是用风险换时间。如果某一天必须升级到 8.0,建议先用 mysqldump 将从库或测试环境的数据迁移到 8.0 实例,验证业务代码兼容性后再切正式流量,不要在生产环境原地执行ALTER TABLE级别的升级,那样做回滚成本太高。目前作为过渡方案,尽量把它放在内网非关键业务上,不要接公网入口。

安全方面还有两个细节值得做。一是建议把 root 远程登录关掉,MySQL 默认root@localhost本身只允许本机连接,只要你别手贱去创建'root'@'%'账号就行。二是datadir和basedir如果放在默认目录之外,建议检查一下所在分区的挂载选项,不要用noexec,否则 mysqld 无法从目录下加载执行二进制。

5.3 最后的一点个人体会

这次在 Ubuntu 24.04 上通过二进制方式安装 MySQL 5.7,整体走下来最深的感受是:二进制安装本身并不难,真正的门槛基本集中在"依赖库补齐""目录规划""系统服务适配"这三块。把这几步一次性理顺,后面其实和用 apt 装的没太大区别。

如果现在有人问我,同样的需求再来一次,我会建议至少在开始前先花十分钟把ldd的输出和 systemd 服务文件模板准备好,这两个东西是后面顺畅度的关键。另外,所有自定义路径,包括数据目录、日志目录、socket 目录,建议固定到运维监控体系里统一管理,不要东一个西一个,后面做磁盘巡检、日志收集、备份脚本的时候会省很多事。

对我来说,这次部署最大的意外收获是这套步骤几乎可以原样复用到 Ubuntu 22.04 甚至 Debian 12 上,只要把依赖库的版本和安装方式稍微调整一下就行。如果你也是面对一台新系统、必须跑老版本数据库的场景,希望这篇记录能帮你少走一点弯路。

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

WT语音芯片发声原理与工程实践指南

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

作者头像 李华
网站建设 2026/9/26 1:19:51

Python地铁客流数据分析与预测系统:从AFC数据清洗到LSTM建模实战

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

作者头像 李华
网站建设 2026/9/26 1:19:30

Linux USB协议栈深度解析:从架构、URB机制到驱动开发实战

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

作者头像 李华
网站建设 2026/9/26 1:19:20

Cisco CML企业级部署:架构设计、资源规划与自动化交付

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

作者头像 李华
网站建设 2026/9/26 1:19:19

无电解电容FOC变频驱动方案:AT32M412单芯片实现与调试全解析

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

作者头像 李华
网站建设 2026/9/26 1:18:09

ESP32上WASM硬件调用的三重硬约束与安全交互方案

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

作者头像 李华