
把MyCat从1.4版本一路用到MyCat2我算是看着它从一个小众开源项目变成国内很多公司的分库分表首选。这些年团队里换过好几波人但每次遇到“单库扛不住”的问题总有人第一个想到MyCat。这篇文章不打算复述官方文档而是把MyCat和MyCat2的功能、优缺点、配置方式以及我在生产环境里踩过的坑一起串起来讲。内容适合正在做技术选型的架构师、接手MyCat集群的DBA以及刚准备接触数据库中间件的后端开发。1. 从Cobar到MyCat它到底站在架构的哪个位置1.1 一个典型的“单库扛不住”场景先说我印象最深的一个项目。那是一个订单系统单库单表数据量涨到4000万峰值QPS大概5000。到后面主库连接数天天打满CPU持续在90%以上慢查询日志里飘的全是分页和统计类SQL。当时我们做了很多优化索引重建、把热点数据丢Redis、做了主从读写分离。结果是从99%的报警阈值压到了85%但业务还在涨谁都知道这只是把问题往后推了几个月。真正能解决问题的路子只有两条要么把表拆开要么把库拆开。拆表拆库落到工程上就是分库分表。而分库分表的落地方式在很长一段时间里绕不开MyCat。这个场景我相信很多团队都遇到过。单表数据到了千万级以上再牛的SQL优化也有天花板这时候引入中间件几乎是必然选择。1.2 中间件代理的定位与工作原理分库分表做起来有两类路线。一类是在应用层自己写路由逻辑把不同的业务数据打到不同的库表另一类是用中间件代理让分片逻辑和业务代码解耦。MyCat属于后者而且是很典型的代理模式。它的位置在应用和真实数据库之间。应用 - MyCat - 后端MySQL集群应用不直连MySQL而是连MyCat。MyCat拿到SQL之后自己解析、路由、改写再把语句发到后端的某些节点上执行。关键点是MyCat原生实现了MySQL协议所以对应用几乎是透明的。原来用的JDBC驱动、Hibernate、MyBatis什么都不用换只需要把数据源地址改成MyCat的地址就行。这种透明性是MyCat最大的卖点也是很多人选它的理由。再说说来历。MyCat最早脱胎于阿里巴巴开源的CobarCobar停止维护之后社区团队在它基础上做了MyCat 1.x继承了大量Cobar的设计思路。到了MyCat2属于彻底重写不再是Cobar那一套老底子。这也是为什么很多人从MyCat1迁到MyCat2时发现配置方式完全变了的根本原因。2. 核心功能拆解读写分离、分库分表与全局ID生成2.1 读写分离最容易理解也最常被误用的功能MyCat的第一个核心功能是读写分离。很多人以为MyCat实现了主从同步其实不是。主从之间的数据复制仍然是MySQL自己在做MyCat只负责把流量分发给不同的库。在MyCat1.x里这个能力体现在schema.xml的dataHost节点。一个dataHost里可以定义writeHost和readHost心跳检测通过heartbeat里的SQL来做比如select user()。dataHost nameorderHost maxCon100 minCon10 balance1 writeType0 switchType1 heartbeatselect user()/heartbeat writeHost hosthostM1 url192.168.1.10:3306 userroot password123456 readHost hosthostS1 url192.168.1.11:3306 userroot password123456/ /writeHost /dataHostbalance参数控制读流量的分发策略。balance1表示一主一从读请求分摊到从库balance2表示一主多从多个从库都承担读流量。writeType0表示写请求发到第一个writeHostswitchType1表示主库挂了自动切换。这里有个容易踩的坑读写分离解决的是读压力但主从延迟带来的数据不一致MyCat不管。如果你的业务刚写完立刻要读到比如下单后马上回显订单状态从库延迟几百毫秒就可能出问题。实际部署时这类关键读要么强制走主库要么把延迟控制在秒级以内。2.2 分库分表路由规则到底怎么生效分库分表是MyCat的核心价值。理解它的第一步是搞清几个概念逻辑表、数据节点、分片规则。逻辑表就是你业务里看到的表比如t_order。应用发SQL时只认识t_orderMyCat负责把它路由到真实物理表上。数据节点表示一个具体的物理库表比如dn1下面的t_order。分片规则决定了一条记录该进哪个节点。路由过程可以拆成五步解析SQL识别语句类型和涉及的表。提取分片键的值比如customer_id。调用分片算法计算目标节点。改写SQL把逻辑表名替换成物理表名必要时加上limit之类的限制。把SQL发到目标节点执行如果涉及多个节点再聚合结果。举个例子。订单表按customer_id取模分4片请求是一条INSERTINSERT INTO t_order (id, customer_id, amount) VALUES (1001, 7, 88.00);MyCat会计算7 % 4 3然后把这条SQL路由到第4个数据节点也就是dn3上的物理表。应用感知不到这一切它只知道自己执行了一条INSERT。反过来也一样。SELECT查询如果带了customer_id条件MyCat同样按取模结果路由到精准节点这是性能最好的情况。麻烦的是查询条件里没有分片键比如按订单号查MyCat不知道去哪找只能广播到所有分片再合并这就是典型的全分片扫描。2.3 全局ID和分布式事务分片之后逃不掉的两个问题分片之后数据库自增主键就废了。因为每个分片的自增ID从1开始多节点必然撞车。MyCat为此提供了全局序列号机制。MyCat1.x支持几种全局ID生成方式本地文件方式、数据库方式、时间戳方式。生产环境我比较常用数据库方式也就是专门建一张MYCAT_SEQUENCE表来分配ID段MyCat每次取一批序号缓存起来。这个方案的好处是重启不丢号坏处是多一次数据库访问但影响不大。另一种更通用的做法是业务自己发ID比如雪花算法。MyCat只管分片ID由应用侧生成这样后续扩分片也更灵活。如果你问我个人建议我会说别依赖MyCat的序列号统一用一个独立发号器更省心。再就是分布式事务。分片之后一条业务可能同时改多个库的数据本地事务管不了跨库。MyCat1.x支持XA事务也就是说两阶段提交先让所有节点prepare全部成功后统一commit失败则统一rollback。XA的优点是强一致缺点是性能差、协调者单点风险高。MyCat1.x的XA实现真要说多成熟我觉得谈不上生产上用得也很谨慎。MyCat2在这方面做了增强XA的稳定性比1.x好不少但依然不建议为了省事把大量跨分片事务都交给XA把事务边界控制在单个分片内才是正道。3. MyCat 1.x实战schema.xml和rule.xml怎么配才不坑3.1 三个配置文件的职责与常见误区MyCat1.x的配置核心就三个文件server.xml、schema.xml、rule.xml。server.xml管系统级配置包括用户账号、密码、逻辑库权限。schema.xml管逻辑库的拓扑哪个逻辑表在哪些数据节点、用什么分片规则。rule.xml管分片算法定义和具体的分片规则。很多新人最开始会把三者搞混把分片规则写进schema.xml或者把数据源信息写进rule.xml。搞清楚它们的分工之后配置起来会清晰很多。还有一个容易被忽略的点MyCat1.x修改配置有两种生效方式。管理端口9066下执行reload config_all;可以重载配置但有些改动必须重启才彻底生效。我的经验是线上环境改完配置如果reload之后出现路由异常别反复折腾直接找低峰期重启最省事。这一步我吃过不少亏reload不彻底导致的诡异问题比配置本身的问题还多。3.2 一个订单系统分库分表的完整配置示例直接上一个我实际用过的简化版配置。场景是订单库两个MySQL实例订单表和订单明细表按customer_id分片字典表做成全局表。schema.xmlschema nameorder_db checkSQLschemafalse sqlMaxLimit100 table namet_order dataNodedn1,dn2 rulemod-customer autoIncrementtrue childTable namet_order_item joinKeyorder_id parentKeyid/ /table table namet_dict typeglobal dataNodedn1,dn2/ /schema dataNode namedn1 dataHostorderHost1 databaseorder_db1/ dataNode namedn2 dataHostorderHost2 databaseorder_db2/ dataHost nameorderHost1 maxCon100 minCon10 balance0 writeType0 switchType1 heartbeatselect user()/heartbeat writeHost hosthostM1 url192.168.1.10:3306 userroot password123456/ /dataHost dataHost nameorderHost2 maxCon100 minCon10 balance0 writeType0 switchType1 heartbeatselect user()/heartbeat writeHost hosthostM2 url192.168.1.20:3306 userroot password123456/ /dataHostrule.xmltableRule namemod-customer rule columnscustomer_id/columns algorithmmod-long/algorithm /rule /tableRule function namemod-long classio.mycat.route.function.PartitionByMod property namecount2/property /function解释一下关键点。t_order_item是t_order的子表通过childTable声明joinKey是order_idparentKey是id。这样同一个customer_id的订单和订单明细会落到同一个分片上跨分片Join就被消除了。t_dict是全局表typeglobal表示每个分片都保存完整的一份副本。插入、更新、删除时MyCat会广播到所有节点查询时随便路由到任意分片。适合放地区、订单状态等字典数据。sqlMaxLimit100是个保命设置防止应用漏掉分片键导致全分片扫描时返回海量数据。这个在生产环境强烈建议开着代价是应用必须显式写limit否则会被截断。配置的细节太多但核心逻辑其实一句话逻辑表负责定义规则数据节点负责描述物理位置分片规则负责决定数据去哪。3.3 配置之后的验证手段配置完别急着上线先用EXPLAIN验证路由结果。EXPLAIN SELECT * FROM t_order WHERE customer_id 100;MyCat会返回这条SQL被路由到了哪个数据节点以及实际的执行计划。如果返回结果是dn1和dn2两个节点都有说明分片键没有生效查询变成了广播。这时候再查语法八成是查询条件写错了或者分片键字段名和rule.xml里的columns对不上。另一个常用入口是管理端口9066。用MySQL客户端连上去执行show datanode;可以看每个数据节点的状态执行show datasource;可以看后端连接池的情况。线上出了问题这些命令能帮你快速判断是MyCat本身的问题还是后端MySQL的问题。4. MyCat2的重构逻辑从XML到SQL化动态配置4.1 XML配置的三个痛点MyCat1.x的XML配置虽然清晰但用久了真的很烦。第一改配置要重启。虽然官方说支持reload但实际线上环境你不敢太放心很多时候还是走重启流程一次变更就是一次维护窗口。第二多环境维护成本高。dev、test、prod各一套XML变更靠人肉同步版本管理很容易出岔子。经常是生产环境少配了一个function上线才发现。第三reload不彻底的问题在复杂场景下特别明显。比如改了rule.xml但schema.xml里的引用还指着旧算法热加载有时抓不到这种不一致。这些痛点就是MyCat2选择重构配置体系的直接原因。4.2 SQL化配置是怎么一回事MyCat2最大的变化是把配置从静态XML变成了动态元数据。简单说MyCat2启动后不再主要依赖读schema.xml和rule.xml去构建路由表而是把数据源、逻辑库、逻辑表这些配置信息持久化在MyCat自己的元数据库里。管理员通过SQL语句就能完成配置变更而且在线生效。比如创建数据源大概思路是登录MyCat后执行创建数据源的指令-- 示意写法具体语法以官方文档为准 CREATE DATASOURCE ds_1 ( HOST127.0.0.1, PORT3306, USERroot, PASSWORD******, DBorder_db1 );然后再创建逻辑库、逻辑表并把分片规则绑定上去。整个体验更接近操作数据库本身而不是改配置文件。这种设计对运维的改善是实打实的。变更可以自动化、可以脚本化不用再人肉编辑XML也不用担心漏改文件。配置变更之后路由规则能更快生效维护窗口可以大大缩短。不过也要提醒一句MyCat2的版本迭代节奏比1.x快很多官方文档和实际版本之间的差异时不时会出现。我见过不止一个人按文档操作发现命令对不上。所以用MyCat2一定要盯着自己那个版本的文档。4.3 MyCat2增强了什么迁移要注意什么除了配置方式的变化MyCat2在架构层面做了不少升级。后端数据源支持更广了不再局限于MySQL还可以接PostgreSQL等数据库。底层IO模型重构整体性能表现更好。XA事务能力比1.x强分布式场景下的数据一致性有了更好的基础。但这里要泼盆冷水MyCat2和MyCat1不是简单的版本平滑升级配置完全不兼容。从1.x迁到2.x不能指望拷走schema.xml和rule.xml就完事所有逻辑库、分片规则都需要在MyCat2里重新配置一遍。而且如果在1.x上跑了很久的业务分片键设计、ER关系这些都得重新梳理。我的建议是新项目可以直接考虑MyCat2老项目如果跑得好好的没必要为了追新而迁移先把稳定性保住。5. 优缺点硬碰硬MyCat和MyCat2相差不止一个版本号5.1 功能与性能维度对比把两个版本放在一起看最直观的差异在配置方式、动态能力和后端数据库支持上。维度MyCat 1.xMyCat2配置方式server.xml/schema.xml/rule.xmlSQL语句管理配置持久化到元数据库动态调整支持reload部分变更需重启在线生效变更成本低底层架构继承自Cobar的经典结构完全重构后端数据源以MySQL为主支持MySQL、PostgreSQL等多种数据源XA分布式事务支持但成熟度一般能力增强稳定性更好社区资料文档多、踩坑经验多文档迭代快经验沉淀相对少生产验证多年大规模验证新项目持续落地中这个表格透露出的信息很直接MyCat1强在生产验证充分问题基本都被前人踩过搜一搜就有答案MyCat2强在设计理念和扩展性但需要团队有自己解决问题的能力。5.2 运维生态的差别选中间件不只是选技术还是选生态。MyCat1.x的文档和踩坑贴在网上随便搜都是一大片。包括我自己的很多经验也是在论坛和博客里查出来的。遇到问题大概率能搜到别人的解决方案。MyCat2就不太一样。它属于还在快速演进的项目文档版本和实际版本可能对不上社区讨论也没有1.x那么厚实。遇到冷门问题往往要自己去扒源码。还有一个很现实的问题真正把MyCat2用于核心生产链路的团队目前公开分享经验的并不多。这意味着踩坑成本有一大部分要自己承担。5.3 我的整体感受老实说如果团队没有很强的中间件自研能力选MyCat1.x更稳妥至少它被大量生产环境验证过。MyCat2的设计理念更先进但把它跑稳需要团队愿意跟着版本迭代走愿意花时间看源码、试错。这不算黑MyCat2这是选型的现实逻辑。技术先进不等于落地容易生产环境最怕不确定性。6. 实战最容易翻车的三个点分片键、扩容和跨分片查询6.1 分片键选错的代价分片键是整个分库分表设计里最不能错的决定。一旦分片键选错后面的每一个操作都在为这个决定付出代价。最常见的错误是拿订单号做分片键。表面上看订单号按照取模均匀分布没问题。但业务上有一个高频查询用户查看自己的订单列表条件是WHERE customer_id ?。customer_id不是分片键MyCat不知道去哪找只能把这条查询广播到所有分片然后合并结果。数据量大起来之后这种广播查询就是灾难比单库还慢。解决思路有两个。一是直接拿customer_id当分片键这样用户的订单天然落在同一个分片上查询精准订单明细也能跟随主表。二是保留订单号分片的方案但额外建一张映射表记录customer_id和order_id的关系查询先查映射表再定位分片代价是多一次查询维护也更复杂。我强烈建议在设计阶段就把这个问题想清楚因为上线之后再改分片键基本等于重新做一次数据迁移。6.2 扩容的进退两难分片之后还有一个绕不开的问题扩容。假设订单表按customer_id取模分4片业务又涨了一倍4个分片扛不住了要扩到8个。取模规则从4变成8意味着之前每一条数据的路由结果都变了。原来的数据全都要重新分布理论上每个分片一半的数据要搬到别的地方去。这个工程量和风险比当初做分库分表还大。停机窗口要足够长数据校验要足够周全迁移脚本要反复演练。这也是为什么很多人后来倾向用一致性哈希分片。一致性哈希的好处是从4片扩到8片时只需要迁移一小部分数据大部分键的路由结果不变。MyCat也提供了一致性哈希的实现但引入了虚拟节点等概念配置和调试会更复杂。我的经验是无论选哪种算法都要在设计阶段把未来3年的数据量预估进去给分片数留足余量。宁可初始分片多一点也不要一年后就面临扩容。6.3 跨分片查询和事务的绕法分片之后跨分片Join和跨分片事务是最痛苦的两类问题。MyCat的跨分片查询能力是有限的跨节点Join、子查询稍微复杂一点就容易出问题。生产上不能指望MyCat把这类SQL都搞定而应该在设计上规避。常用的招数有三个。第一是全局表。字典这类几乎不变化的数据每个分片保存完整副本查询永远在本地完成没有跨分片问题。第二是ER分片。有外键关系的表用同一个分片键比如订单表和订单明细表都按customer_id分片一条订单的所有明细都在同一个节点上Join就变成了单节点操作。第三是索引表。高频查询不落在分片键上时建反向映射表先定位分片再查真正的数据。至于跨分片事务最稳妥的办法就是别让跨分片事务发生。在业务设计时把需要强一致的操作放在同一个分片内完成。实在避免不了再考虑XA同时做好性能下降和协调者可用性的预案。7. 选型判断什么时候该用MyCat什么时候该绕开它7.1 代理模式和应用内嵌模式的本质区别选MyCat之前先要明白它属于代理模式和你真正在对比的往往是另一类方案应用内嵌的分片组件比如ShardingSphere-JDBC。代理模式的特点是独立部署应用无感知改个数据源地址就接入。缺点是每次SQL都多一跳网络性能有损耗而且MyCat本身成了新的单点需要运维关注。应用内嵌模式的特点是分片逻辑在应用进程内执行性能更好SQL路由更灵活但侵入性更强业务代码要依赖分片组件改造量更大。我的经验是团队规模不大、希望快速落地分库分表MyCat这类代理模式更省心部署一套对全业务透明。团队有能力改造应用、追求极致性能客户端分片是更好的选择。7.2 不该上MyCat的几种情况下面这几种情况我通常不建议上MyCat。第一数据量还没到瓶颈。单表几百万行其实靠索引和缓存就能扛强行分片只会增加复杂度。中间件不是万能药过早引入等于自找麻烦。第二SQL太复杂。动辄七八张表Join、多层子查询、动态SQLMyCat的SQL解析能力处理起来非常吃力经常会遇到路由不对、结果不对的问题。这类业务更适合先做应用层改造或者干脆不要用中间件。第三业务强一致要求极高。如果核心链路对分布式事务的依赖很重MyCat的XA虽然是强一致方案但性能和稳定性都需要充分验证。与其上中间件不如先从架构上避免跨库事务。第四团队没有中间件运维经验。MyCat本身也是一套系统有配置、有监控、有故障处理。没人会维护出事就是事故。7.3 我个人落地建议做了这么多年我自己的落地原则可以浓缩成几句话。数据没到千万级先不碰中间件用缓存和读写分离撑。到了非拆不可的程度优先把分片键设计做扎实尤其是增长最快的高频查询条件让查询尽量落在单分片上。分片键一旦定下来后面基本没有回头路。上线前把核心SQL全部用EXPLAIN打一遍确认没有意外的全分片广播。上线后盯着MyCat的监控指标和慢日志连接数、活跃线程、后端连接池的使用率都要有告警。如果现在让我在新项目中选我大概率会直接看MyCat2但前提是团队愿意持续跟踪版本、接受文档和版本对不上的现实。而如果是在维护跑了好几年的老系统我不会为了追新去动它。MyCat1.x的坑已经被踩得差不多了稳定本身就是价值。最后再分享一个小习惯。我每次配置完MyCat都会专门写一个脚本把核心业务SQL逐条执行EXPLAIN把路由结果固化到文档里。后续任何人改配置、扩分片、调规则都能快速对比路由变化。这个动作花不了多少时间但能在很多个深夜的排查里救你一命。