新闻详情

新闻详情

首页 / 资讯中心 / 详情

MySQL与MongoDB对比与实战:选型、部署、高可用全解析

发布时间:2026/9/28 13:18:22来源:尧图网络
MySQL与MongoDB对比与实战:选型、部署、高可用全解析
1. 为什么我同时保留MySQL和MongoDB先搞清楚存储选型的底层逻辑聊数据库存储几乎绕不开两个名字MySQL和MongoDB。很多刚入行的同学容易陷入一个误区觉得这两个东西是二选一的关系——用了MySQL就不碰MongoDB或者觉得新时代直接梭哈NoSQL。我在实际项目里踩过不少跟存储选型相关的坑可以负责任地告诉你这两个不是替代关系而是互补关系。理解它们的核心差异比死记硬背安装命令重要得多。先说MySQL。它是典型的关系型数据库数据按表组织表与表之间通过主键、外键建立关联。它的核心价值在于约束和一致性。举个例子你做一个电商系统订单表和用户表一定要能关联查询订单金额和库存扣减必须保证事务性这种场景你用文档型数据库去搞不是不能做而是会做得非常痛苦。ACID事务、JOIN查询、复杂报表统计这是MySQL的舒适区。再说MongoDB。它是文档型数据库数据以BSON格式存储一条记录就是一个文档Document字段结构和数量都可以灵活变化。它的核心价值在于灵活和水平扩展。比如你做一个内容管理系统文章表可能有标题、作者、标签、正文、评论列表每个文章的字段数量还不一样用MySQL就得建一堆关联表或者预留一堆NULL字段用MongoDB直接把整篇文章的元数据塞进一个文档里嵌套结构天然匹配。这里有个反直觉的点很多人觉得MongoDB没有Schema约束就是乱来但正是因为它没有固定Schema才让它在快速迭代的业务面前表现极其优秀。字段要加就加要改就改不用跑ALTER TABLE。代价是——你必须通过应用层来保证数据质量数据库本身管不严。所以我的经验是数据关系明确、需要强一致性的模块放MySQL数据结构多变、读多写少、需要快速迭代的模块放MongoDB。抛开理论直接看人的手感。两者在存这个动作上都能把数据稳稳落盘但存储引擎和索引机制完全不同。MySQL的InnoDB用B树做聚簇索引数据按主键顺序物理排列范围查询非常快MongoDB的WiredTiger虽然也用B树变种但文档之间没有物理顺序关系更多的性能优势来自它对嵌套数据结构和复制的优化。理解了这些底层机制你就知道为什么MySQL适合做核心账务而MongoDB适合做用户画像、操作日志、物联网数据采集这类场景了。2. 环境部署实操对比从官网下载到第一个数据落库不管选哪个数据库第一步永远是装环境。这个环节看起来简单其实80%的初学者问题都出在安装配置上。我结合热搜词里高频出现的mysql安装配置教程mongodb安装失败linux安装mysqlwindows 安装 mysql 8把两条完整链路拆开讲。2.1 MySQL安装Windows和Linux的差异Windows下装MySQL 8我建议直接下载官方安装包mysql-installer-community-xxx.msi。安装时重点注意两个地方第一选Server Only。别一上来就装一堆Workbench、Shell、Router这些组件后面用到再补一次装全套反而容易出权限冲突。第二端口默认3306不要改除非你确定没有冲突认证方式选Use Strong Password Encryption后面的应用连接方式兼容性最好。装完用命令行验证mysql -u root -p能进去就说明服务起来了。Windows下最容易出现的坑是服务启动失败排查顺序是先看Windows服务里MySQL服务是否启用再看3306端口是否被占用netstat -ano | findstr 3306最后看my.ini文件路径是否正确。Linux离线安装MySQL是另一个高频场景。热搜里那句error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock几乎每个用rpm包安装的人都遇到过。这个错误的本质是客户端通过socket文件连接服务器但socket文件不存在也就是mysqld服务根本没启动成功。排查思路要清晰# 先确认进程是否活着 ps -ef | grep mysqld # 查看错误日志通常在/var/log/mysql/error.log tail -100 /var/log/mysql/error.log大多数情况下是目录权限不对或者配置文件里socket路径写错了。用rpm安装后默认数据目录是/var/lib/mysql这个目录的所有者必须是mysql用户chown -R mysql:mysql /var/lib/mysql systemctl restart mysqld如果还不行检查/etc/my.cnf里的socket配置是否一致我见过有人把socket路径写在/tmp/mysql.sock但日志里显示实际在/var/run/mysqld/mysqld.sock两边对不上自然连不上。2.2 MongoDB安装4.4和更新的版本需要注意什么安装MongoDB比MySQL要痛快一些但也有自己的坑。热搜词里安装mongodb ops manager 4.4其实是一个特定场景Ops Manager是MongoDB的企业级运维管理平台个人开发基本用不到我更推荐直接装社区版。官网选择对应的操作系统版本Ubuntu用apt源CentOS用yum源Windows直接下msi几分钟就能跑起来。但mongodb安装失败这个问题反复出现核心集中在三个点第一libcurl依赖缺失。新版MongoDB在Linux上依赖libcrypto和libcurl机器环境太干净的时候经常报error while loading shared libraries: libcurl.so.4。解决办法yum install -y libcurl openssl第二需要手动创建数据目录。MongoDB不会帮你建dbpath默认/data/db这个目录是不存在的。很多新手装完启动直接报错mkdir -p /data/db mongod --dbpath /data/db --logpath /var/log/mongodb.log --fork第三4.4版本之后的配置文件格式更严格。新版本对YAML格式的mongod.conf要求很严格缩进、空格、不支持tab键一个笔误直接服务起不来。建议先用mongod --config /etc/mongod.conf --configsvr这种带参数的启动方式来排查配置问题比看日志效率高。安装验证也很简单mongosh命令进入shell执行db.runCommand({ ping: 1 })返回ok说明数据库已经正常工作了。2.3 用Docker的方式更适合快速试验如果是学习用途或者做本地开发我个人最推荐用Docker装数据库。几分钟起一套环境不用折腾系统依赖毁掉重来也方便。# MySQL docker run -d --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 mysql:8.0 # MongoDB docker run -d --name mongo6 -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAMEroot -e MONGO_INITDB_ROOT_PASSWORD123456 mongo:6.0注意MongoDB容器如果设置了认证进入shell连接时要这样docker exec -it mongo6 mongosh -u root -p 123456 --authenticationDatabase admin这里有个细节MongoDB 6.0及以上版本默认捆绑了mongosh老版本用的是mongo命令你如果装了4.4之前的老版本mongo命令还有但新版已经移除了。新手如果在5.0以上版本里敲mongo会提示找不到命令别慌换成mongosh就行。3. 核心数据操作SQL与查询语句的对应关系数据库装上只是开始真正的日常是跟数据打交道。MySQL和MongoDB的操作风格差异非常大我总结了一个最简单的理解方式MySQL是先定义表结构再往里填数据MongoDB是直接把数据扔进去想怎么扔怎么扔。这个本质区别决定了后续所有操作逻辑的不同。3.1 MySQL基础操作与排序、存储过程MySQL的增删改查语法是标准SQL不必全部展开但有几个热搜关键词值得单独说排序、存储过程、聚合。排序是最基础的查询需求-- 按照价格降序再按销量升序 SELECT * FROM products ORDER BY price DESC, sales_count ASC;这里有个性能细节当数据量超过几万行ORDER BY字段如果没有索引MySQL会使用filesort把结果集加载到内存再排序性能明显下降。所以高频排序的字段一定要建索引ALTER TABLE products ADD INDEX idx_price (price);存储过程是MySQL里比较容易让人犯迷糊的东西。它的作用是把一段固定的逻辑保存在数据库端应用层调用适合做批量数据处理、定时任务的底层实现。一个最基础的示例DELIMITER $$ CREATE PROCEDURE batch_update_status() BEGIN DECLARE done INT DEFAULT 0; DECLARE product_id INT; DECLARE cur CURSOR FOR SELECT id FROM products WHERE status 0; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done 1; OPEN cur; read_loop: LOOP FETCH cur INTO product_id; IF done THEN LEAVE read_loop; END IF; UPDATE products SET update_count update_count 1 WHERE id product_id; -- 模拟耗时业务处理 DO SLEEP(0.01); END LOOP; CLOSE cur; END$$ DELIMITER ;我第一次写存储过程的时候踩过一个大坑DELIMITER $$这行不写或者写错MySQL会把整个存储过程当成一条普通SQL逐条执行第二行直接报语法错误。这是初学者最容易卡住的地方没有之一。3.2 MongoDB文档操作和list嵌套list查询MongoDB的核心操作是文档的插入、更新、删除、查询。插入非常简单db.users.insertOne({ name: 张三, age: 28, tags: [developer, backend], address: { city: 北京, street: 中关村 } });查询则完全换了一套思维。热搜词里那句mongodb 怎么查list嵌套list非常典型我展开说一下。假设我们有个集合orders每个订单文档里含有一个items数组每个item又有subItems数组{ _id: ObjectId(...), orderNo: A001, items: [ { name: 电脑, subItems: [ { partName: 内存条, price: 399 }, { partName: 硬盘, price: 699 } ] }, { name: 显示器, subItems: [ { partName: 支架, price: 129 } ] } ] }想查出所有包含内存条的订单用点号嵌套字段匹配db.orders.find({ items.subItems.partName: 内存条 })想统计每个订单下有多少个零件用聚合管道遍历数组db.orders.aggregate([ { $unwind: $items }, { $unwind: $items.subItems }, { $group: { _id: $_id, totalParts: { $sum: 1 } } } ])这里$unwind的作用是把数组摊平嵌套数组需要两次$unwind。这是MongoDB聚合查询里最高频的操作热搜词里mongodb之聚合函数查询统计指的就是这类操作。3.3_id字段与ObjectId的秘密热搜词里还有一条mongodb _id字段objectid这个值得单独讲。MongoDB每个文档都必须有_id字段如果插入时不指定系统自动生成ObjectId。ObjectId是12字节的十六进制字符串结构如下前4字节生成时间的时间戳中间5字节随机值每台机器唯一后3字节自增计数器这个设计保证了分布式环境下也能唯一生成ID并且从ObjectId可以直接反推写入时间const id ObjectId(65f2a6d2e4b0c8f1a2b3c4d5); id.getTimestamp(); // 返回插入时的时间戳这个特性在排查数据问题时非常好用。曾经有次线上数据对不上我直接用_id提取时间比对连日志都不用翻就定位到是某台服务器在某个时间点接入的数据。MySQL的自增主键可没这个功能。不过要提醒一句不能依赖ObjectId保证严格的递增顺序因为随机值和计数器部分会有重叠同一秒内创建的文档ID顺序不一定完全一致。如果有严格的按插入时间排序需求建议单独加一个created_at字段做索引。4. 连接管理数据库连接池与常见连接问题排查数据库用起来之后下一步就是应用层怎么连它。这里涉及两个高频热搜词“mysql的数据库连接池”和“mysql ssl连接错误”。这两块都是生产环境里血泪教训密集的地方。4.1 为什么一定要用连接池直接回答核心问题数据库连接池是用空间换时间的经典方案。如果没有连接池每次应用执行SQL都要经过建立TCP连接-握手认证-执行查询-断开连接的完整流程。一次两次无所谓但高并发场景下连接建立和销毁的开销甚至大于SQL执行本身。Java生态里最常用的是HikariCPSpring Boot 2.x默认版本。它的配置参数看起来简单但有几个坑必须提醒spring: datasource: hikari: maximum-pool-size: 10 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000maximum-pool-size最大连接数不是越大越好。MySQL默认max_connections是151如果你的连接池设为50而后面跟了4个服务实例就是200个连接直接撑爆数据库。这是非常常见的线上事故。max-lifetime建议小于MySQL的wait_timeout默认28800秒避免数据库主动断开后连接池还在继续用死连接。我通常把max-lifetime设为1800000毫秒30分钟提前规避风险。连接池还有一个容易被忽略的好处它能自动检测并剔除无效连接。有了它MySQL重启后应用不需要跟着重启连接池会逐步重建连接。4.2 MySQL SSL连接错误一个看似莫名奇妙的报错“mysql ssl连接错误”这个问题我研究过很多次。典型场景是应用连开发库没问题连生产库报Unable to load authentication plugin caching_sha2_password或者SSL connection error: unknown error number 2026。问题根源在于MySQL 8.0默认开启SSL客户端驱动版本太老不支持新的认证和SSL协议。解决办法有两种方向第一种升级驱动。Java的mysql-connector-java升级到8.0.xPython的pymysql升级到最新版本基本都能解决。第二种如果驱动升级受限比如老项目不敢动可以在MySQL端关闭SSL或者使用兼容认证ALTER USER usernamehost IDENTIFIED WITH mysql_native_password BY password;但注意从MySQL 8.0.34版本开始mysql_native_password插件被标记为弃用未来版本可能移除这不是长久之计。真正正规的做法还是升级驱动然后配置SSL证书实现加密连接spring: datasource: url: jdbc:mysql://localhost:3306/dbname?useSSLtruerequireSSLtrueverifyServerCertificatetrue4.3 MongoDB的客户端连接与安全验证MongoDB的连接方式比MySQL简单但安全设置要提前想清楚。如果开启了认证连接串长这样mongodb://username:passwordlocalhost:27017/database?authSourceadminauthSourceadmin指的是用户认证信息存在admin库里。我遇到过不少新手的痛点明明用户名密码都对但连接失败就是因为没指定authSource默认用了当前数据库去认证。C#开发MongoDB时热搜词里有c# mongodb开发连接串写法一致主要关注驱动版本的差异。MongoDB.Driver 2.x之后的API风格偏异步var client new MongoClient(mongodb://user:passlocalhost:27017); var database client.GetDatabase(mydb); var collection database.GetCollectionBsonDocument(users); // 异步插入 await collection.InsertOneAsync(new BsonDocument { { name, test } }); // 查询 var filter BuildersBsonDocument.Filter.Eq(name, test); var result await collection.Find(filter).FirstOrDefaultAsync();连接池层面MongoDB官方驱动本身就内置连接池默认最大连接数是100。高并发场景下不需要像MySQL那样手调连接池参数只要保证连接串复用一个MongoClient实例就行不要每次都new一个新的MongoClient——这是我在好几个项目里见到过的性能杀手。5. 高可用与数据冗余主从复制和副本集的配置逻辑数据落到单机只是开始生产环境第一要求就是别丢数据。MySQL有主从复制MongoDB有副本集两者思想一致但实现细节天差地别。5.1 MySQL主从复制的搭建思路热搜词里“怎么使用mysql 主从复制”和“把远程库的这张表同步到本地。提供详细操作步骤”都指向同一个需求数据冗余和读写分离。MySQL主从复制的原理不复杂主库把所有写操作记录到binlog从库通过IO线程拉取binlog保存为relay log再由SQL线程重放relay log中的SQL语句实现数据同步。搭建的核心步骤我梳理如下主库配置/etc/my.cnf[mysqld] server-id1 log-binmysql-bin binlog_formatROW注意binlog_format在MySQL 8.0默认已经是ROW格式但老版本可能是STATEMENT。ROW格式记录的是一行数据的变更能减少主从数据不一致的概率。复制时从库配置[mysqld] server-id2 relay-logmysql-relay-bin在主库创建复制专用账号CREATE USER repl% IDENTIFIED BY your_password; GRANT REPLICATION SLAVE ON *.* TO repl%; FLUSH PRIVILEGES;查看主库当前binlog位置SHOW MASTER STATUS; -- 记下File和Position从库执行同步命令CHANGE MASTER TO MASTER_HOST主库IP, MASTER_PORT3306, MASTER_USERrepl, MASTER_PASSWORDyour_password, MASTER_LOG_FILEmysql-bin.000001, MASTER_LOG_POS154; START SLAVE; -- 检查同步状态 SHOW SLAVE STATUS\G判断同步是否正常的核心是看两个字段Slave_IO_Running: Yes和Slave_SQL_Running: Yes。有一个是No就得查错。最常见的问题是IO线程连不上主库多半是防火墙没开放3306端口或者账号授权的主机范围不对。5.2 MongoDB副本集高可用的最小单元MongoDB的高可用不叫主从复制叫副本集Replica Set。一个副本集由多个节点组成其中一个是主节点Primary负责读写其余是从节点Secondary负责冗余。主节点挂了副本集会自动选举新主节点这个过程应用层无感知。搭建一个包含三个成员的副本集# 启动三个mongod实例注意replSet名称要一致 mongod --port 27017 --dbpath /data/node1 --replSet rs0 --bind_ip 0.0.0.0 --fork --logpath /var/log/mongo1.log mongod --port 27018 --dbpath /data/node2 --replSet rs0 --bind_ip 0.0.0.0 --fork --logpath /var/log/mongo2.log mongod --port 27019 --dbpath /data/node3 --replSet rs0 --bind_ip 0.0.0.0 --fork --logpath /var/log/mongo3.log然后连接任意节点初始化rs.initiate({ _id: rs0, members: [ { _id: 0, host: localhost:27017 }, { _id: 1, host: localhost:27018 }, { _id: 2, host: localhost:27019, arbiterOnly: true } ] });arbiterOnly是投票节点不存数据只在主节点选举时参与投票。三个节点里放一个仲裁者是小型项目最常见的副本集配置——既保证了奇数票数又不用多存一份全量数据。这里有个和MySQL完全不同的使用习惯MongoDB的从节点默认是不可查询的连接从节点执行find会报not master or secondary。如果想让从节点承担读流量读写分离需要在连接时设置读偏好db.getMongo().setReadPref(secondaryPreferred);5.3 从库数据延迟主从复制中最常见的隐形坑配置完主从复制或者副本集不代表一劳永逸。数据同步延迟是生产环境最大的隐患。MySQL的主从延迟可以用SHOW SLAVE STATUS里的Seconds_Behind_Master字段观察。如果这个值持续增长多半是主库写入压力太大单线程的SQL线程重放速度跟不上主库的写入速度。解决方案有开启并行复制slave_parallel_workers4、拆分大事务、优化慢SQL。MongoDB副本集的同步延迟也能通过心跳机制感知但排查思路类似如果Secondary一直追不上Primary的数据通常也是写热点问题——某一个集合写入过于高频同步日志积压。我分享一条个人经验任何复制架构下都不要总想着靠同步解决问题先优化写操作本身。主从延迟本质上是单机写能力的上限要根治还得靠分库分表或者分片集群那是另一个级别的架构设计了。6. 生产环境排错与安全加固那些反复出现的经典坑文章最后一部分重点写写我在实际运维中积累的排错思路和安全加固经验。这些内容不是从官方文档里抄的全是真实踩坑后的总结。6.1 MySQL常见错误链路排查MySQL的高频报错翻来覆去就那几类但每次出现的原因可能不一样。我总结了一张排查表错误现象核心原因第一步怎么查ERROR 2002 (HY000) socket连接失败服务未启动或socket路径不一致ps -ef | grep mysqldERROR 1045 (28000) Access denied用户名密码错误或权限未刷新SELECT user, host FROM mysql.userERROR 1205 Lock wait timeout锁等待超时存在未提交事务SHOW PROCESSLIST看是否有Sleep状态长时间不结束Lost connection to MySQL server网络超时或MySQL主动断开大查询查看max_allowed_packet配置锁等待超时是最隐蔽的坑。有一次线上偶发报错查了一圈发现是后台有个定时任务开启事务后调用远程接口远程接口响应慢事务迟迟不提交导致其他会话一直锁等待。经验是应用层事务里绝对不要做远程调用和耗时IO事务开启时间越短越好。6.2 MongoDB数据库安全加固热搜词里头歌mongodb数据库安全和mongodb数据库安全几度出现说明不少人关注这个话题。但坦白说很多人对MongoDB安全的理解停留在启动时加--auth参数这个层面远远不够。我按优先级列一下安全设置清单关闭默认端口暴露。MongoDB默认端口27017很多云服务器的安全组把端口全放开了数据库直接裸奔在公网这是极大的隐患。绑定内网IP不要用0.0.0.0。开启认证。在配置文件里设置security.authorization: enabled创建管理员用户。很多人以为设了密码就安全了实际上不开启认证模式密码形同虚设。权限最小化。给应用创建专用账号只授权需要的数据库和操作权限不要给root级别的权限。网络层隔离。用防火墙限制只有应用服务器IP能访问27017端口。# 以CentOS为例 firewall-cmd --permanent --add-rich-rulerule familyipv4 source address10.0.0.5 port protocoltcp port27017 accept firewall-cmd --reload6.3 结合KubeSphere部署MySQL与数据库上容器化热搜词里kubesphere部署mysql说明现在很多人已经走云原生路线了。在Kubernetes里部署数据库和传统方式完全是两套思维。核心问题是状态数据库是有状态应用需要持久化存储Pod重启后数据不能丢。KubeSphere部署MySQL的流程大致是先创建PVCPersistentVolumeClaim挂载数据目录再配置Secret保存密码最后用StatefulSet或Deployment编排。注意StatefulSet的每个副本有固定的网络标识和存储更适合数据库。一个关键参数是podManagementPolicy: Parallel可以加速多副本启动。如果只是单实例部署用Deployment加一个PVC就够用了。容器化数据库最大的坑是临时文件目录没有持久化。MySQL的/var/lib/mysql不挂PVCPod每次重建等于一次数据重置这不是故障而是配置失误。曾有个同事排查数据丢失查了一整天才发现是PVC忘挂这种低级错误在云原生环境里特别容易犯因为一切看起来都正常。说实话数据库的领域太宽了MySQL和MongoDB这两款数据库已经各自发展成一整套知识体系。但不管底层再复杂选型逻辑可以很朴素数据关系复杂、事务要求高、需要灵活联表统计的统一走MySQL数据格式灵活多变、需要水平扩展、追求写入吞吐的场景统一走MongoDB。实际项目的健康姿势往往是两者共存互为补充——让合适的场景用合适的存储而不是逼着一款数据库包打天下。希望这篇总结能帮你少走些弯路特别是那些安装报错和连接失败的坑早看到早避开。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

