多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

数据库选型实战:从业务需求到多数据库混合架构

数据库选型实战:从业务需求到多数据库混合架构 在座的各位应该都体会过这种纠结新项目启动技术负责人拍板“数据库先选型”结果一群人围着白板争得面红耳赤。MySQL、PostgreSQL、MongoDB、Redis、ES……每个名字背后都站着一堆拥护者每个都能讲出一套“非它不可”的理由。我见过太多团队在选型会议上把时间花在吵架上而不是花在搞清楚自己的业务到底需要什么。最后要么老板一拍脑袋选了个“最流行的”要么技术负责人凭个人偏好定了个“用得最顺手的”等项目上线跑了大半年才发现各种别扭迁移成本高到想哭。数据库选型这件事说穿了就一句话没有最好的数据库只有最合适的数据库。它的本质不是在选一个“数据库软件”而是在选一套对你业务场景的“匹配度方案”。这篇文章我不会告诉你“你就用XX库准没错”而是把这些年我在选型调研、方案对比、落地踩坑过程中积累的方法论和实战细节摊开来讲。无论你是刚入行的后端开发还是需要拍板的技术负责人这篇内容都能帮你建立一套自己的选型判断框架让你下次面对选型讨论时能胸有成竹地拆解问题而不是人云亦云。1. 选型之前必须想清楚的四件事很多团队做数据库选型上来就问“哪个数据库性能最强”“哪个数据库最流行”这个起点就错了。性能再强的数据库如果跟你的业务模型不匹配也是白搭。我在动手调研之前一定会先逼着业务方和技术团队把下面四个问题彻底聊透。1.1 业务数据长什么样怎么访问这是最基础也是最关键的一步。你需要先盘点业务里有什么数据数据之间是什么关系未来会怎么被查询和写入。我习惯做一个简单的数据特征梳理表逼着团队把核心业务对象都列出来。针对每个对象回答几个问题一条数据有多大数据量预计会到多少万、多少亿数据是结构化强关联的比如订单、用户、账户之间的关系还是半结构化甚至完全非结构化的比如日志、帖子正文、评论读多写少还是写多读少还是读写对半实时性要求有多高——是毫秒级响应还是容忍秒级延迟还是只要跑批任务算出来就行举个例子如果你做的是电商系统订单、商品、用户之间的关联关系盘根错节事务要求极高那你大概率离不开关系型数据库。但如果你做的是物联网设备上报的海量时序数据一条记录就一个设备ID加一个时间戳加几个数字没有复杂关联那你上MySQL反而会很痛苦写得太吃力了。数据形态决定了你选型的第一个大方向关系型还是非关系型。1.2 数据一致性要求能放多松这个问题我每次选型都会单独拎出来问因为它直接决定你能不能选那些“性能好但一致性弱”的分布式数据库。很多业务方拍着胸脯说“我们数据绝对不能错”但你往下深挖一层“不能错”到底是什么意思其实大有名堂。银行转账、电商扣库存这类场景要求强一致性写操作完成后读到的必须是最新的绝不能出现超卖。这种场景你只能老老实实上传统关系型数据库或者使用支持分布式事务的数据库方案。但更多业务场景其实可以接受“最终一致性”。比如一个资讯类App的文章浏览量今天看到的是100次还是101次影响大吗点错一次按钮提示“操作失败”用户刷新一下看到了最新状态也完全没问题。最近读的书里看到一句很认可的话“一致性要求是成本的最大来源从强一致退到最终一致你几乎可以换一个数据库阵营。”所以选型前务必要把业务的“一致性底线”拉出来对齐。凡是需要强事务、强一致的地方就必须纳入关系型阵营的势力范围凡是能容忍最终一致性的地方就有NoSQL们大展拳脚的余地。1.3 团队能维护好哪个数据库这是最容易被忽略但往往最致命的问题。我见过不止一个团队捧着行业标杆方案选了某个高端的分布式数据库结果团队里没有一个人能把它真正用好。出了问题没人敢动性能调优无从下手连备份恢复都是小心翼翼照着文档敲根本不知道命令背后的原理。选型不是写论文不是选一个“理论上最正确的答案”而是选一个“团队有把握驾驭的武器”。所以每次选型我都会建立一张维护能力对照表让团队如实自评这个数据库大家有没有人真正在生产环境跑过如果出了问题有没有人能快速定位并解决出了数据损坏、节点宕机这类严重故障我们有能力恢复吗如果核心成员离职新人接手这个数据库的学习成本有多高对这些问题的诚实回答往往能帮你过滤掉很多“看起来很美的技术债”。宁可选一个功能上80%匹配但团队熟手的数据库也不要选一个120%匹配但没人会养的数据库。1.4 数据量和增长速度算过了吗规模评估也是选型前必须做的一步但这个“规模”很多人算得太粗糙。不是简单说“我们预计用户100万”而是要结合数据特征估算出核心业务表的数据量级会是什么水平每年增长多少最热的数据集有多大比如最近30天的热数据冷数据累积下来有多少查询的吞吐量峰值大概是多少——每秒多少请求我一般会按三年时间窗口来评估。假设业务每年增长3倍三年后数据量是多少到时候这套数据库方案的容量上限在哪里是否需要分库分表是否需要对冷热数据做分离这些前置问题的答案会影响你后续的表结构设计、索引策略甚至硬件投入成本。2. 主流数据库各自能扛什么活当你把上面四个问题聊透了再来看市面上主流数据库的定位就会有“一览众山小”的感觉。因为每种数据库本质上都在用特定的数据模型和存储引擎去适配某几类特定场景。2.1 关系型三剑客MySQL、PostgreSQL、SQL Server关系型数据库至今仍是绝大多数业务系统的中流砥柱。它们最大的优势在于数据模型贴合业务直觉有着最成熟的事务支持经过几十年的发展生态稳定、工具丰富、人才充裕。你不用操心太多底层细节只要把表结构设计好剩下的事情大概率不会出大乱子。MySQL是最流行的开源关系型数据库互联网公司用得最多。它的优势是部署简单、性能均衡、运维人才遍地都是社区资料也极其丰富遇到任何问题几乎都能搜到现成的答案。InnoDB存储引擎在默认的读写场景上表现非常稳定配合主从复制、读写分离这类常规架构能支撑绝大多数中大型业务。缺点是它的一些高级特性比如复杂的窗口函数、地理空间查询、部分完整性约束相对保守在非常复杂的SQL场景下会显得心有余而力不足。PostgreSQL是近些年口碑逆袭的“全能选手”。它开源免费功能极其丰富在很多方面已经超越了MySQL支持更全面的SQL标准窗口函数和CTE表达式强大得不像传统型数据库JSONB类型让它可以兼任部分文档型数据库的职责扩展机制让你几乎能给数据库装上任何能力比如PostGIS做地理空间计算。如果你做的是数据分析类业务、GIS类业务或者业务的数据结构未来可能频繁变化PostgreSQL往往比MySQL更有优势。代价是它的并发模型在某些高并发点查场景下调优到极致时略逊于MySQL的成熟方案而且团队如果只有MySQL经验转过来还是要交一点学费。SQL Server在企业级市场中依然占有重要地位尤其在一些传统企业、制造业、金融行业的内部系统里。它和Windows生态、.NET技术栈结合紧密自带的分析服务、报表服务等工具链非常完整。但它的授权费用不低而且技术栈锁定性强一旦选择就意味着和开源生态逐渐疏远。我个人的建议是除非公司有既有的技术路线强依赖或者业务深度绑定微软生态否则新项目优先考虑开源阵营是更稳妥的选择。这三个关系型数据库的选择逻辑很简单默认选MySQL因为稳妥如果遇到更复杂的SQL需求、JSON混合结构或者数据分析场景不妨选PostgreSQL如果绑定微软生态那SQL Server就是自然选择但请务必清晰认识到它的锁定期。2.2 非关系型阵营从文档到宽表再到键值非关系型数据库往往被笼统地称为“NoSQL”但这个词其实很容易让人误解。NoSQL不是“不要SQL”准确的翻译应该是“不仅仅是SQL”。这个阵营内部差异巨大我选型时一定会逼着自己拆细。文档型数据库以MongoDB为代表存储的数据结构是JSON文档。它的最大好处是“schema less”字段随便加天然契合快速迭代的开发模式——前端传什么后端存什么中间几乎不需要额外的映射代码。如果你的业务是内容管理、用户个性化数据、或者数据结构频繁变化的场景MongoDB开发效率会非常高。但它在事务支持上比关系型弱复杂关联查询要小心设计而且如果团队缺乏经验很容易把数据存成一团乱麻查询性能直线下滑。列式存储数据库的代表是HBase和Cassandra。它们的共同特点是牺牲掉复杂的查询能力换取海量数据下的高写入吞吐和水平扩展能力。适合极端的大数据场景比如日志存储、用户行为埋点、画像标签存储。但这种数据库对运维水平要求不低而且你几乎要定制化地设计你的数据模型来匹配列簇结构迁入成本比较高。键值型数据库的代表是Redis和Memcached。Redis严格来说不只是缓存它还能做消息队列、分布式锁、排行榜、计数器等。它把数据存在内存里读写速度是微秒级。但它存不下海量全量数据不适合当业务的主存储更适合当高性能加速层。我见过有人轻信“Redis万物可存”的说法结果把几千万条业务数据全塞进去最后内存成本和数据安全问题双双爆炸。Redis是武器的副武器不是主武器。2.3 搜索引擎和时序数据库别跟主存储混为一谈同样是NoSQL搜索引擎和时序数据库每个都有自己的专属定位跟主存储的职责很容易混在一起选型时必须分清楚。Elasticsearch是搜索引擎的代名词。它基于倒排索引能实现毫秒级的全文检索、复杂条件聚合分析。很多团队把MySQL里的数据同步到ES里做站内搜索和日志分析这已经成为标准架构。但要注意ES本质上是“索引型数据库”不是“持久化存储数据库”。它牺牲了事务性、实时一致性换取的是搜索分析能力。把ES当主存储用将来数据可靠性、一致性会让你欲哭无泪。时序数据库的典型代表有InfluxDB、TDengine、Prometheus。它们专为“时间戳 标签 数值”这种数据模型设计写入吞吐量极高存储压缩比非常优秀查询时能轻松地做时间范围聚合。物联网设备数据、监控系统指标、金融行情数据都是它们的天下。你要是不自量力拿MySQL存IoT的海量时序数据磁盘和性能大概率会先崩溃。选型铁律不要让一个数据库承载它不擅长的工作。搜索的归搜索分析的归分析缓存的归缓存主存储的归主存储。3. 实战选型把需求翻译成数据库聊完各种数据库的特性我来演示一个我自己常用的实战选型流程。假设现在有一个典型的互联网项目叫“社区内容分享平台”主要功能是用户注册登录、发图文动态、关注他人、浏览信息流、内容搜索、以及全站运营数据看板。我会把需求拆成几个独立的模块对每个模块分别做数据库方案匹配而不是整个项目只选一个数据库。3.1 用户与关系模块事务为王用户账号、用户资料、关注关系、粉丝列表这一类数据强结构化、强关联而且必须保证数据一致性。用户注册不能注册到一半关注操作不能只写一半。这类数据我必须放到关系型数据库MySQL里。这里的表结构设计有几个实操要点。用户主表不要存大字段把用户的基本信息和扩展信息头像、简介、个性签名拆开或者用单独的字段存储避免“大宽表”拖垮查询性能。关注关系表非常吃并发核心索引一定要设计好(follower_id, followee_id)做唯一索引同时要单独给followee_id建一个二级索引这样才能同时支撑“我关注了谁”和“谁关注了我”两种查询。这个模块的读写特点是读多写少所以我会在MySQL前面加Redis缓存把热点用户数据缓存起来。但注意绝对不能为了追求速度而绕过数据库直接写Redis用户数据主从必须强一致落到MySQLRedis只是缓存加速层。3.2 内容模块文档型数据库更灵活用户发布的图文动态每条内容包含多个图片、标题、正文、标签、关联话题等。这类数据天然是非结构化的而且不同内容类型字段差异极大图文动态和视频动态的字段结构就不一样。如果强行塞进MySQL要么建一堆空字段要么拆成多张关联表开发成本高查询也麻烦。我的选择是MongoDB它的集合结构完全贴合“一条动态”这个单位。一条文章动态插入就是一个JSON文档图片数组、标签数组直接内嵌一次性读写不需要连表。后续如果业务加需求比如增加“投票”“位置打卡”字段直接加进文档就行不需要跑ALTER TABLE迁移。开发迭代速度会明显加快。要注意两点一定要给“动态ID”建索引同时给“发布者ID 发布时间”建一个复合索引来支撑“某人发布的动态列表”这类查询们。另外MongoDB的文档有16MB大小限制一条动态通常远达不到但如果有人丧心病狂地在里面塞大量评论数据就要小心了。3.3 信息流模块别用传统数据库硬扛信息流这个模块是“关注的人发布时间线”。如果让我用MySQL实现“我关注的人的最新N条动态”瞬间就得做表连接而且要扫描大量维度全部取出来再排序一旦数据量上来性能几乎一定是灾难。这就是一个典型的“量级一上来SQL再熟练也没用”的模块。主流解法有两种按业务数据量大小分。小规模方案在MySQL里做一个异步任务把所有用户的关注列表按时间排序的结果物化进Redis的ZSET里每个用户一个ZSETkey是动态IDscore是发布时间戳。要刷信息流时直接ZREVRANGE拉取。中大规模方案引入消息队列流计算框架比如Kafka Flink在动态发布的时候实时地给所有粉丝的用户时间线推一份拷贝。查询的时候Redis直接命中查询性能极佳扩展性也高。这里必须强调信息流模块本质上不是一个“数据存储问题”它是一个“数据分发问题”。数据库选型只解决存储解决不了分发。你需要的是一套实时数据管道逻辑上要清晰这已经超出单机数据库的能力边界了。3.4 搜索模块给ES喂数据站内搜索功能比如“搜话题”或“搜用户发的动态”每次查询都让MySQL LIKE扫表绝对不行。这个模块的标准做法是引入Elasticsearch但引入后要建立一套同步管道。同步方案我讲究“双写同步 兜底补数”。动态发布成功后业务代码同时把文档写到ES同时订阅MySQL的变更日志如Canal框架监听Binlog异步补推一条数据到ES。万一业务代码里的双写失败了Binlog同步这条还能兜底。这种双保险机制能极大减少数据不一致的窗口期。搜索数据库选型的核心是接受“秒级延迟”。ES的写入不是实时可见的默认有个Refresh Interval比如1秒。这意味着你刚发布的动态可能在1秒后才被搜索到。在内容社区里这完全没问题但如果你的业务要求“发了就必须立刻被搜到”那就得调整这个配置或者调整业务预期。3.5 分析模块列式存储和实时数仓运营数据看板、活跃用户数、热门内容排行这类分析性强、聚合计算多的需求如果直接在主库MySQL上做统计天天跑大查询对业务性能的影响是无法忍受的。分析查询往往要扫全表、做GROUP BY、算COUNT这些都是典型的OLAP负载和OLTP交易负载的优化思路完全相反。我的做法是把MySQL的业务数据通过数据同步工具如DataX、Flink CDC定时同步到ClickHouse这类列式数据库里。ClickHouse的列式存储天生擅长做海量数据的多维聚合统计一个用MySQL跑半天的报表SQL在ClickHouse里往往几百毫秒就能完成。分析侧的数据允许分钟级延迟用定时批处理同步即可完全够用了。如果老板要求看实时数据那就需要在管道上再引入流计算做到准实时但成本会直线上升需要权衡业务价值。“分析不碰生产库”这条经验我是用好几晚上的告警电话换来的请你务必记住。4. 多数据库混用实战数据同步怎么处理看到这里你应该已经明白了真正复杂的项目大概率不会只用一个数据库而是多个数据库各司其职组成一个“混合架构”。这时候最核心的问题就变成了数据如何在多个存储系统之间流动怎么保证不丢、不错4.1 同步方案的三种选择第一个方案是应用层双写。业务代码在写MySQL的同时自己去写MongoDB/ES/Redis。这个方案实现最简单适合对时效性要求非常高的场景比如用户发了动态立刻进搜索。但它的缺点是业务代码侵入强后续每加一个存储端改一次业务代码一旦某个存储端写失败了还有一致性问题。第二个方案是订阅Binlog同步。部署一个Canal组件伪装成MySQL的从库去消费Binlog然后把解析出来的数据变更推给下游ES、Redis、ClickHouse等。这个方案的好处是业务完全无侵入代码零改动同步的可靠性和持续性好。缺点是需要额外部署和维护一套数据管道有一定的运维成本。第三个方案是定时批量同步。用DataX或Sqoop这类离线同步工具每隔几分钟或几小时整批拉一次数据。这个方案最省事适合对时效性不敏感的模块比如报表数据但实时性最差。4.2 同步链路中的数据一致性保障多库同步最容易翻车的点是业务数据在主库已经写成功了但同步链路挂了。比如用户发了动态MySQL里有但Kafka挂了导致ES里没有修好之后搜索又搜不到这条。这个问题怎么解我的经验是建立“对账机制”。每天固定时间跑一个批处理任务对比MySQL里的业务主键和ES、Redis里的数据找出缺失的然后重新增量同步。这种对账任务看起来笨但真正能救命。别指望同步框架能百分百可靠没有任何中间件能保证永不丢失。对账是最后一堵无缝的墙。4.3 非核心链路必须容忍延迟在多数据库架构里不同存储之间的数据不一致不一定代表系统有问题。你在MySQL里改密码用户Redis里5分钟内还是旧密码——这到底是不是Bug不是这是可接受延迟。做选型架构时一定要和业务方对清楚每个模块的延迟容忍度写SLA文档。否则未来每一次微小的数据不同步都会变成你想不通的Bug来源开发背锅、运维背锅、架构师也背锅。我分享一个封板标准核心链路用户资产、账户、支付、订单必须强一致延迟为零容忍中链路信息流、关注关系允许秒级延迟弱链路搜索、报表、推荐允许分钟甚至小时级延迟。这三条标准敢写进SLA后面的吵架会少很多。5. 选型后的评估清单和几个容易翻车的隐蔽坑当你初步选定了数据库组合先别急着上线。我建议花一小时做一轮系统性的“选型验证”同时自查几个隐蔽坑看看有没有给自己埋雷。5.1 六道自测题过不了别上线我会拿几张纸把选型方案放上去然后逐条过下面的清单。第一条性能摸底能不能用你的真实数据量和查询模式做一次压测不要让数据库厂商或者框架文档里的“百万TPS”迷惑双眼在真实业务场景下跑一把才是真东西。第二条扩容预演当数据量翻两倍时这个架构要做哪些调整分库分表怎么分如果现在没想清楚那就说明方案还不够成熟。第三条高可用验证主库挂了怎么办从库能顶上吗数据能找回吗有没有演练过第四条备份恢复演练数据库的备份是否真实有效恢复一次要多长时间恢复后的数据是否完整这三条是上线的前提不是上线后的福利。第五条异常恢复路径如果删错数据了能恢复到哪一刻有没有延迟窗口领导问起来你能立刻答上来吗第六条团队上手评估方案里的技术栈团队里至少有一个人能讲清楚底层原理吗至少不要出现“没人敢碰”的白象。5.2 隐蔽坑一把自己做成“分库分表艺术家”有些团队在选型时其实心里已偏向某个数据库于是就把业务往“适合分库分表的形态”上生掰。比如明明可以选PostgreSQL的JSONB和分区表天然解决的事非要拆成32张表并用中间件做分布式事务。分库分表是最后的利器不是首选方案。我的原则很简单数据量单表超过2000万先看有没有办法分页、归档、冷热分离。如果只是因为“大家说MySQL要分库分表”就先别动等真实量级逼到那个份上再说。过度设计带来的复杂度往往比数据量带来还高。5.3 隐蔽坑二把缓存当成数据库Redis虽好用但它本质是内存数据库。一旦进程崩溃、机器宕机内存里的数据就烟消云散了。如果你把核心业务数据只放Redis出了问题要恢复就非常痛苦。我的红线是Redis只负责加速和临时状态存储永远不能作为唯一的持久化存储。凡是需要长期保存的数据必须同时落一份到磁盘型数据库里。这句话哪怕过了一百年架构选型也适用。5.4 隐蔽坑三忽视运维与监控投入选型时大家总是兴奋地讨论功能、性能但部署上线后要面临的监控、告警、备份、调优才是长期消耗人力物力的大头。选型讨论到最后一页我一定会加一个问题这套组合的运维成本大概是多少监控告警要做哪些数据库的慢查询日志怎么处理备份恢复怎么做这些问题最好提前有结论否则就会欠下技术债后期在凌晨的4点还。6. 关于选型的三个个人经验做完这么多年的选型我有一个越来越笃定的感触选型方案在未来5年之内会过时但选型的思路永远不会过时。技术圈的热点更新极快今天还是这个NewSQL明天又出现另外一个“分布式数据库终结者”但你要掌握的根本能力始终是“把业务语言翻译成技术语言把业务约束匹配到技术约束”的能力。每次做数据库选型我都会用一个小本子把讨论过程记录下来。不是为了留文档而是为了复盘当时为什么做这个决定核心假设是什么当时的备选方案还有哪些假设后来被证明是错的那调整的成本有多大这种记录和复盘的习惯比任何一份选型报告都更能提高你的架构能力。最后再分享一个日常工作里的高效技巧做技术调研时多去看看那些真正经历过海量数据场景的公司公开的技术分享看他们踩过哪些坑。数据库选型不只是选一个软件而是选择一套长期与你共处的工作方式。你可以把每一种数据库都当成一个有性格的伙伴MySQL稳重踏实PostgreSQL博学多才Redis机敏高效ES不容置疑地擅长检索……你要做的就是知道在什么样的业务阶段让他们各司其职也知道什么时候该让他们退休换个更合适的接替者。这就是数据库选型的真正功力所在。
返回列表