MySQL与MongoDB实战:从安装、主从复制到性能调优全解析
发布时间:2026/9/26 20:11:48来源:尧图网络
1. 数据库存储的整体认知先分清两条技术路线做后端开发或者运维绕不开“数据库存储”这四个字。我见过太多人在项目初期纠结一个问题到底该用 MySQL 还是 MongoDB有人一听“NoSQL 性能好”就把用户订单存进了 Mongo结果要做跨表事务时欲哭无泪也有人死守 MySQL 一个方案遇到真正的文档型数据模型硬要拆成五六张表最后 join 到自己都看不懂。说白了选错存储方案项目后期要付出极大的重构代价。这篇文章会把 MySQL 和 MongoDB 这两条主流存储路线摊开来讲清楚各自的核心原理、完整安装流程、常用操作、安全加固、主从复制、性能调优要点还有网上很多人问到的“把远程库的一张表同步到本地”“MongoDB 安装失败怎么排查”这类实操问题。适合正在学数据库的学生、刚入门后端开发的新人以及需要用数据库但没系统梳理过这两种方案的从业者。先建立最核心的认知MySQL 是关系型数据库数据像 Excel 表格一样按行列组织强调表结构、外键、事务的一致性MongoDB 是文档型数据库一条数据就是一个完整的 JSON 文档不需要提前定义表结构天然适合像“用户资料带一组标签”“文章带评论列表”这种嵌套结构。这两者不是谁替代谁的关系而是各自覆盖了不同的数据世界。把这条主线拎清了后面所有细节才串得起来。1.1 关系型与非关系型不只是“有没有外键”的区别很多人理解 MySQL 和 MongoDB 的区别就停留在“一个 SQL 一个 NoSQL”其实底层差异比这个深远得多。MySQL 的核心是“行 表 约束”你在建表时就得把字段、类型、长度、默认值全部定死例如ALTER TABLE user_credit MODIFY COLUMN balance INT NOT NULL DEFAULT 0;——对这就是热搜里那个“mysql 设置默认值为 0”的用法先有结构才有数据。而 MongoDB 里每一条文档都可以长得不完全一样你可以先往里 insert后追加字段旧数据也不受影响这种灵活性在迭代极快的业务里非常舒服但要付出的是数据一致性校验的代价。事务机制也是分水岭。MySQL 靠 InnoDB 引擎把 ACID 做得很成熟转账、订单状态流转这些必须用事务出了问题可以回滚。MongoDB 4.0 以后也支持多文档事务了但使用限制和性能开销仍然比 MySQL 大分布式事务更是需要谨慎设计。所以说业务数据如果对一致性要求高——比如“库存扣完必须同步订单”——老老实实 MySQL如果数据模型的“形状”经常变、读取为主、可以接受最终一致性MongoDB 就是更顺手的选择。1.2 为什么这两个数据库几乎覆盖了所有应用场景MySQL 在传统企业级应用、Web 后端、ERP、电商系统里几乎是标配生态成熟度无可匹敌Workbench 可视化工具、JDBC 连接池、主从复制、读写分离随便一搜就是铺天盖地的资料。另一方面MongoDB 在日志系统、实时风控、内容管理、物联网传感器数据、爬虫抓取的半结构化数据等场景中特别出众因为它的查询方式接近程序语言对象模型C#、Java、Node.js 开发人员几乎不需要任何“对象转表”的中间层。我做的实际项目里两种数据库经常共存于同一个系统核心订单走 MySQL 保证事务商品标签和用户画像走 MongoDB 支撑灵活查询。这种“混搭”的做法在行业内非常常见所以这篇文章不只是讲单独的某一套而是帮你把两套存储方案都打通。2. MySQL 实操要点从安装、语法到主从复制全打通2.1 版本选择与安装避坑5.7 还是 8.0离线包怎么装先说版本选择这是很多新手第一个卡住的地方。MySQL 官方现在主推 8.0它默认字符集是 utf8mb4支持窗口函数、公共表表达式、WITH ... AS这类高级查询性能也比 5.7 有明显提升。但要注意8.0 的密码加密插件默认是caching_sha2_password有些老版本 JDBC 驱动连不上需要改成mysql_native_password。如果公司已经有跑了好几年的业务系统别盲目升级 8.0先确认所有客户端驱动兼容。个人学习或者新项目我建议直接 8.0。Linux 服务器上安装最稳妥的办法是下载官方 tar.gz 离线包因为很多内网环境是不能直接联网用 yum 的。步骤是先到 MySQL 官网下载mysql-8.0.x-linux-glibc2.12-x86_64.tar.xz解压到/usr/local/mysql然后安装依赖libaio和numactl创建 mysql 用户和data目录再用mysqld --initialize-insecure初始化数据目录这个方式初始密码为空适合本地调试最后启动并修改 root 密码。整个过程中最容易导致启动失败的原因就是目录权限data 目录和日志文件的所有者必须是 mysql 用户很多人忘了这一步直接报Permission denied。2.2 存储过程与核心 SQL 语法写之前先理解 DELIMITERMySQL 存储过程是面试常客也是实际开发中批量数据处理的利器。很多人第一次写存储过程卡在DELIMITER上其实这个命令的作用就是告诉 MySQL 客户端“把默认的分号结束符临时改成别的符号”。因为存储过程内部有多个;如果还用分号当作语句结束还没输完整个过程就被执行了。所以写法是DELIMITER // CREATE PROCEDURE batch_update_scores() BEGIN DECLARE done INT DEFAULT 0; DECLARE user_id INT; DECLARE cur CURSOR FOR SELECT id FROM users WHERE status 1; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done 1; OPEN cur; REPEAT FETCH cur INTO user_id; UPDATE scores SET score score 10 WHERE uid user_id; UNTIL done END REPEAT; CLOSE cur; END // DELIMITER ;这段代码用游标遍历符合条件的用户给他们的分数统一加 10。写存储过程时我有一句忠告能用一条 UPDATE 完成的业务不要用游标一行行处理游标性能远不如集合操作。存储过程里真正该做的事是那种“无法用单个 SQL 语句表达、并且逻辑稳定的多步骤任务”比如批量对账、定时归档。UPDATE 语法也有几个很容易被忽视的坑。单表更新大家都熟但 MySQL 还支持多表 UPDATE——注意这里没有JOIN关键字咚咚升级直接把FROM后面多张表列出来然后用WHERE关联UPDATE orders o INNER JOIN users u ON o.uid u.id SET o.status blocked WHERE u.credit_score 50;还有一个高级用法是带排序和限制条数的 UPDATE这可以解决“只更新一批数据里的前 N 条”这种需求UPDATE coupon SET status 1 WHERE expire_time NOW() ORDER BY create_time DESC LIMIT 100;这类写法在数据清理时非常实用。最恐怖的坑是什么呢写 UPDATE 时漏了 WHERE 条件全表数据被误改。我见过同事在测试库执行UPDATE users SET balance 1000;然后整个用户表全变富翁了。所以先把SELECT写出来查一遍再把SELECT改成UPDATE这个习惯能救你无数次。2.3 连接池与主从复制“远程库的一张表同步到本地”完整实操连接池是 MySQL 高性能访问的关键机制。你以为每次DriverManager.getConnection(url, user, pass)只是建立一个 Socket 连接其实背后还有 TCP 三次握手、身份认证、SSL 握手、初始化会话一次完整连接可能耗时几十毫秒到几百毫秒。网页请求量大时不可能每个请求都现场建连接所以要用连接池比如 HikariCP、Druid维护一批现成的连接。核心参数就几个initialSize初始连接数、maxActive最大连接数、maxWait拿连接的最大等待时间、minIdle最小空闲数。注意连接池不是越大越好每一个连接在 MySQL 服务端都是一个线程几百个连接同时执行查询会让 CPU 上下文切换开销暴增。我以前调过一个系统把连接池从 200 降到 50接口响应时间反而降了一半。下面重点说热搜里提到的“怎么使用 MySQL 主从复制把远程库的这张表同步到本地。提供详细操作步骤”。这种场景在真实工作中太常见了线上库不能直连统计系统或者在本地环境需要一份生产表数据做开发联调。完整步骤如下。第一步在主库开启 binlog确保my.cnf里有这行配置并设置一个唯一的server-id[mysqld] log-binmysql-bin server-id1第二步在主库创建用于复制的账号不要直接用 root。复制账号至少要有REPLICATION SLAVE权限CREATE USER repl% IDENTIFIED BY YourStrongPass; GRANT REPLICATION SLAVE ON *.* TO repl%; FLUSH PRIVILEGES;第三步查看主库当前的 binlog 文件名和偏移量记录备用SHOW MASTER STATUS;假设结果是mysql-bin.000003, Position154这两个值等会儿要用。第四步在从库配置并能联通主库后用mysqldump把主库数据备份出来再导入到从库。只同步一张表时可以只导出这张表mysqldump -h主库IP -urepl -p 数据库名 表名 table_dump.sql mysql -uroot -p 从库数据库名 table_dump.sql第五步在从库上执行CHANGE MASTER TO把数据源指到主库CHANGE MASTER TO MASTER_HOST主库IP, MASTER_USERrepl, MASTER_PASSWORDYourStrongPass, MASTER_LOG_FILEmysql-bin.000003, MASTER_LOG_POS154;第六步启动并检查同步状态START SLAVE; SHOW SLAVE STATUS\G;看到Slave_IO_Running: Yes和Slave_SQL_Running: Yes就代表同步正常。如果只想同步这一张表可以在从库my.cnf里加上replicate-do-table数据库名.表名。还有一个走主从复制绕不开的坑binlog 千万不能expire_logs_days设得太短否则从库落后太多重建 binlog 地址会出现Could not find first log file name in binary log index。Master Position 要尽可能和 dump 时刻一致最稳妥的做法是先用--master-data2参数备份它会把 CHANGE MASTER 语句直接写进 dump 文件这样就不会有位置偏差。2.4 Workbench 使用、SSL 连接与常见性能调优MySQL Workbench 不只是可视化的“Navicat 替代品”它最有价值的功能其实是Server Status和Performance Dashboard。前端图形界面看线程数、缓冲池命中率一目了然适合用来快速发现库是不是有连接泄漏或者慢查询堆积。用它导入导出数据也很方便Data Export能按库/表导出 SQL 转储还可以选Export to Self-Contained File一键备份。连接 URL 里的 SSL 配置是最近几年高频踩坑点。MySQL 8.0 默认 SSL 开启如果 JDBC 连接串是裸奔的jdbc:mysql://localhost:3306/db概率会遇到Public Key Retrieval is not allowed。这是因为 8.0 默认认证插件caching_sha2_password在非 SSL 连接下需要先取公钥。解决办法是连接串里加allowPublicKeyRetrievaltrue并设置useSSLfalse仅限内网且可信任环境或者正经把证书导成 trustStore 再配置sslModeVERIFY_CA。我给生产环境写 JDBC 时会统一这样配jdbc:mysql://prod-host:3306/dbname?useSSLtruerequireSSLtrueverifyServerCertificatetruetrustCertificateKeyStoreUrlfile:/path/cacertstrustCertificateKeyStorePasswordchangeit性能调优有个特别容易被忽略的起点慢查询日志。打开slow_query_log ON和long_query_time 2跑个一两天看日志你会惊讶原来数据库慢的根源根本不用猜。然后再针对慢 SQL 执行EXPLAIN看执行计划注意有没有Using filesort和Using temporary有 index 却没走就是索引失效常见原因是查询条件里对索引列用了函数或者前导通配符%xxx。记住一条铁律优化 SQL 的顺序永远先于调参水平扩展是最后手段索引设计是主要突破点。面试时被问 MySQL 调优把“先索引、后配置、再架构”这个思路讲清楚已经赢了大部分人。3. MongoDB 实操要点灵活文档模型下的完整开发链路3.1 Linux 安装与卸载90% 的失败是权限和依赖问题MongoDB 的安装听起来简单——下载、解压、运行mongod --dbpath /data/db但对 Linux 新手来说安装失败的高频原因往往集中在目录权限、日志路径和文件句柄限制上。推荐走官方 yum 源方式比如 CentOS 7 上把官方 repo 文件放进/etc/yum.repos.d/mongodb-org-4.4.repo然后yum install -y mongodb-org。这里注意版本当时如果要装 4.4配置文件关键字要用writeConcern等老语法。如果是要内网离线装下载对应mongodb-linux-x86_64-4.4.x.tgz解压到/usr/local/mongodb建好/var/lib/mongo数据目录和/var/log/mongodb日志目录再用mongod -f /etc/mongod.conf启动。mongod.conf里最核心的参数包括storage.dbPath、systemLog.path、processManagement.fork和net.bindIp。有个致命错误是默认配置里bindIp是127.0.0.1开发机没改导致远程程序连不上报connection refused。改绑成内网 IP 后还要记得云安全组和服务器防火墙放行27017端口。卸载 MongoDB 也是一个热搜点。有两条血泪教训第一直接用rm -rf /usr/local/mongodb删了二进制但忘了systemctl stop mongod下次启动会因端口占用报address already in use第二数据目录/var/lib/mongo一定不能保留否则旧库里带不同版本的 WiredTiger 文件重装会直接启动失败。正确卸载步骤是systemctl stop mongod yum remove -y mongodb-org rm -rf /var/lib/mongo /var/log/mongodb /etc/mongod.conf再检查一下还有没有残留的mongod.lock文件——这个锁文件是 MongoDB 优雅退出时删除的如果进程被 kill -9 强杀下一次启动会报“Unclean shutdown detected”这时千万别直接删锁文件要用mongod --repair修复。3.2 基本操作、查询语句与 _id 的 ObjectId 结构MongoDB 的基本操作我建议用 Mongo Shell 先手敲一遍别一上来就依赖 Compass。插入用的是db.users.insertOne({name: 张三, age: 30, tags: [vip, default]})查询用db.users.find({age: {$gt: 25}}).limit(10).sort({createTime: -1})。这里特别要说一句MongoDB 的查询能力不比 SQL 弱多少聚合管道aggregate能做的事情非常多比如$match过滤、$group分组、$unwind展开数组典型场景是一次db.orders.aggregate([{$match: {status: paid}}, {$group: {_id: $userId, total: {$sum: $amount}}}])就完成了“统计每个用户累计消费金额”的任务这在 MySQL 里可能要关联两张表写复杂 GROUP BY。很多人问 MongoDB 里_id为什么是一长串看起来像乱码的东西不直接像 MySQL 那样用自增数字。其实 ObjectId 是 12 字节的十六进制字符串前 4 字节是插入时的时间戳中间 5 字节是随机机器标识和进程标识最后 3 字节是单调递增的计数器。这个结构天然适合分布式环境——多个服务器各自生成 _id 不会冲突还能直接从 _id 中读取出插入时间不用额外存一个 createTime 字段。理解了这一点你就明白为什么 MongoDB 官方不推荐用自增 ID在分片集群里自增 ID 需要全局协调器性能瓶颈和单点风险都很大。3.3 Compass 可视化与 C# 开发实战MongoDB Compass 是官方免费的可视化工具填mongodb://127.0.0.1:27017/admin?authSourceadmin就能连上。它最好的使用姿势不是“替代 Shell”而是用它查看集合的索引建议和文档结构分布比如Schema页签能列出每个字段出现的频率和类型对确定要不要加索引非常有帮助。C# 开发 MongoDB 是很多 .NET 程序员的刚需NuGet 上装MongoDB.Driver包连接字符串建议写成mongodb://user:pass127.0.0.1:27017/admin?authSourceadmin其中authSource指定了认证库如果账号是在 admin 库创建的就写 admin千万别默认成业务库。基础代码如下var client new MongoClient(mongodb://root:secretlocalhost:27017/admin?authSourceadmin); var database client.GetDatabase(shop); var collection database.GetCollectionBsonDocument(products); // 插入 collection.InsertOne(new BsonDocument { { name, 机械键盘 }, { price, 399 }, { tags, new BsonArray {数码, 外设} } }); // 查询 var filter BuildersBsonDocument.Filter.Eq(name, 机械键盘); var doc collection.Find(filter).FirstOrDefault();如果要映射成强类型对象定义一个Product类然后GetCollectionProduct(products)就行。这里有个新时代 OpenTracing 坑默认序列化器对大写属性名会自动转成小写驼峰作为 MongoDB 字段名比如 C# 属性CreateTime会被存成createTime。如果你在 Mongo Shell 里手插了一条create_time下划线字段和 C# 类映射就对不上了——解决方案是字段上加[BsonElement(create_time)]特性。3.4 安全加固认证开启、权限控制与备份恢复MongoDB 最经典的安全事故就是启动后没开认证直接暴露在公网被勒索程序删库。新手本地玩的可以直接禁网运行但任何要对外提供服务或部署在云服务器上的 Mongo 一定要开启访问控制。第一步创建管理员账号use admin db.createUser({user: root, pwd: StrongPassword2024, roles: [{role: root, db: admin}]})第二步修改mongod.confsecurity: authorization: enabled重启后用mongo -u root -p xxx --authenticationDatabase admin才能连。日常开发账号则按最小权限给比如只读账号db.createUser({user: readonly, pwd: xxx, roles: [{role: read, db: shop}]})权限管理的核心思路跟 MySQL 一致别一个 root 走天下应用程序连库账号只给业务库的readWrite运维账号才给root。备份恢复的工具是mongodump和mongorestore基础命令如下mongodump --host localhost --port 27017 --username root --password xxx --authenticationDatabase admin --db shop --out /backup/mongo mongorestore --host localhost --port 27017 --username root --password xxx --authenticationDatabase admin --db shop /backup/mongo/shop还有个安全跟数据库安全相关的头歌实验强调过--bind_ip参数很重要生产环境绝不建议设0.0.0.0。你想想一个数据库全裸裸挂在公网等于是专程邀请黑客来遍历你所有集合。即使内网环境也要用防火墙限制来源 IP二层防护总比一层强。4. 高频问题速查安装、连接、同步的真实踩坑记录问题现象根因解决方案ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sockMySQL 服务没启动或 socket 路径不对systemctl status mysqld检查服务确认socket配置在my.cnf与客户端一致启动后mysqladmin ping验证MySQL 连接报Public Key Retrieval is not allowed8.0 默认caching_sha2_password非 SSL 连接需要取公钥JDBC URL 加allowPublicKeyRetrievaltrue或把驱动升级到新版并配置sslModemysqld.service: LSB: start and stop MySQL loaded报错用 LSB init 脚本启动老版本 MySQLsystemd 无法管理把脚本改为 service 文件或直接用mysql.server脚本实在不行mysqld_safe 临时启MongoDB 启动报Address already in use上一次进程没被干净杀掉或端口被占用lsof -i:27017查占用进程kill后重启确认卸载前先systemctl stop mongodMongoDB 启动报Unclean shutdown detected进程被强杀锁文件残留不要直接删锁跑mongod --repair --dbpath /var/lib/mongo修复从库SHOW SLAVE STATUS显示Slave_IO_Running: Connecting主库账号权限不足、3306 端口不通、master 的 binlog 位置不匹配先ping主库 IP telnet 3306确认repl账号权限REPLICATION SLAVE如果 dump 后位置变了重新CHANGE MASTER TO主从同步只有某张表没更新replicate-do-table列表写错或表名大小写不一致用SHOW SLAVE STATUS的Replicate_Do_Table检查列表表名一般为小写使用库名.表名并对库名用反引号包裹这里再补充一个让我印象特别深的 MySQL 主从案例。当时有个项目要我“把远程库的这张表同步到本地”我照着标准流程搭好后发现数据一直没进来。查了半天原因是主库的binlog_format是STATEMENT而同步表里的更新用了不确定函数UUID()结果 binlog 里记录的语句在从库执行时生成了不同值导致数据对不上。后来把所有复制用的库改成binlog_formatROW问题才彻底解决。现在只要有主从同步需求我都会第一时间把主库 binlog 格式调成ROW它对数据一致性最友好。MongoDB 安装失败还有一个高频原因很多人没意识到OpenSSL 版本不匹配。如果你用低版本 glibc 的 Linux 发行版去跑新版 MongoDB启动时命令会直接报error while loading shared libraries: libssl.so.1.1: cannot open shared object file。这对应了热搜里的“linux 离线安装 mongodb”场景。解决办法是找一个和你系统版本匹配的 MongoDB 发行包不要盲目下载最新版直接扔上去——这件事我吃过亏后来养成了先查官方mongodb-org包兼容矩阵的习惯。5. 运维管理与监控让两个库都跑得平稳MySQL 的日常运维监控重点关注三个指标QPS/TPS、慢查询数、连接数。QPS 过高通常是 SQL 质量差或缓存失效慢查询数偏高就得回头查 EXPLAIN连接数打满则要检查连接池是否泄漏。用SHOW GLOBAL STATUS LIKE Threads_connected;就能看当前连接数配合SHOW PROCESSLIST;检查有没有长事务或者Sleep状态堆积的连接。如果发现大量Sleep连接占用不释放八成是应用层没做连接回收。MongoDB 的监控关注点不太一样更看重 WiredTiger 引擎的 cache 使用率、操作延迟和磁盘 IO。mongostat命令能输出插删改查的实时速率mongotop能看每个集合的操作耗时。让我印象比较深的一个线上事故是某个日志集合无限增长忘了建 TTL 索引最后把磁盘打满。要给 MongoDB 里的数据设过期时间用一行命令就行db.log_events.createIndex({ createdAt: 1 }, { expireAfterSeconds: 604800 })TTL 索引的意思就是让 MongoDB 在后台自动删除超过 604800 秒7 天的文档。这类维护性命令其实特别实用你对比一下 MySQL 里的定时清理怎么实现还得写存储过程 event_scheduler 调度Mongo 这边建个索引就完事这就是文档型数据库“自洽性”的优势。如果业务量再大一点可以尝试部署 MongoDB Ops Manager 4.4它是官方运维管理平台能集中监控备份和自动告警。安装 Ops Manager 比较重最少需要三台机器应用服务器、监控数据库、备份数据库但对 MongoDB 上了规模的企业来说自动备份和滚动升级功能能省掉大量手工运维时间。我见过一个踩坑点Ops Manager 默认备份端口连不通其实它走的是 27017 以外的专用端口防火墙一定要放行对应范围。6. 学习路径与场景决策两个库该怎么选、怎么学你去看热搜词里面有一堆“头歌初识 mongodb”“头歌 mongodb 数据库基本操作”说明很多课程正在用这个方向做实验。我倒觉得学数据库最有效的路径不是先啃理论而是先把环境装好跑通几个典型场景再回头看原理。比如 MongoDB你先装一个单机版把一个嵌套结构的文档插进去试着用聚合管道完成一次统计你就理解文档模型的优势了。再试着开认证、建只读账号安全概念也自然通了。MySQL 也是同样思路装完一个 8.0把主从复制和 EXPLAIN 调优各跑一遍完成“远程库同步表到本地”这个任务你基本就摸到了 MySQL 的筋。真正到了选型场景我给你的判断标准很简单系统一开始就有明确表结构、需要事务一致性、要跟报表和 BI 工具对接选 MySQL数据形态流动、字段频繁变化、需要线性扩展、写入并发极大且数据可以做最终一致性选 MongoDB。一个项目完全可以同时用两个——这不是“偷懒”而是把每类数据交给最擅长处理它的存储引擎。最后再分享一个学习心得安装数据库的“失败经验”其实比“成功经验”更有价值。我刚开始学 MongoDB 时卸载重装了七八次每次都是因为权限、锁文件、端口这些琐碎问题恰恰是这些折腾让我把 Linux 运维功底也练出来了。现在遇到同事报错我扫一眼报错信息基本就知道病根在哪。所以如果你现在正是安装失败、连不上数据库的阶段别烦躁这正是你长进最快的时候。那些看起来枯燥的报错信息其实就是数据库和你在对话听懂它你就真正入门了。
网站建设高端定制企业官网