0
点赞
收藏
分享

微信扫一扫

MySQL分库分表详解


数据库架构演变

刚开始我们只用单机数据库就够了,随后面对越来越多的请求,我们将数据库的写操作和读操作进行分离, 使用多个从库副本(Slaver Replication)负责读,使用主库(Master)负责写, 从库从主库同步更新数据,保持数据一致。架构上就是数据库主从同步。 从库可以水平扩展,所以更多的读请求不成问题

但是当用户量级上来后,写请求越来越多,该怎么办?加一个Master是不能解决问题的, 因为数据要保存一致性,写操作需要2个master之间同步,相当于是重复了,而且更加复杂。

这时就需要用到分库分表(sharding),对写操作进行切分。
 

 

分库分表前的问题

任何问题都是太大或者太小的问题,我们这里面对的数据量太大的问题。

 

用户请求量太大

因为单服务器TPS,内存,IO都是有限的。 解决方法:分散请求到多个服务器上; 其实用户请求和执行一个sql查询是本质是一样的,都是请求一个资源,只是用户请求还会经过网关,路由,http服务器等。

 

单库太大

单个数据库处理能力有限;单库所在服务器上磁盘空间不足;单库上操作的IO瓶颈 解决方法:切分成更多更小的库

 

单表太大

CRUD都成问题;索引膨胀,查询超时 解决方法:切分成多个数据集更小的表。
 

 

分库分表的方式方法

一般就是垂直切分和水平切分,这是一种结果集描述的切分方式,是物理空间上的切分。 我们从面临的问题,开始解决,阐述: 首先是用户请求量太大,我们就堆机器搞定(这不是本文重点)。

如果是单个库太大,这时我们要看是因为表多而导致数据多,还是因为单张表里面的数据多。 如果是因为表多而数据多,使用垂直切分,根据业务切分成不同的库。

如果是因为单张表的数据量太大,这时要用水平切分,即把表的数据按某种规则切分成多张表,甚至多个库上的多张表。 分库分表的顺序应该是先垂直分,后水平分。 因为垂直分更简单,更符合我们处理现实世界问题的方式
 

 

垂直拆分

垂直分库

MySQL分库分表详解_字段

概念:以表为依据,按照业务归属不同,将不同的 表拆分到不同的库中

垂直分库针对的是一个系统中的不同业务进行拆分,比如用户User一个库,商品Producet一个库,订单Order一个库。 切分后,要放在多个服务器上,而不是一个服务器上。为什么? 我们想象一下,一个购物网站对外提供服务,会有用户,商品,订单等的CRUD。没拆分之前, 全部都是落到单一的库上的,这会让数据库的单库处理能力成为瓶颈。按垂直分库后,如果还是放在一个数据库服务器上, 随着用户量增大,这会让单个数据库的处理能力成为瓶颈,还有单个服务器的磁盘空间,内存,tps等非常吃紧。 所以我们要拆分到多个服务器上,这样上面的问题都解决了,以后也不会面对单机资源问题。

数据库业务层面的拆分,和服务的“治理”,“降级”机制类似,也能对不同业务的数据分别的进行管理,维护,监控,扩展等。 数据库往往最容易成为应用系统的瓶颈,而数据库本身属于“有状态”的,相对于Web和应用服务器来讲,是比较难实现“横向扩展”的。 数据库的连接资源比较宝贵且单机处理能力也有限,在高并发场景下,垂直分库一定程度上能够突破IO、连接数及单机硬件资源的瓶颈。

结果:每个库的结构都不一样,每个库的 数据也不一样,没有交集,所有库的并集是全量数据。

场景:系统绝对并发量上来了,并且可以抽象出单独的业务模块。

分析:到这一步,基本上就可以服务化了。例如,随着业务的发展一些公用的配置表、字典表等越来越多,这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,这时可以将相关的表拆到单独的库中,甚至可以服务化。

 

 

 

垂直分表

MySQL分库分表详解_mysql_02