C#特性(Attribute)本质、自定义与反射读取实战指南 2026/9/28 15:10:36

C#特性(Attribute)本质、自定义与反射读取实战指南

最近被好几个朋友问起C#特性(Attribute)到底是个什么东西,有人把它和“属性(Property)”搞混,有人说自己贴了[Serializable]程序却还是不能序列化,还有人想在Unity里用特性做自定义玩法&#xf…

阅读更多 →
Go微服务实战:从零入门gRPC与protobuf核心要点 2026/9/28 15:10:29

Go微服务实战:从零入门gRPC与protobuf核心要点

搞Go微服务的,迟早会撞上gRPC这个东西。我第一次认真接触它是在给一个内部网关做改造的时候,当时REST接口越堆越臃肿,接口文档靠人肉维护,客户端和服务端各写一套DTO,字段对不齐是常事。后来核心链路全部换成了gRPC&am…

阅读更多 →
从谢飞机翻车现场看Java面试:八股文背后必须吃透的底层原理 2026/9/28 15:10:29

从谢飞机翻车现场看Java面试:八股文背后必须吃透的底层原理

1. 谢飞机是谁,以及为什么他的面试值得你围观我到现在都记得谢飞机走出面试间时的表情,那种"刚才发生了什么"的迷茫,配上他在楼下咖啡厅跟我复盘时的一句灵魂拷问:"哥,Java面试真的都这么问吗&#xff…

阅读更多 →
AIDE与Wazuh实战对比:Linux文件防篡改基线与监控方案选型 2026/9/28 15:10:28

AIDE与Wazuh实战对比:Linux文件防篡改基线与监控方案选型

先交代清楚背景:我这次不是闲着没事做对比评测,而是手上一批生产服务器的防篡改改造真的到了需要落地的程度。过去半年里,我处理过几起典型的破坏事件——网站首页被植入跳转代码、Linux 主机上的 OpenSSH 二进制被替换成带后门的版本、日志目…

阅读更多 →
基于OpenCV的双目立体视觉图像匹配与测距实战与避坑指南 2026/9/28 15:10:21

基于OpenCV的双目立体视觉图像匹配与测距实战与避坑指南

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

阅读更多 →
AT7456E视频字符叠加芯片实战:STM32 SPI驱动与OSD显示 2026/9/28 15:10:14

AT7456E视频字符叠加芯片实战:STM32 SPI驱动与OSD显示

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