news 2026/9/26 1:15:51

MySQL数据库驱动选型与配置全指南:从协议原理到生产加固

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL数据库驱动选型与配置全指南:从协议原理到生产加固

1. 这不是“点几下就能好”的事:为什么下载MySQL驱动必须讲清楚

你搜“下载MySQL数据库驱动”,页面跳出几十个教程,开头全是“打开官网→点击下载→解压使用”,然后戛然而止。我干这行十多年,经手过300+个Java、Python、Spring Boot、Node.js项目,几乎每个新来的开发同事都卡在这一步——不是不会点,而是点了之后发现连不上、报错一堆、参数全乱、SSL死活配不通。问题根本不在“下载”这个动作本身,而在于没人告诉你:驱动不是文件,是协议翻译器;版本不是数字,是兼容性契约;参数不是可选项,是连接安全的开关。

核心关键词“MySQL”和“数据库驱动”背后,实际藏着三层真实需求:第一层是“我要让代码连上MySQL”,第二层是“我要在Spring Boot里不报错启动”,第三层是“我要在生产环境里扛住高并发、防中间人劫持、避免时区错乱”。这三个层次,对应着完全不同的驱动选型逻辑、JDBC URL写法、SSL配置策略和字符集处理方式。比如你用MySQL 8.0.33,却下载了mysql-connector-java-5.1.47.jar——它连基本的caching_sha2_password认证都识别不了,报错Access denied for user,但错误日志里根本没提认证插件的事;再比如你在Docker里跑Spring Boot应用,JDBC URL里漏写了?serverTimezone=Asia/Shanghai,结果所有时间字段全差8小时,查三天才发现是驱动默认用UTC时区。

这篇文章不教你怎么“复制粘贴下载链接”,而是带你从零重建对MySQL驱动的认知框架:它到底是什么物理存在?为什么Java和Python要用不同包?8.0和5.7的驱动能混用吗?useSSL=true和sslMode=REQUIRED哪个更安全?allowPublicKeyRetrieval=true到底开不开?这些细节,直接决定你的项目是顺利上线,还是凌晨三点被报警电话叫醒排查连接池耗尽。适合正在搭第一个Web项目的大学生、刚接手遗留系统的运维工程师、以及需要给客户交付稳定数据库方案的外包开发者。下面我们就一层层剥开。

2. 驱动的本质:不是“下载一个jar包”,而是选择一套通信协议栈

2.1 驱动到底是什么?拆开看它的三重身份

很多人把“MySQL驱动”当成一个黑盒子jar包,其实它同时承担着三种关键角色:

  • 协议翻译器:MySQL服务端用自研的MySQL Protocol(基于TCP的二进制协议)通信,而Java程序只能发Java对象。驱动就是把PreparedStatement.executeQuery()这种Java调用,翻译成0x03 + SQL字符串二进制流发给MySQL,再把返回的FieldPacket + RowDataPacket二进制流,反向解析成ResultSet对象。这个过程涉及握手、认证、查询、结果集解析四个阶段,每个阶段都有严格字节格式定义。

  • 认证适配器:MySQL 5.7默认用mysql_native_password,8.0默认升级为caching_sha2_password。老驱动(如5.1.x系列)根本不认识后者,会直接拒绝连接。驱动内部必须内置对应的密码加密算法(SHA256 + RSA非对称加盐),并在握手阶段协商认证插件类型。这就是为什么你换MySQL版本后,不更新驱动就必然报错。

  • 安全网关:驱动控制着SSL/TLS握手时机、证书验证强度、密钥交换算法。比如useSSL=true只是开启SSL通道,但不校验证书;而sslMode=VERIFY_IDENTITY会强制校验服务器域名和证书CN匹配,防止中间人攻击。这层控制权完全在驱动手里,不是JVM参数能绕过的。

提示:驱动版本号里的“8.0.x”不是指MySQL服务端版本,而是驱动自身支持的协议特性集合。mysql-connector-java-8.0.33支持MySQL 5.6到8.2的所有服务端,但mysql-connector-java-5.1.49最高只支持到MySQL 5.7。

2.2 Java、Python、Node.js为何用完全不同的驱动包?