概念:以 字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。

也就是“大表拆小表”,基于列字段进行的。一般是表中的字段较多,将不常用的, 数据较大,长度较长(比如text类型字段)的拆分到“扩展表“。 一般是针对那种几百列的大表,也避免查询时,数据量太大造成的“跨页”问题。

结果:每个表的 结构都不一样,每个表的数据也不一样,一般来说,每个表的 字段至少有一列交集,一般是主键,用于关联数据。所有表的并集是全量数据。

场景:系统绝对并发量并没有上来,表的记录并不多,但是字段多,并且热点数据和非热点数据在一起,单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈。

分析:可以用列表页和详情页来帮助理解。垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,非热点数据放在一起作为扩展表。这样更多的热点数据就能被缓存下来,进而减少了随机读IO。拆了之后,要想获得全部数据就需要关联两个表来取数据。但记住,千万别用join,因为join不仅会增加CPU负担并且会讲两个表耦合在一起(必须在一个数据库实例上)。关联数据,应该在业务Service层做文章,分别获取主表和扩展表数据然后用关联字段关联得到全部数据。

 

水平拆分

水平分库

概念:以 字段为依据 ,按照一定策略(hash、range等),将一个 库中的数据拆分到多个库中。

MySQL分库分表详解_数据_03

结果:每个库的结构都一样,每个库的 数据都不一样,没有交集。所有库的并集是全量数据。

场景:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。

分析:库多了,io和cpu的压力自然可以成倍缓解。

 

水平分表

MySQL分库分表详解_字段_04

概念:以 字段为依据 ,按照一定策略(hash、range等),将一个 表中的数据拆分到多个表中。

针对数据量巨大的单张表(比如订单表),按照某种规则(RANGE,HASH取模等),切分到多张表里面去。 但是这些表还是在同一个库中,所以库级别的数据库操作还是有IO瓶颈。不建议采用。

结果:每个表的结构都一样,每个表的数据都不一样,没有交集,所有表的并集是全量数据。

场景:系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。

分析:表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担。

水平分表

按照年份进行水平拆分,下面是按照年份划分的两张获奖结果表
t_archievement_2017
t_archievement_2016

mysql> create table t_archievement_2017
-> (id int primary key auto_increment,name varchar(10));
mysql> create table t_archievement_2018
-> (id int primary key auto_increment,name varchar(10));

mysql> show tables;
+---------------------+
| Tables_in_mytestdb |
+---------------------+
| t_archievement_2017 |
| t_archievement_2018 |
+---------------------+
在业务那端数据的写入就要根据年份实际情况来选择表

 

水平分库分表

将单张表的数据切分到多个服务器上去,每个服务器具有相应的库与表,只是表中数据集合不同。 水平分库分表能够有效的缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源等的瓶颈。

水平分库分表切分规则

(1)RANGE

从0到10000一个表,10001到20000一个表。

 

(2)HASH取模

一个商场系统,一般都是将用户,订单作为主表,然后将和它们相关的作为附表,这样不会造成跨库事务之类的问题。 取用户id,然后hash取模,分配到不同的数据库上。

(3)地理区域

比如按照华东,华南,华北这样来区分业务,七牛云应该就是如此。

(4)时间

按照时间切分,就是将6个月前,甚至一年前的数据切出去放到另外的一张表,因为随着时间流逝,这些表的数据 被查询的概率变小,所以没必要和“热数据”放在一起,这个也是“冷热数据分离”。

 

分库分表后面临的问题

事务支持
分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价; 如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

多库结果集合并(group by,order by)
TODO

跨库join
TODO 分库分表后表之间的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表, 结果原本一次查询能够完成的业务,可能需要多次查询才能完成。 粗略的解决方法: 全局表:基础数据,所有库都拷贝一份。 字段冗余:这样有些字段就不用join去查询了。 系统层组装:分别查询出所有,然后组装起来,较复杂。
 

 

 

 

 

 

举报

相关推荐

0 条评论