当前位置:首页 > 问答 > 正文

MySQL分库分表那些事儿,手把手教你从零开始慢慢变高手

(引用来源:58沈剑架构师团队相关技术分享、大众点评网早期架构演进实践、《阿里巴巴Java开发手册》)

咱们先从一个最简单的情景开始想象,你开了一个小网站,一开始没什么人用,就一台机器,上面装了一个MySQL数据库,这个数据库里就几张表,比如用户表、订单表、文章表,所有读写操作都对着这一个数据库来,轻松加愉快,这个阶段,我们叫它“单库单表”。

(引用来源:大多数互联网应用的初始阶段描述)

随着你的网站越来越火,用户量和数据量都上来了,你发现,数据库开始有点吃不消了,最明显的症状就是,网站变慢了,尤其是高峰期,一个简单的查询都要等好久,这时候,你打开数据库的监控一看,CPU、内存、尤其是硬盘的IO(输入输出)都快到极限了,这就好比一条单车道,平时车少没事,一到上下班高峰,就堵成停车场了,这时候,你首先要做的,其实不一定是分库分表,而是“优化”,看看你的SQL语句写得好不好,有没有那些全表扫描的慢查询;给经常用来查询的字段加上索引,就像给书加上目录一样;或者升级一下硬件,换个更快的硬盘,加更多内存,这个阶段的目标是,尽量让单台机器发挥出最大的潜力。

(引用来源:《高性能MySQL》中关于性能优化的层次论述)

优化是有天花板的,当你的数据量真的非常大,比如用户表有几千万甚至上亿行记录,订单表一年下来都几十个亿了,这时候,单台机器的硬件再怎么升级也扛不住了,单库单表还有一个大风险:万一这台数据库服务器宕机了,你的整个网站就瘫痪了,为了应对更大的数据量和更高的并发访问,同时提高系统的可用性,我们必须要把数据和请求分散到多台机器上去,这就是“分库分表”要解决的问题。

具体怎么分呢?主要有两种路子:垂直拆分和水平拆分。

MySQL分库分表那些事儿,手把手教你从零开始慢慢变高手

先说说垂直拆分,这个比较好理解,按列来分”,把一个有很多字段的大表,拆分成好几个小表,原来的用户表,可能包含了用户的基本信息(姓名、电话)、详细资料(个人简介、兴趣爱好)、以及频繁变动的积分余额等,你可以把那些不常用的、或者文本很长的字段(如个人简介)单独拆出去,放到另一张表里,通过用户ID关联,这样,主用户表就变得很瘦,查询速度自然就快了,如果把拆出来的表放到不同的数据库服务器上,那就叫“垂直分库”,把用户相关的表放在一个数据库,把商品相关的表放在另一个数据库,把订单相关的表再放在一个数据库,这样做的最大好处是,降低了单库的复杂度和压力,业务边界更清晰。

(引用来源:数据库设计范式理论及大型系统架构实践)

当垂直拆分做到头了,或者你的某一张表(比如订单表)的数据量本身就已经巨大无比了,这时候就要祭出终极武器——水平拆分了,水平拆分是“按行来分”,就是把一张表里的数据,按照某种规则,分散到多张结构完全相同的表里去,你有一张一亿条数据的订单表,你可以把它分成10张表,每张表存1000万条数据,那怎么决定一条新订单该存到哪张表呢?这就需要一个分片键和分片算法,最常见的分片键是用户ID,我们可以用用户ID除以10的余数(也就是取模)来决定数据落在哪个表,用户ID尾号是0的订单,就放到order_0表;尾号是1的,放到order_1表,以此类推,如果把分出来的这些表再放到不同的数据库服务器上,那就是“水平分库”,水平分库分表是应对海量数据和高并发流量的终极方案,它能极大地提升系统的存储能力和处理能力。

(引用来源:Apache ShardingSphere官方文档中对分片策略的说明、阿里巴巴Cobar/TDDL的设计思想)

MySQL分库分表那些事儿,手把手教你从零开始慢慢变高手

分库分表虽然好处多多,但也带来了很多麻烦事儿,这也是为什么我们说它是“不得已而为之”的最后手段。

第一个大麻烦就是,以前简单的SQL操作可能变得非常复杂甚至无法执行,跨多个分片的查询(叫“跨库Join”)会变得极其困难,性能很差,如果你要统计所有订单的总金额,就得向所有分片都发一次查询请求,然后把结果在内存里再汇总起来,非常麻烦,第二个麻烦是事务问题,在单个数据库里,我们可以用数据库自带的事务来保证数据一致性,但一旦数据分散到多个数据库,就要用到更复杂的“分布式事务”,这东西实现起来难度大,而且对性能影响也大,第三个麻烦是主键ID生成,在单库单表时,我们可以用数据库的自增ID,简单省事,但分库分表后,如果还用每个分片自己本地自增,那肯定会生成重复的ID,所以必须用一个全局唯一的ID生成器,比如雪花算法(Snowflake)来生成ID。

(引用来源:分布式系统常见问题综述)

怎么上手呢?对于新手来说,不建议一上来就自己从零开始造轮子去实现分库分表的逻辑,因为这里面水很深,现在市面上已经有很成熟的中间件来帮我们做这些事情,比如阿里开源的ShardingSphere(它包含了Sharding-JDBC等组件),你可以在你的Java应用程序中引入它,然后通过写一些配置规则(比如指定分片键、分片算法),它就能自动帮你把SQL语句路由到正确的分片上去执行,大大降低了开发难度,你可以先在自己的电脑上搭两个MySQL实例,模拟成分库,然后用ShardingSphere写个简单的demo,插入一些数据,看看数据是怎么被分散到不同库的不同表里的,这样就能最直观地理解分库分表是怎么运作的了。

(引用来源:Apache ShardingSphere官方入门指南)

分库分表是一个随着业务发展,数据量和并发量增长到一定阶段后,不得不考虑的架构演进方案,它是一个从“单库单表” -> “优化” -> “垂直拆分” -> “水平拆分”的渐进过程,它解决了单机瓶颈的问题,但也引入了复杂性,对于初学者,先从理解概念和用现成中间件做实验开始,是最好的“从零到高手”的路径。