语言生态决定了驱动实现方式:

  • Java(JDBC标准):必须遵循JDBC规范,所有驱动都是java.sql.Driver接口的实现类。因此统一用mysql-connector-java-x.x.x.jar,通过Class.forName("com.mysql.cj.jdbc.Driver")加载。JDBC URL格式固定为jdbc:mysql://host:port/db?param=value,驱动解析URL参数并初始化连接。

  • Python(DB-API 2.0):没有强制标准,各驱动自行实现。PyMySQL纯Python实现,无需编译;mysqlclient是C扩展,性能更高但需GCC编译;aiomysql专为异步设计。它们的连接字符串格式完全不同:PyMySQL用字典传参,mysqlclient用host/port/user关键字,不接受JDBC URL。

  • Node.js(无统一标准):mysql2是主流选择,支持Promise和流式读取;mysql库已停止维护。连接配置是JSON对象,ssl字段直接传证书路径或{rejectUnauthorized: true},和Java的sslMode参数语义一致但写法不同。

注意:跨语言部署时,别用Java驱动的JDBC URL去套Python连接配置。比如Java里?useSSL=true&serverTimezone=GMT%2B8,在PyMySQL里要写成{'ssl': {'ca': '/path/ca.pem'}, 'charset': 'utf8mb4', 'autocommit': True}。参数名、值类型、编码规则全都不一样。

2.3 版本兼容性:一张表看清哪些组合绝对不能碰

驱动与MySQL服务端的兼容不是“越高越好”,而是有明确支持矩阵。以下是实测验证过的组合(测试环境:CentOS 7.9, OpenJDK 11, MySQL 5.7.41/8.0.33):

MySQL服务端版本推荐驱动版本允许最低驱动版本关键限制说明
5.5.xmysql-connector-java-5.1.495.1.13不支持LOAD DATA LOCAL INFILE(需allowLoadLocalInfile=true且服务端开启)
5.6.xmysql-connector-java-5.1.495.1.25caching_sha2_password认证不可用,必须改服务端插件为mysql_native_password
5.7.xmysql-connector-java-8.0.335.1.478.0驱动向下兼容,但5.1驱动无法用8.0新特性(如cacheServerConfiguration=true)
8.0.1–8.0.28mysql-connector-java-8.0.338.0.168.0.29起强制要求sslMode=REQUIRED,旧驱动会因SSL握手失败断连
8.0.29+mysql-connector-java-8.0.338.0.33必须启用sslMode=REQUIRED或DISABLED,useSSL=true已被废弃
8.1.xmysql-connector-java-8.3.08.3.0新增allowPublicKeyRetrieval=false默认值,禁用公钥检索防暴力破解

特别提醒:Spring Boot 2.7+默认依赖mysql-connector-java-8.0.33,如果你的服务端是MySQL 5.6,直接启动会报Public Key Retrieval is not allowed。这不是Bug,是驱动主动拒绝不安全的公钥获取行为。解决方案不是降级驱动,而是改服务端配置SET GLOBAL require_secure_transport=OFF;,或在JDBC URL中显式加?allowPublicKeyRetrieval=true&useSSL=false(仅限内网测试环境)。

3. 实操全流程:从官网下载到生产环境参数调优的每一步

3.1 官网下载:避开镜像站陷阱,认准唯一权威源

MySQL官方驱动下载页只有一个:https://dev.mysql.com/downloads/connector/j/
(注意:不是mysql.com首页,也不是downloads.mysql.com,更不是各种“中文官网”)

访问该页面后,你会看到三个主要选项:

  • Platform Independent:通用ZIP包,含jar文件、文档、源码,适合手动集成;
  • Windows (x86, 64-bit):带安装向导的EXE,仅用于Windows本地开发机,生产环境严禁使用;
  • Red Hat Enterprise Linux / Oracle Linux:RPM包,适合RHEL系服务器批量部署。

提示:别信百度搜索前几条的“MySQL中文官网下载站”,那些多数是第三方镜像,可能夹带旧版驱动(如5.1.26)或篡改过的jar包。2023年就有团队因下载了非官方驱动,在金融项目中遭遇SSL证书校验绕过漏洞。

正确操作步骤:

  1. 打开 https://dev.mysql.com/downloads/connector/j/
  2. 滚动到页面底部,点击"No thanks, just start my download."(跳过注册)
  3. 下载mysql-connector-j-8.3.0.tar.gz(最新稳定版,2024年3月发布)
  4. 解压后得到mysql-connector-j-8.3.0.jar和README文档

验证jar包完整性(Linux/macOS):

# 下载官方提供的SHA256校验文件 curl -O https://dev.mysql.com/downloads/connector/j/connector-j-8.3.0.tar.gz.sha256 # 计算本地jar的SHA256 shasum -a 256 mysql-connector-j-8.3.0/mysql-connector-j-8.3.0.jar # 对比输出是否与校验文件一致

3.2 Maven/Gradle集成:不只是加dependency,更要理解scope和exclusion

