MySql, 1分钟掌握分库分表方案

1,058 阅读5分钟

随着互联网的发展,用户规模不断扩大,Mysql单库单表的性能问题也会随之暴露。

下面几个因素,会直接影响数据库性能:

数据量

虽然MySQL单表可以存储10亿级的数据,但如果数据库硬件不是很好,或者遇到数据表结构设计优化不到位,当单表数据量达到几千万上亿时,性能就会有折扣。

磁盘io

磁盘IO经常会成为系统的一个瓶颈,特别是对于运行数据库的系统而言。数据从磁盘读取到内存,再到CPU缓存和寄存器,然后进行处理,最后写回磁盘,中间要经过很多的过程。

因此在并发压力下,所有的请求都访问同一个节点,肯定会对磁盘IO造成非常大的影响。

数据库连接

数据库连接是非常稀少的资源,如果一个库里既有用户、商品、订单相关的数据,当海量用户同时操作时,数据库连接就很可能成为瓶颈。

为了提升线上服务的处理性能,我们就有必要考虑对Mysql进行分库分表。

常见方案:

垂直分表

即基于列字段拆分的“大表拆多个小表”。将同一个表中的大量不常用的、数据长度较长的字段拆分到“扩展表”。

扩展表可以有很多种形式,单表形式、宽表形式、kv表形式,等等。

垂直分库

同一个库中,太多不同业务维度的数据表。比如一个库里同时包括用户、商品、订单、费用表。 当一个电商网站对外提供服务时,用户的行为可能会对这些表同时进行操作,单库的磁盘空间、磁盘io、内存就会产生较大的影响。

如上图,将其拆分到多个服务器上,以释放单机资源。

水平分库分表

将单张表的数据切分到多个服务器,每个服务器具有相应的库与表,只是表中数据集合不同。

这样能够有效的缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源等瓶颈。

常见的几种水平拆分策略:

时间分片

按月分片,按季度分片等等,可以做到冷热数据。分页与合并排序,可以利用UNION ALL解决,当然前提是子表的结果集数据量不能太大。

Hash取模

假设有订单表order,将其分为4个表,order0,order1,order2,order3,路由规则 = orderId % 4;当orderId=3时,对应的是order3。

范围分片

从1-10000一个表,10001-20000一个表。

地理位置分片

北京一个表,上海一个表,南京一个表。

分库分表后引入的问题

分布式事务问题

如果我们做了垂直分库或者水平分库以后,就必然会涉及到跨库执行SQL的问题,这样就引发了互联网界的老大难问题-"分布式事务"。那要如何解决这个问题呢?

  1. 使用分布式事务中间件
  2. 使用MySQL自带的针对跨库的事务一致性方案(XA)。
  3. 能否避免掉跨库操作(比如将用户和商品放在同一个库中)。

跨库join的问题

分库分表后表之间的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表,结果原本一次查询能够完成的业务,可能需要多次查询才能完成。

粗略的解决方法:全局表:基础数据,所有库都拷贝一份。字段冗余:这样有些字段就不用join去查询了。系统层组装:分别查询出所有,然后组装起来,较复杂。

结果集合并、排序的问题

因为我们是将数据分散存储到不同的库、表里的,当我们查询指定数据列表时,数据来源于不同的子库或者子表,就必然会引发结果集合并、排序的问题。

如果每次查询都需要排序、合并等操作,性能肯定会受非常大的影响。

怎么分,能同时解决上述问题?

分布式ID, 水平分库, 垂直分表

以用户下单的订单库的场景举例:

分布式订单ID

利用类似snowflake算法生成一个64bit位的分布式id,作为订单唯一id。如下图所示:

这样的订单id可以直接定位到所在数据库。

go语言版实现:github.com/HaroldHoo/i…

用户ID取模,水平分库

用户下单之前,将用户id取模的值,作为上述「分布式订单ID」的一部分,生成分布式订单ID后创建订单。

这样,同一个用户所产生的订单,将始终在同一个数据库中,可以满足同一用户在订单维度的结果集合并、排序、分页。

按不同业务维度,垂直分表

用类似上一步的方式,可以将其他业务维度的表,比如费用详情表、用户表、评价表,都放在同一个库中。

通过以上步骤,完成分库分表的同时,跨库事务和跨库join问题,也将迎刃而解。


欢迎关注公众号「架构观察」,热爱技术,喜欢阅读,热衷于分享和总结,希望能把每一篇好文章分享给成长道路上的你。公号回复666,有你想要的资源。

架构实战经验分享,技术圈内秘闻观察