MySQL、MongoDB、Redis怎么选?业务场景驱动的数据库选型实战框架
聊到数据库选型尤其是 MongoDB、Redis、MySQL 这三兄弟怎么挑我几乎每次带项目都要给团队讲一遍。这问题看似老生常谈但真正落到业务里我见过太多“用错地方”的惨案有人拿 Redis 当主库存全量数据内存爆了直接雪崩有人拿 MongoDB 硬做电商订单事务对账对到崩溃也有人明明就是个论坛帖子表非得上 MySQL 分库分表把自己折腾得够呛。这篇文章就从真实业务场景讲起不搞教科书式对比把我这些年做技术选型的判断逻辑、踩过的坑、以及最终沉淀下来的选择框架一次性讲清楚。如果你是后端开发、架构师或者正处在技术选型阶段的团队负责人这篇文章大概率能帮你少走几个月弯路。1. 选型前先搞清楚一件事你的数据到底长什么样很多人一上来就纠结性能数字MySQL 每秒能扛多少 QPSMongoDB 写入多快Redis 延迟多低。说实话这些指标在选型里属于“术”的层面真正决定选型的永远是你的数据形态和访问模式。我习惯把数据分成三类。第一类是强关系型数据比如用户、订单、账户、库存它们之间有明确的外键关联需要事务保证一致性这类数据闭着眼睛选 MySQL 不会错。第二类是文档型数据比如商品详情、文章内容、用户画像、日志记录这类数据本身就是一个完整的 JSON 结构字段不固定、嵌套深、经常变硬拆成关系表反而费力不讨好MongoDB 就是干这个的。第三类是热数据比如排行榜、计数器、会话状态、秒杀库存这类数据的特点是访问频率极高、单次读写极快、可以接受短暂丢失或延迟落盘Redis 是天然主场。举一个很典型的例子。我曾经做过一个内容社区类的模拟项目里面有用户表、文章表、评论表还有首页的热门榜单。一开始团队图省事把文章内容和评论全塞进 MySQL文章详情要 JOIN 四张表才能查出来热点文章一高并发就把数据库打满。后来我们把文章正文挪到 MongoDB评论计数和热门榜单放 RedisMySQL 只留用户和权限这类核心关系数据。改造之后接口延迟从 200 毫秒降到了 30 毫秒以内数据库负载降了七成。这中间没有换编程语言、没有改业务逻辑纯粹是数据归属归位的问题。选型的第一步不是比数据库谁厉害而是把你要存的数据按形态分类。关系型、文档型、缓存型三种形态对应三种引擎。你要做的只是让合适的数据去合适的家这比你纠结事务隔离级别、纠结 B 树和 LSM 树的实现细节有用得多。2. 三个数据库的本质差异几句话讲透把数据分类之后再来看三者的本质差异就清楚多了。我用最直白的话讲给你听。2.1 存储模型决定思维模型MySQL 是典型的关系型数据库数据存进二维表格表与表之间靠外键和 JOIN 关联。它的核心优势在于约束强、事务稳。你定义好表结构数据就必须按规矩来想插一条缺少必填字段的数据报错。想同时更新两张表并且要么全成功要么全失败ACID 事务帮你兜底。这种确定性非常适合金融、电商、后台管理系统这类需要严格数据一致性的场景。MongoDB 是文档型数据库存储单位是文档本质就是 JSON 对象。它的核心优势是结构灵活、扩展自然。一个集合里的文档可以有不同的字段数组嵌套随便存加字段不用改表结构。项目原型阶段需求天天变或者数据结构天然就是树形嵌套MongoDB 能让你省掉大量改表迁移的功夫。代价是什么事务能力弱关联查询别扭一致性和隔离性远不如 MySQL。你可以在订单主表上用 MongoDB但要它做账务对账就是给自己挖坑。Redis 是内存键值数据库数据结构丰富支持字符串、哈希、列表、集合、有序集合。它的核心优势是极致的速度和丰富的数据结构操作。排行榜用有序集合、去重用集合、消息队列用列表、缓存用字符串和哈希几乎每类热数据都有现成的结构对应。代价是数据存在内存里成本高、容量受限持久化只能靠 RDB 快照或 AOF 日志极端情况下会丢数据。Redis 适合做缓存和加速层而不是当唯一的事实来源。2.2 一致性模型和数据安全等级不同选型还有一个很多人忽略的点数据丢了会怎样。MySQL 靠 redo log 和 binlog 保证数据安全只要不是机器彻底报废你基本不用担心丢数据主从切换的丢失窗口也控制在毫秒级别。MongoDB 在副本集模式下写入可以被确认到多数节点数据安全等级比单机 MySQL 更强但跨数据中心容灾比 MySQL 繁琐。Redis 则要分清场景作为缓存丢了可以从源头重建如果存了业务数据还只开了 RDB 默认配置一次宕机丢几秒甚至几分钟的数据都是正常的必须接受这个现实再决定用它存什么。打个比方吧。MySQL 像保险柜安全第一存取慢点但绝不丢东西MongoDB 像集装箱什么奇形怪状的东西都能往里塞整箱搬运也方便Redis 像前台抽屉拿取最快但抽屉里的东西随时可能被清空需要你随时能补货。选型就是看你手里是什么货要保险柜、集装箱、还是前台抽屉。3. 从真实业务场景反推选型四个判断维度说了这么多理论落到实操层面我总结了一套选型判断框架。每接到一个需求我让团队依次问四个问题基本能锁定数据库选型。3.1 场景一强一致与事务优先MySQL 没得商量典型特征数据有明确关联、需要事务、金额或库存等敏感信息、审计要求严格。订单系统是最好的例子。一张订单要同时扣库存、扣余额、生成流水三个操作必须同生共死。MySQL 的 InnoDB 引擎提供行级锁和真正的事务支持这种需求就是它的看家本领。你可能会说订单数据量大了 MySQL 撑不住但这属于数据量大之后的灰度演进问题而不是初始选型问题。先老老实实用 MySQL 把业务跑通后面真有千万级日订单量再考虑分库分表或者引入分布式事务中间件。一开始就用 MongoDB 或 Redis 来扛订单数据一致性写到你怀疑人生。这类场景的正解是 MySQL 加合理索引热点订单查询走主键或唯一索引列表查询按分库键过滤线数据库基本保持在个位数毫秒。给订单表的 user_id 建索引、给 order_no 建唯一索引这两步做到绝大多数项目根本碰不到性能瓶颈。3.2 场景二高并发读、热点数据加速Redis 必选典型特征读写比例悬殊、单点热点压力大、需要毫秒级响应、数据允许短时不一致。绝大多数业务系统都是读多写少80% 的请求在访问 20% 的热点数据。比如商品详情页、用户登录状态、热点文章内容每次都查数据库既浪费连接资源又扛不住瞬时流量。Redis 的使命就是把这 20% 的热数据前置到内存里。我的套路通常是数据库里放全量数据Redis 缓存热点数据设置过期时间后端做缓存穿透、击穿的兜底。这样 MySQL 的压力直线下降接口响应时间从几十毫秒压到几毫秒。Redis 能干的远远不止 set/get。商品排行榜、投票排名用 ZSET每个用户未读数用 Hash 存储在线用户列表用 Set 去重简单的异步任务队列用 List 的 BRPOPLPUSH。这些功能我自己项目里几乎天天用。需要注意的前提只有一个你是把 Redis 当加速层不是当数据源。所有丢到 Redis 的数据必须能在数据库中找到重建路径。3.3 场景三数据结构灵活、嵌套深、字段频繁变化MongoDB 优势明显典型特征文档/JSON 形态的自然数据、字段不固定、数组嵌套层级深、需求迭代频繁。我用 MongoDB 做得最多的东西有两类。一类是内容型数据文章、帖子的正文、标签、作者信息、图片列表天然就是一个嵌套文档。用 MySQL 存这类数据有一点经验的都知道有多痛苦标签要单独一张表、图片要单独一张表、关联关系再来一张表查一篇文章要 JOIN 四五次。换到 MongoDB一整个文档直接取出来用写起来那就是一个词舒服。另一类是埋点日志和用户行为数据每条记录长什么样都不固定今天多个字段明天少个字段用 MongoDB 存储动态字段不需要任何提前设计直接 insert 一个 JSON 对象就完事。有个隐藏的优势常被忽略MongoDB 的扩展性。MySQL 到单表千万级以后你要么分区要么分表都是折腾人的活。MongoDB 原生支持分片数据量大了加机器做分片迁移就行。对于快速增长的互联网业务这个能力是实打实的底气。代价是事务弱跨文档操作要非常小心能用一个文档解决的业务尽量就别拆多个文档。3.4 场景四时序型、快速迭代型原型验证需要灵活组合不是每个场景都能一刀切归到某一种数据库。比如设备上报的时序数据可以用 MySQL 按时间分区存也可以 MongoDB 按设备和时间建复合索引还可以用专门的时序数据库。但绝大多数情况下没有到非上专用数据库不可的量级MySQL 加 Redis 缓存或者 MongoDB 加 TTL 索引自动清理过期数据已经够用。再比如快速迭代的初创项目原型需求一周一变表结构三天两头加减字段。这时候用 MySQL每一次改动都要走流程去 ALTER TABLE对一个大表执行 alter 还可能导致锁表。MongoDB 的无 Schema 特性让你完全不需要关心“改表”模型随便变代码里加字段直接存。我常跟团队说快速迭代期用 MongoDB 把你最早期的业务不确定性消化掉等核心模型稳定了再逐步把资金、账务等强一致数据迁回 MySQL这是很多团队实际上在走的路。4. 混合架构才是常态多库协同的正确姿势现实中的业务系统很少只用一种数据库。选型不是做单选题而是做组合题。我自己最喜欢的组合方案是 MySQL Redis MongoDB 三库协同让它们各司其职。4.1 MySQL 主导 Redis 加速这是最经典、覆盖面最广的架构。MySQL 负责所有核心数据的持久化和一致性保证Redis 负责支撑高并发读写。我具体展开几个落地场景。电商商品详情页商品的主数据标题、价格、库存存 MySQL商品详情页的渲染数据序列化成一个 JSON 缓存在 Redis。用户请求进来直接读缓存缓存没有就走数据库查询并回填缓存。秒杀库存用 Redis 的 DECR 操作做原子扣减扣减成功的请求才落数据库生成订单。这里需要注意的是缓存和数据库的一致性。我实践下来比较稳的做法是先更新数据库再删缓存延迟双删兜底配合较短的过期时间做为最终保险。这套方案无法做到绝对一致但业务上基本可接受。排行榜和计数类的场景就完全不用碰 MySQL直接用 Redis 的 ZINCRBY 累加分数ZREVRANGE 查询 TopN性能无可挑剔。定期把排行榜结果异步同步到 MySQL 做冷备和离线分析就行。别用 MySQL 的 ORDER BY 去跑排行榜数据量一上来你就知道什么叫做慢查询报警。4.2 MongoDB 主导 定期归约到 MySQL另一类常见组合是 MongoDB 做业务主库MySQL 做统计和财务仓。比如一个内容平台前端页面的所有读写全部走 MongoDB获取文章文档一次查询全部搞定评论按帖子的文档结构直接嵌在文章里或者用单独的集合按 post_id 索引体验非常流畅。后台需要一个报表系统统计用户活跃、内容数量、互动数据这些复杂报表在 MongoDB 里写聚合管道非常费劲那就用定时任务把 MongoDB 里的数据清洗归约到 MySQL报表查询全部走 MySQL。这个模式的精髓叫做“热数据走文档库冷数据进关系库”。实时业务要的是快和灵活MongoDB 给足统计报表要的是稳和准MySQL 兜底。两个角色不冲突反而各自发挥长处。需要注意的是数据同步链路的闭环。要把同步写成一个幂等任务支持失败重试、断点续跑确保脏数据不会二次污染。我自己一般会给每条同步记录加一个 processed 标记或者用时间戳增量同步跑挂了下次继续从断点开始。4.3 Redis 中间层 最终一致性Redis 在很多架构里不只做缓存还能兼职做消息队列或分布式锁。比如用 Redis 的 List 做轻量级任务队列生产者 LPUSH消费者 BRPOP配合可靠的确认机制可以支撑中等规模下的异步任务处理。用 Redisson 或者自己基于 SETNX 过期时间实现的分布式锁可以在多实例部署下保证同一时刻只有一个节点执行定时任务。但我要提醒一句用 Redis 做队列和锁都要预设异常处理。进程崩了队列里消息没处理要有人补偿持有锁的节点挂了锁要能自动过期释放。Redis 是中间件不是万能的它给你的是速度和便利可靠性还是要靠业务代码自己去补。这是很多项目出问题的根源只看到了 Redis 的高性能忽略了它的数据安全和可靠性短板。5. 选型踩坑实录我走过的弯路你就别走了光讲框架不聊实战踩坑都是耍流氓。下面几个反面教材都是我亲历或者是身边团队经历过的每一个都真实且肉疼。5.1 坑一Redis 存全量数据直接内存雪崩我见过一个团队把所有业务数据全塞进 Redis包括用户详情、订单记录、商品信息图省事、图快。数据量一旦涨上来内存占用飙升服务器频繁触发淘汰策略大量 key 被 LRU 清除。用户一脸懵地发现自己的订单怎么“消失”了界面全是缓存穿透后的白屏。最后项目上线没三个月数据损失了一把才老实把核心数据全部回迁到 MySQLRedis 退回去只做缓存。我的教训Redis 的容量是硬伤它不是用来存所有数据的而是只放最热的那一部分。数据一定要分层全量数据在磁盘数据库Redis 只保存数据库层的子集。哪怕你有钱买大内存机器也不该把所有鸡蛋放一个篮子里内存数据库的丢失风险是天然的。5.2 坑二MongoDB 硬扛强事务需求另一个项目团队觉得 MySQL 分库分表太麻烦想用 MongoDB 的文档模型把订单加商品一次存一个文档里省事还快。一上线确实看着很快等到做退款、做对账时跨文档事务的操作变得极其绕要么自己写大量的补偿逻辑要么用 MongoDB 的分布式事务硬磨性能跌得没法看。最后他们还是把订单拆回了 MySQL折腾了一个多月。我的判断标准很简单模板里包含“金额变化”这四个字一律 MySQL。涉及资金的任何操作不用 ACID 事务兜底就是埋雷。MongoDB 的文档模型无论怎么设计在处理扣款、退款、平账这类强一致需求时都很难让人安心。人不能跟数据库的天性对抗——MySQL 天生就是为了一致性和事务而设计的。5.3 坑三不管数据大小先分库分表还有一种常见操作项目刚开始一天几十万请求都没有团队很早就搞起了分库分表用户表按 ID 取模分成 32 张表。写作是没问题但查询一涉及跨表条件就很痛苦。跨表统计、按月查询、按手机号查用户全都要走中间件聚合。本来轻轻松松一个索引就能解决结果一张简单的业务需求要写几百行代码。我得说句大实话绝大多数系统的数据量单库单表加几个索引就搞定了。MySQL 单表千万级、甚至亿级数据只要索引建得合理查询都能扛住。真正到瓶颈了再考虑升级硬件、读写分离、冷热数据分离这几板斧够绝大多数业务用很多年。分库分表永远是你最后的底牌别一上来就打。5.4 坑四忽视团队技术栈和学习成本这个坑我很少见人提但特别致命。不考虑团队里其他人熟不熟悉这个数据库直接把全家桶换成一个没人用过的东西。比如你是个 MySQL 老兵突然引入 MongoDB然后要求全组在两周内切换数据访问层业务代码全部重写。这期间的效率损失、出错风险、维护成本比你省下的那点索引功夫大得多。选型除了考虑技术本身还要考虑这五个因素团队掌握程度、运维熟悉度、周边生态、招聘成本、踩坑后的自救能力。新引入一种数据库就要承担对应的学习成本和运维成本团队没有人踩过它的坑就意味着所有坑都要亲自踩一遍。这个隐形代价一定要记在选型的账本上。6. 一套可落地的选型决策流程直接抄作业看完上面这些你大概已经对选型有了感觉。这里我再给一套完整的决策模板下次团队遇到选型分歧开会就直接套这套流程走。6.1 选型五步法第一步画数据流图。把业务模块的数据完整画一遍哪些数据产生、存哪里、怎么流动、谁会读取。这一步做完基本就知道哪些数据是关系型的、哪些是文档型的、哪些是高频热数据。第二步量化访问特征。每个数据的读写比例多少、QPS 峰值多少、单条数据大小多大、平均延迟要求多少。数据量小就根本不用考虑分布式数据量大才需要上分片。第三步排布数据优先级。划分哪些是核心主数据丢了会被老板杀哪些是缓存数据丢了还能重新加载。核心主数据只能选 MySQL 或同等事务型数据库缓存数据选 Redis 没毛病。第四步评估一致性要求。列表页多一条少一条无所谓可以用 Redis 缓存加异步写账户余额错一分钱都不行必须是事务保证。把一致性要求按“强、弱、最终”三级分好每一级对应数据库能力等级。第五步验证测试。别光靠文档和理论做决策拿出业务最复杂的一个查询用真实数据量做一次压测。写个脚本造一千万条数据看看 MySQL 走索引和 MongoDB 走嵌套查询各自的响应。实践是检验选型的唯一标准比看一百篇技术博客都好使。6.2 我常用的一套验证清单为了把团队选型过程尽量规范我总结了一份问题清单每次开会都对着过一遍这个数据是否需要事务支持是直接 MySQL否继续往下问。这个数据的结构会频繁变化吗会偏向 MongoDB不会继续看。这个数据会被高频并发读吗会Redis 缓存必须加。数据量预期能到多少千万级以内常规单库即可亿级以上选型时必须考虑分片能力和迁移方案。团队有没有人真正玩过待选数据库没有增加学习成本预算并考虑引入 Mantor 先在低风险场景验证。如果新数据库挂了可以快速回滚到备选方案吗不能说明选型风险过高要准备过渡方案。这套清单帮我拦截过不少冲动的选型决定。建议你也打印出来贴在工位上每次选型讨论会就按这个过能省下大量无休止的争论。7. 写在最后的经验之谈坦白说数据库选型没有银弹没有哪个数据库能同时做到强一致、高可用、高性能、高扩展、低成本和低门槛。每一次选型本质上都是取舍你要想清楚业务现阶段最需要什么。如果业务还在验证期速度比稳定重要那就大胆用 MongoDB 加 Redis如果业务涉及资金和核心交易稳重比速度重要那就老老实实 MySQL加 Redis 做加速这是中国互联网行业跑通千万次的标准路径。我还想分享一个特别实用的技巧。新建一个项目不要急着决定数据库而是先定义数据模型把核心数据流画出来给全团队评审等大家对数据形态达成共识再确定数据库往往水到渠成。选型这个事决定你后三年是填坑还是挖坑。选对的人后期一路顺风顺水选错的人每个季度都在做数据迁移方案。做技术这么多年我越发觉得没有最好的数据库只有最合适的数据归属。把 MySQL 放到它该待的位置让 Redis 在它擅长的领域发光允许 MongoDB 处理那些让你很头疼的非结构化数据这才是架构师真正该做的事。希望这篇从实战里熬出来的选型经验能帮你下次做决定时多一点笃定少一点犹豫。