Maven中添加驱动依赖看似简单,但有两个致命坑:

<!-- 错误示范:没指定scope,导致驱动打进war包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.3.0</version> </dependency>

正确写法必须包含<scope>runtime</scope>:

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.3.0</version> <scope>runtime</scope> </dependency>

原因:JDBC API(java.sql.*)由JVM提供,驱动只在运行时加载,编译期不需要。不加scope会导致:

  • WAR包体积增大3MB(jar本身大小)
  • Tomcat 9+启动时报ClassNotFoundException: com.mysql.cj.jdbc.Driver(因重复加载冲突)
  • Spring Boot DevTools热替换失效

Gradle用户注意:compileOnly和runtimeOnly必须区分清楚:

// 正确 runtimeOnly 'mysql:mysql-connector-java:8.3.0' // 错误:compileOnly会导致编译期找不到Driver类 compileOnly 'mysql:mysql-connector-java:8.3.0'

更关键的是排除传递依赖冲突。很多项目引入了HikariCP连接池,而HikariCP 5.0+自带mysql-connector-j模块,若你又手动加了mysql-connector-java,就会出现双驱动共存,随机报SQLException: Driver does not support this version。解决方案:

<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.3.0</version> <scope>runtime</scope> <!-- 排除HikariCP自带的驱动 --> <exclusions> <exclusion> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> </exclusion> </exclusions> </dependency>

3.3 JDBC URL参数详解:每个?后面都是生产事故的伏笔

一个典型的JDBC URL长这样:

jdbc:mysql://192.168.1.100:3306/mydb?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&sslMode=REQUIRED&enabledTLSProtocols=TLSv1.2,TLSv1.3

参数不是越多越好,而是每个都要有明确目的。以下是生产环境必设的7个核心参数(按重要性排序):

参数名推荐值为什么必须设不设后果
serverTimezoneAsia/ShanghaiMySQL 8.0+默认用系统时区,Java用JVM时区,不一致导致时间字段偏移2024-01-01 00:00:00存进去,查出来变2023-12-31 16:00:00
characterEncodingutf8mb4MySQL 5.5.3+才支持4字节UTF-8,utf8实际是utf8mb3,存emoji会截断微信昵称“👨‍💻”存成“??”
useSSL废弃,改用sslModeuseSSL=true不校验证书,等同于明文传输中间人可窃取账号密码
sslModeREQUIRED(内网)或VERIFY_IDENTITY(公网)强制TLS加密,VERIFY_IDENTITY额外校验域名公网暴露未加密连接,违反等保要求
allowPublicKeyRetrievalfalse(默认)禁用公钥检索,防暴力破解RSA密钥攻击者可发起GET_PUBLIC_KEY请求爆破
cachePrepStmtstrue开启预编译语句缓存,减少服务端解析开销高并发下CPU占用飙升30%
rewriteBatchedStatementstrue将addBatch()批量转成INSERT INTO ... VALUES (...),(...)单条语句批量插入速度提升5倍

实操心得:我在某电商项目上线前做压测,发现TPS卡在1200上不去。抓包发现每条INSERT都走独立网络往返。加上rewriteBatchedStatements=true后,TPS直接冲到6800。这不是玄学,是驱动把100条INSERT合并成1条INSERT ... VALUES (..),(..),..发给MySQL,省掉99次网络RTT和SQL解析。

3.4 Spring Boot自动配置:application.yml里的隐藏陷阱

Spring Boot 2.7+的application.yml配置看似简洁:

spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

但这里埋了三个雷:

第一雷:driver-class-name不是必须的
Spring Boot会根据URL前缀jdbc:mysql://自动推导驱动类。手动写反而容易出错——比如写成com.mysql.jdbc.Driver(5.1旧版),而实际用8.3.0驱动,启动时会报ClassNotFoundException。正确做法是删掉这一行,让Boot自动匹配。

第二雷:password明文写死
生产环境必须用Jasypt加密:

spring: datasource: password: ENC(8A2F3C...加密后字符串)

配合启动参数--jasypt.encryptor.password=your-secret-key。否则运维扫描到配置中心,整个数据库权限就泄露了。

第三雷:连接池参数缺失
默认HikariCP只建10个连接,电商大促时瞬间打满。必须显式配置:

spring: datasource: hikari: maximum-pool-size: 50 minimum-idle: 10 connection-timeout: 30000 validation-timeout: 3000 idle-timeout: 600000 max-lifetime: 1800000 # 关键:检测连接有效性 connection-test-query: SELECT 1

注意:connection-test-query在MySQL 8.0.22+必须用SELECT 1,旧版用SELECT 1或SELECT NOW()都行。但千万别写SELECT * FROM DUAL——MySQL没有DUAL表,会报错中断连接池初始化。

4. 常见报错与根因分析:从错误日志定位到驱动层真相

4.1 经典报错速查表:错误码、日志特征、根本原因、修复方案

错误现象典型日志片段根本原因修复方案
Access denied for user 'root'@'192.168.1.100' (using password: YES)Caused by: java.sql.SQLException: Access denied...MySQL 8.0默认caching_sha2_password,驱动不支持或未配allowPublicKeyRetrieval=true方案1:ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
方案2:JDBC URL加?allowPublicKeyRetrieval=true&useSSL=false(仅测试)
The server time zone value 'XXX' is unrecognizedCaused by: com.mysql.cj.exceptions.UnableToConnectException: The server time zone value 'UTC' is unrecognizedJVM时区与MySQL时区不一致,驱动无法解析时间戳JDBC URL加?serverTimezone=Asia/Shanghai,且MySQL服务端执行SET GLOBAL time_zone = '+08:00';
Could not create connection to database serverCaused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure网络不通、防火墙拦截、MySQL未监听3306用telnet 192.168.1.100 3306测试端口;检查MySQLbind-address = 0.0.0.0;确认iptables放行
Public Key Retrieval is not allowedCaused by: com.mysql.cj.exceptions.UnableToConnectException: Public Key Retrieval is not allowed驱动8.0.29+默认禁用公钥检索,但服务端要求RSA密钥交换方案1:JDBC URL加?allowPublicKeyRetrieval=true(不推荐)
方案2:MySQL执行SET GLOBAL require_secure_transport=OFF;(内网)
方案3:升级MySQL到8.0.32+,用caching_sha2_password替代RSA
Connection is closedjava.sql.SQLException: Connection is closed连接池回收了空闲连接,但代码未检查conn.isClosed()在finally块中显式if (conn != null && !conn.isClosed()) conn.close();,或用try-with-resources

4.2 SSL连接失败的深度排查:从证书链到TLS协议

当遇到SSLHandshakeException: Received fatal alert: handshake_failure,别急着重启服务,按顺序检查:

第一步:确认MySQL服务端SSL状态

SHOW VARIABLES LIKE '%ssl%'; -- 必须看到 have_ssl=YES, ssl_ca, ssl_cert, ssl_key 非空 SHOW STATUS LIKE 'Ssl%'; -- Ssl_version 应为 TLSv1.2 或 TLSv1.3

第二步:检查驱动TLS协议支持MySQL 8.0.28+默认禁用TLSv1.0/v1.1,只允许TLSv1.2+。若驱动太旧(如5.1.47),会因协议不匹配握手失败。解决方案:

  • 升级驱动到8.0.33+
  • 或在JDBC URL中强制指定协议:?enabledTLSProtocols=TLSv1.2,TLSv1.3

第三步:验证证书链完整性用OpenSSL检查服务端证书:

openssl s_client -connect 192.168.1.100:3306 -servername your-domain.com

若返回Verify return code: 21 (unable to verify the first certificate),说明证书链缺失中间CA。需将Root CA和Intermediate CA合并成ca.pem,在JDBC URL中指定:

?sslMode=VERIFY_IDENTITY&trustCertificateKeyStoreUrl=file:///path/to/ca.p12&trustCertificateKeyStorePassword=changeit

实操心得:某政务项目上线前,我们反复测试SSL连接都失败。最后发现是MySQL服务端用了Let's Encrypt的R3中间证书,但驱动信任库没更新。解决方案不是换证书,而是把R3证书追加到JVM的cacerts里:keytool -import -alias letsencrypt-r3 -file r3.pem -keystore $JAVA_HOME/jre/lib/security/cacerts。

4.3 字符集乱码终极指南:从客户端到服务端的七层过滤

乱码问题常被归咎于“没设utf8”,实际是七层字符集转换链断裂:

  1. MySQL服务端默认字符集:SHOW VARIABLES LIKE 'character_set%';→character_set_server=utf8mb4
  2. 数据库创建时字符集:CREATE DATABASE test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  3. 数据表字符集:CREATE TABLE t1 (c1 VARCHAR(100)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  4. 连接层字符集:JDBC URL必须含?characterEncoding=utf8mb4&useUnicode=true
  5. JVM启动参数:-Dfile.encoding=UTF-8(影响String.getBytes())
  6. IDE编码设置:IntelliJ → File → Settings → Editor → File Encodings → Global Encoding = UTF-8
  7. 操作系统区域设置:Linux执行locale -a | grep zh_CN,确保zh_CN.UTF-8存在并启用

任一环节掉链,都会导致“存进去是中文,查出来是问号”。最隐蔽的是第4步——即使URL写了characterEncoding=utf8mb4,若驱动版本低于5.1.13,该参数会被忽略。所以务必用SELECT @@character_set_client, @@character_set_results;确认连接层字符集是否生效。

5. 生产环境加固:驱动不是装上就行,而是要持续监控和演进

5.1 连接池健康度监控:用Prometheus抓取HikariCP指标

驱动本身不提供监控,但连接池(HikariCP)暴露了12个关键指标。在Spring Boot Actuator中启用:

management: endpoints: web: exposure: include: health,metrics,prometheus endpoint: prometheus: show-details: always

重点关注三个指标:

  • hikaricp_connections_active:当前活跃连接数,持续>90% maximum-pool-size说明SQL慢或连接未释放
  • hikaricp_connections_idle:空闲连接数,长期为0说明连接泄漏
  • hikaricp_connections_acquire_seconds_max:获取连接最大耗时,>1秒说明MySQL响应慢或连接池不足

我在线上部署过告警规则:当hikaricp_connections_acquire_seconds_max > 2持续5分钟,自动触发钉钉告警,并执行SHOW PROCESSLIST;抓取阻塞SQL。

5.2 驱动版本演进策略:如何制定三年升级路线图

别等出问题才升级。我们团队的驱动升级策略是:

  • 每年Q1:评估新驱动LTS版本(如8.3.x),在测试环境跑全量SQL回归测试
  • 每年Q3:将驱动升级纳入版本迭代,要求所有新功能必须用新驱动API(如executeBatch()替代addBatch())
  • 每两年:淘汰旧驱动(如2024年停用8.0.x,全面切8.3.x),同步更新MySQL服务端到8.0.33+

升级前必做三件事:

  1. 语法兼容性扫描:用mysql-connector-java自带的CompatibilityChecker工具分析代码中Statement.execute()等过时方法
  2. SSL配置审计:生成所有JDBC URL,检查sslMode是否为REQUIRED或VERIFY_IDENTITY
  3. 时区一致性验证:在测试库执行SELECT NOW(), UTC_TIMESTAMP(), CONVERT_TZ(NOW(), '+00:00', '+08:00');,确认三者时间差符合预期

5.3 故障应急手册:当驱动突然不工作时的5分钟响应流程

  1. 第一分钟:确认MySQL服务状态systemctl status mysqld,检查/var/log/mysqld.log是否有OOM killer日志
  2. 第二分钟:用telnet测试端口连通性,用mysql -h 127.0.0.1 -u root -p命令行直连,排除网络和权限问题
  3. 第三分钟:检查应用日志中com.mysql.cj包的ERROR级别日志,定位到具体异常类(如CJCommunicationsException)
  4. 第四分钟:临时切换JDBC URL参数,?useSSL=false&allowPublicKeyRetrieval=true快速恢复业务(仅限紧急)
  5. 第五分钟:提交Hotfix PR,回滚到已知稳定驱动版本,并启动根因分析

最后分享一个小技巧:在CI/CD流水线中加入驱动健康检查。我们用Shell脚本在构建后自动解压jar包,检查META-INF/MANIFEST.MF中的Implementation-Version是否符合预期,不符合则中断发布。这比人工核对版本号可靠100倍。

我在实际项目中踩过最多的坑,不是不会下载驱动,而是把驱动当成一次性消耗品。它其实是数据库通信的神经中枢,版本、参数、SSL配置、字符集,每一处都是生产环境的命门。现在你拿到的不是一份下载教程,而是一套驱动治理方法论——从选型、集成、调优到监控,覆盖全生命周期。下次再看到“下载MySQL驱动”这个需求,你应该本能地问:服务端版本多少?部署环境是内网还是公网?有没有等保合规要求?这些答案,才真正决定该下载哪个文件、怎么配置、怎么验证。

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

Oracle跨平台迁移:XTTCONVERT 2.0实战指南与SCN映射原理

/* 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:14:44

ARTEX深度解析:PostgreSQL作为分布式调度黑板的原理与实践

/* 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:14:32

5G单站验证远程交付:GC平台如何实现报告自动生成与数据规整

/* 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:14:02

OpenHarmony下AD9833波形发生器驱动开发实战

/* 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:13:56

Codex与GitHub CLI身份验证失效的根因与解决方案

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

作者头像 李华